added WIP 'custom_dimension' to the API
This commit is contained in:
1 parent
e2c8b9cc41
commit
598219608b
5 files changed
+152
No files matched your search
@@ -730,3 +730,25 @@ All generated structures will retain their properties, like mob spawning, howeve
|
||||
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
|
||||
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
|
||||
|
||||
### `custom_dimension(name, seed?)`
|
||||
|
||||
Ensures the dimension with the given `'name'` is available and configured with the given seed. It merely sets the world
|
||||
generator settings to the overworld, and the optional custom seed (or using current world seed, if not provided).
|
||||
|
||||
If the dimension with this name already exists, returns `false` and does nothing.
|
||||
|
||||
Created dimension with `custom_dimension` only exist till the game restart (same with the datapacks, if removed), but
|
||||
all the world data should be saved. If custom dimension is re-created next time the app is loaded it will be using
|
||||
the existing world content. This means that it is up to the programmer to make sure the custom dimensions settings
|
||||
are stored in app data and restored when app reloads and wants to use previous worlds. Since vanilla game only keeps
|
||||
track of world data, not the world settings, if the dimension hasn't been yet configured via `custom_dimension` and
|
||||
the app hasn't yet initalized their dimension, the players will be positioned in the overworld at the same coordinates.
|
||||
|
||||
List of custom dimensions (to be used in the likes of `/execute in <dim>`) is only send to the clients when joining the
|
||||
game, meaning custom worlds created after a player has joined will not be suggested in vanilla commands, but running
|
||||
vanilla commands on them will be successful. Its due to the fact that datapacks with dimensions are always loaded
|
||||
with the game and assumed not changing.
|
||||
|
||||
`custom_dimension` is experimental and considered a WIP. More customization options besides the seed will be added in
|
||||
the future.
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public interface MinecraftServerInterface
|
||||
{
|
||||
void forceTick(BooleanSupplier sup);
|
||||
LevelStorage.Session getCMSession();
|
||||
Map<RegistryKey<World>, ServerWorld> getCMWorlds();
|
||||
}
|
||||
@@ -4,8 +4,11 @@ import carpet.CarpetServer;
|
||||
import carpet.fakes.MinecraftServerInterface;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.ServerTask;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.util.thread.ReentrantThreadExecutor;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -14,6 +17,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.ENDER_TICK;
|
||||
@@ -38,6 +42,8 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
||||
|
||||
@Shadow @Final protected LevelStorage.Session session;
|
||||
|
||||
@Shadow @Final private Map<RegistryKey<World>, ServerWorld> worlds;
|
||||
|
||||
@Override
|
||||
public void forceTick(BooleanSupplier isAhead)
|
||||
{
|
||||
@@ -52,6 +58,12 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RegistryKey<World>, ServerWorld> getCMWorlds()
|
||||
{
|
||||
return worlds;
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At(
|
||||
value = "CONSTANT",
|
||||
args = "stringValue=tallying"
|
||||
|
||||
@@ -26,6 +26,7 @@ import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.utils.ShapeDispatcher;
|
||||
import carpet.script.utils.WorldTools;
|
||||
import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.FormattedTextValue;
|
||||
@@ -1292,6 +1293,33 @@ public class CarpetExpression
|
||||
return (_c, _t) -> ret;
|
||||
});
|
||||
|
||||
this.expr.addLazyFunction("custom_dimension", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0) throw new InternalExpressionException("'custom_dimension' requires at least one argument");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
String worldKey = lv.get(0).evalValue(c).getString();
|
||||
|
||||
Long seed = null;
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
String seedKey = lv.get(1).evalValue(c).getString();
|
||||
try
|
||||
{
|
||||
seed = Long.parseLong(seedKey);
|
||||
}
|
||||
catch (NumberFormatException ignored)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect number format for seed: " + seedKey);
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = WorldTools.createWorld(cc.s.getMinecraftServer(), worldKey, seed);
|
||||
if (!success) return LazyValue.FALSE;
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
return LazyValue.TRUE;
|
||||
});
|
||||
|
||||
|
||||
this.expr.addLazyFunction("reset_chunk", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
package carpet.script.utils;
|
||||
|
||||
import carpet.fakes.MinecraftServerInterface;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.WorldGenerationProgressListener;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.util.registry.SimpleRegistry;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.source.BiomeAccess;
|
||||
import net.minecraft.world.border.WorldBorderListener;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.dimension.DimensionOptions;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.gen.GeneratorOptions;
|
||||
import net.minecraft.world.gen.Spawner;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.ServerWorldProperties;
|
||||
import net.minecraft.world.level.UnmodifiableLevelProperties;
|
||||
import net.minecraft.world.storage.RegionFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class WorldTools
|
||||
{
|
||||
@@ -43,4 +64,68 @@ public class WorldTools
|
||||
catch (IOException ignored) { }
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean createWorld(MinecraftServer server, String worldKey, Long seed)
|
||||
{
|
||||
Identifier worldId = new Identifier(worldKey);
|
||||
ServerWorld overWorld = server.getOverworld();
|
||||
|
||||
Set<RegistryKey<World>> worldKeys = server.getWorldRegistryKeys();
|
||||
for (RegistryKey<World> worldRegistryKey : worldKeys)
|
||||
{
|
||||
if (worldRegistryKey.getValue().equals(worldId))
|
||||
{
|
||||
// world with this id already exists
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ServerWorldProperties serverWorldProperties = server.getSaveProperties().getMainWorldProperties();
|
||||
GeneratorOptions generatorOptions = server.getSaveProperties().getGeneratorOptions();
|
||||
boolean bl = generatorOptions.isDebugWorld();
|
||||
long l = generatorOptions.getSeed();
|
||||
long m = BiomeAccess.hashSeed(l);
|
||||
List<Spawner> list = ImmutableList.of();
|
||||
SimpleRegistry<DimensionOptions> simpleRegistry = generatorOptions.getDimensions();
|
||||
DimensionOptions dimensionOptions = simpleRegistry.get(DimensionOptions.OVERWORLD);
|
||||
ChunkGenerator chunkGenerator2;
|
||||
DimensionType dimensionType2;
|
||||
if (dimensionOptions == null) {
|
||||
dimensionType2 = (DimensionType)server.getRegistryManager().getDimensionTypes().getOrThrow(DimensionType.OVERWORLD_REGISTRY_KEY);
|
||||
chunkGenerator2 = GeneratorOptions.createOverworldGenerator(server.getRegistryManager().get(Registry.BIOME_KEY), server.getRegistryManager().get(Registry.NOISE_SETTINGS_WORLDGEN), (new Random()).nextLong());
|
||||
} else {
|
||||
dimensionType2 = dimensionOptions.getDimensionType();
|
||||
chunkGenerator2 = dimensionOptions.getChunkGenerator();
|
||||
}
|
||||
|
||||
RegistryKey<World> customWorld = RegistryKey.of(Registry.DIMENSION, worldId);
|
||||
|
||||
chunkGenerator2 = GeneratorOptions.createOverworldGenerator(server.getRegistryManager().get(Registry.BIOME_KEY), server.getRegistryManager().get(Registry.NOISE_SETTINGS_WORLDGEN), (seed==null)?l:seed);
|
||||
|
||||
ServerWorld serverWorld = new ServerWorld(
|
||||
server,
|
||||
Util.getMainWorkerExecutor(),
|
||||
((MinecraftServerInterface) server).getCMSession(),
|
||||
new UnmodifiableLevelProperties(server.getSaveProperties(), serverWorldProperties),
|
||||
customWorld,
|
||||
dimensionType2,
|
||||
NOOP_LISTENER,
|
||||
chunkGenerator2,
|
||||
bl,
|
||||
(seed==null)?l:seed,
|
||||
list,
|
||||
false);
|
||||
overWorld.getWorldBorder().addListener(new WorldBorderListener.WorldBorderSyncer(serverWorld.getWorldBorder()));
|
||||
((MinecraftServerInterface) server).getCMWorlds().put(customWorld, serverWorld);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static class NoopWorldGenerationProgressListener implements WorldGenerationProgressListener
|
||||
{
|
||||
@Override public void start(ChunkPos spawnPos) { }
|
||||
@Override public void setChunkStatus(ChunkPos pos, ChunkStatus status) { }
|
||||
@Override public void stop() { }
|
||||
}
|
||||
|
||||
private static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||
}
|
||||
Reference in new issue
Block a user