Open LCLPYT opened 11 months ago
+1 for implementing an API for listing and opening previously created dimensions
Is there a known way of determining what mods created specific dimensions? Perhaps reading from the registry for what dimensions were registered by what mods?
@Aareon I don't think that there is an official API for listing dimensions created by fantasy yet. But maybe this extension can help you? I personally use it in a command that dynamically unloads worlds...
Alternatively you could also read the registry entries of the dimension registry and filter by identifier. However, you cannot get the RuntimeWorldHandle
this way...
DynamicRegistryManager registryManager = server.getCombinedDynamicRegistries().getCombinedRegistryManager();
Registry<DimensionOptions> dimensionRegistry = registryManager.get(RegistryKeys.DIMENSION);
// iterate all registered dimensions
for (RegistryKey<DimensionOptions> key : dimensionsRegistry.getKeys()) {
Identifier id = key.getValue();
// filter the dimension by mod namespace
if (id.getNamespace().equals("target_mod_id")) {
// do something with it, e.g. get the world
RegistryKey<World> wKey = RegistryKey.of(RegistryKeys.WORLD, id);
ServerWorld world = server.getWorld(wKey);
}
}
However, this requires that each mod uses an unique namespace to distinguish the worlds by mod.
Just from looking, this seems like exactly what I'm looking for. Luckily I don't think I need the RuntimeWorldHandle in my case, as I just want to check what namespaces have been registered so that I can blacklist those namespaces from use in my mod. Here's how I go about creating my RuntimeWorldHandle
private static int jumpToDimension(ServerCommandSource source, String namespace, String name) {
MinecraftServer server = source.getServer();
ServerPlayerEntity player = Objects.requireNonNull(source.getPlayer());
Fantasy fantasy = Fantasy.get(server);
Identifier dimensionId = new Identifier(namespace, name);
RegistryKey<World> dimensionKey = RegistryKey.of(RegistryKeys.WORLD, dimensionId);
if (!DimensionUtility.doesDimensionExist(server, dimensionKey)) {
source.sendFeedback(() -> Text.literal("Dimension §6%s§r:§c%s§r does not exist".formatted(namespace, name)), false);
return 1;
}
RuntimeWorldHandle worldHandle = fantasy.getOrOpenPersistentWorld(dimensionId, DimensionUtility.createStandardVoidConfig(server));
player.teleport(worldHandle.asWorld(), player.getX(), player.getY() + 1.5, player.getZ(), player.getYaw(), player.getPitch());
LOGGER.info("Teleported player %s to %s,%s,%s".formatted(player.getName().getString(), player.getX(), player.getY(), player.getZ()));
source.sendFeedback(() -> Text.literal("Teleported to dimension: §6%s§r:§c%s§r".formatted(namespace, name)), true);
return 0;
}
public static boolean doesDimensionExist(MinecraftServer server, RegistryKey<World> dimensionKey) {
try {
ServerWorld world = server.getWorld(dimensionKey);
if (world != null) {
return true; // Dimension exists
}
} catch (NullPointerException | IllegalArgumentException e) {
// Dimension does not exist or other error occurred
return false;
}
return false;
}
I think your extension should help me append new protected namespaces after world initialization to my blacklistedNamespaces
key in my config.
The problem
Currently, if you want to load a previously created persistent world, you have to use
Fantasy::getOrOpenPersistentWorld
. The method obviously requires you to pass a dimension type and chunk generator via aRuntimeWorldConfig
. If you created the world with a custom generator, you will have to memorize it somehow. That creates an unnecessary overhead for the developer IMO.Use case
I personally create worlds for minigames in single player. Often, I change the generator options, such as the dimension type or the chunk generator in the level.dat.
On my minigame server, I copy the level save into the dimensions directory, every time the game starts. I would like to load the map as dimension using the fantasy library. Then, I have to re-create the generator options by hand in Java, for each individual map. In my case, an API would surely be helpful.
Potential solution
I think there should be an API method on the
Fantasy
class, such asopenPersistentWorld(Identifier)
in order to open worlds inside thedimensions/
directory of the currently loaded save. The method needs to somehow re-construct theRuntimeWorldConfig
, the world was created with.For this to work, fantasy would also need to save the config/the generator data to disk on persistent world creation.
Implementation details
My proposal would be to check whether there is a
level.dat
file in the directory of the dimension that should be loaded. If it exists, the code fromnet.minecraft.world.level.storage.LevelStorage#createLevelDataParser
can be used to parse it.This way, users could also put worlds they created in singleplayer / another world into the dimensions folder of the desired save (my use case).
As for writing the
level.dat
, one could usenet.minecraft.world.level.storage.LevelStorage.Session#backupLevelDataFile()
.Maybe there are even better ways to save and load the world data, I am not sure...
Sample implementation
I implemented the
level.dat
parsing and config re-creation as proof of concept in one of my libraries. If you like the approach, I would be happy to create a pull request providing the feature to fantasy. Please let me know, if the concept / the implementation is to your liking.I am still working on the
leve.dat
creation when creating a persistent world. So it can only be tested with other saves ATM.