From a06fa73d8eaecc7deef735004de094c106263822 Mon Sep 17 00:00:00 2001 From: Patbox <39821509+Patbox@users.noreply.github.com> Date: Sat, 23 Nov 2024 11:32:09 +0100 Subject: [PATCH] Add basic support for persistent game worlds --- .idea/inspectionProfiles/Project_Default.xml | 5 +- .../api/game/world/GameSpaceWorlds.java | 16 ++++++ .../game/manager/ManagedGameSpaceWorlds.java | 22 +++++++- .../nucleoid/plasmid/test/PersistentGame.java | 55 +++++++++++++++++++ .../plasmid/test/TestInitializer.java | 3 + .../data/testmod/plasmid/game/persistent.json | 5 ++ 6 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 src/testmod/java/xyz/nucleoid/plasmid/test/PersistentGame.java create mode 100644 src/testmod/resources/data/testmod/plasmid/game/persistent.json diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 965962d8..1803424e 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,9 @@ - + \ No newline at end of file diff --git a/src/main/java/xyz/nucleoid/plasmid/api/game/world/GameSpaceWorlds.java b/src/main/java/xyz/nucleoid/plasmid/api/game/world/GameSpaceWorlds.java index 0372dc97..3fda40a1 100644 --- a/src/main/java/xyz/nucleoid/plasmid/api/game/world/GameSpaceWorlds.java +++ b/src/main/java/xyz/nucleoid/plasmid/api/game/world/GameSpaceWorlds.java @@ -1,6 +1,8 @@ package xyz.nucleoid.plasmid.api.game.world; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import xyz.nucleoid.fantasy.RuntimeWorldConfig; import xyz.nucleoid.plasmid.api.game.GameSpace; @@ -21,6 +23,20 @@ public interface GameSpaceWorlds extends Iterable { */ ServerWorld add(RuntimeWorldConfig worldConfig); + + /** + * Creates (or loads) and adds a persistent world to be associated with this {@link GameSpace}. + * When the game is closed, the world will be unloaded, with all chunks saved. + * If world using this id is already loaded, this method will throw an exception. + * + * @param worldConfig a config describing how the new world should be created and how it should behave + * @return the created world instance + * @see RuntimeWorldConfig + */ + @ApiStatus.Experimental + ServerWorld addPersistent(Identifier identifier, RuntimeWorldConfig worldConfig); + + /** * Removes and deletes a temporary world that is associated with this {@link GameSpace}. * The passed world must have been created through {@link GameSpaceWorlds#add(RuntimeWorldConfig)}. diff --git a/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpaceWorlds.java b/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpaceWorlds.java index d02dfab0..f1c50e36 100644 --- a/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpaceWorlds.java +++ b/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpaceWorlds.java @@ -3,7 +3,9 @@ import com.google.common.collect.Iterators; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; import net.minecraft.world.GameRules; import net.minecraft.world.World; import org.jetbrains.annotations.NotNull; @@ -37,13 +39,29 @@ public ServerWorld add(RuntimeWorldConfig worldConfig) { return worldHandle.asWorld(); } + @Override + public ServerWorld addPersistent(Identifier identifier, RuntimeWorldConfig worldConfig) { + var world = this.space.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, identifier)); + if (world != null) { + throw new RuntimeException("World '" + identifier + "' is already loaded!"); + } + + applyDefaultsTo(worldConfig); + + var worldHandle = Fantasy.get(this.space.getServer()).getOrOpenPersistentWorld(identifier, worldConfig); + this.worlds.put(worldHandle.asWorld().getRegistryKey(), worldHandle); + this.space.onAddWorld(worldHandle); + + return worldHandle.asWorld(); + } + @Override public boolean remove(ServerWorld world) { var dimension = world.getRegistryKey(); var worldHandle = this.worlds.remove(dimension); if (worldHandle != null) { this.space.onRemoveWorld(dimension); - worldHandle.delete(); + worldHandle.unload(); return true; } else { return false; @@ -52,7 +70,7 @@ public boolean remove(ServerWorld world) { void clear() { for (var worldHandler : this.worlds.values()) { - worldHandler.delete(); + worldHandler.unload(); } this.worlds.clear(); } diff --git a/src/testmod/java/xyz/nucleoid/plasmid/test/PersistentGame.java b/src/testmod/java/xyz/nucleoid/plasmid/test/PersistentGame.java new file mode 100644 index 00000000..05862158 --- /dev/null +++ b/src/testmod/java/xyz/nucleoid/plasmid/test/PersistentGame.java @@ -0,0 +1,55 @@ +package xyz.nucleoid.plasmid.test; + +import net.minecraft.registry.RegistryKeys; +import net.minecraft.util.*; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.GameMode; +import net.minecraft.world.GameRules; +import net.minecraft.world.biome.BiomeKeys; +import net.minecraft.world.gen.chunk.FlatChunkGenerator; +import net.minecraft.world.gen.chunk.FlatChunkGeneratorConfig; +import xyz.nucleoid.fantasy.RuntimeWorldConfig; +import xyz.nucleoid.plasmid.api.game.*; +import xyz.nucleoid.plasmid.api.game.event.GamePlayerEvents; +import xyz.nucleoid.plasmid.api.game.player.JoinOffer; +import xyz.nucleoid.plasmid.api.game.rule.GameRuleType; +import xyz.nucleoid.stimuli.event.EventResult; +import xyz.nucleoid.stimuli.event.player.PlayerDeathEvent; + +import java.util.List; +import java.util.Optional; + +public final class PersistentGame { + public static GameOpenProcedure open(GameOpenContext context) { + var biome = context.server().getRegistryManager().getOrThrow(RegistryKeys.BIOME).getOrThrow(BiomeKeys.CHERRY_GROVE); + + var worldConfig = new RuntimeWorldConfig() + .setGenerator(new FlatChunkGenerator(new FlatChunkGeneratorConfig(Optional.empty(), biome, List.of()))) + .setTimeOfDay(6000) + .setGameRule(GameRules.KEEP_INVENTORY, true); + + return context.open((activity) -> { + var gameSpace = activity.getGameSpace(); + var world = gameSpace.getWorlds().addPersistent(Identifier.of("testmod", "persistent_world"), worldConfig); + + + activity.listen(GamePlayerEvents.OFFER, JoinOffer::accept); + activity.listen(GamePlayerEvents.ACCEPT, acceptor -> + acceptor.teleport(world, new Vec3d(0.0, 65.0, 0.0)) + .thenRunForEach(joiningPlayer -> { + joiningPlayer.changeGameMode(GameMode.CREATIVE); + }) + ); + + + activity.allow(GameRuleType.PVP).allow(GameRuleType.MODIFY_ARMOR); + activity.deny(GameRuleType.FALL_DAMAGE).deny(GameRuleType.HUNGER); + activity.deny(GameRuleType.THROW_ITEMS); + + activity.listen(PlayerDeathEvent.EVENT, (player, source) -> { + player.setPos(0.0, 65.0, 0.0); + return EventResult.DENY; + }); + }); + } +} diff --git a/src/testmod/java/xyz/nucleoid/plasmid/test/TestInitializer.java b/src/testmod/java/xyz/nucleoid/plasmid/test/TestInitializer.java index 34522293..e87920a7 100644 --- a/src/testmod/java/xyz/nucleoid/plasmid/test/TestInitializer.java +++ b/src/testmod/java/xyz/nucleoid/plasmid/test/TestInitializer.java @@ -1,5 +1,6 @@ package xyz.nucleoid.plasmid.test; +import com.mojang.serialization.MapCodec; import eu.pb4.polymer.blocks.api.BlockModelType; import eu.pb4.polymer.blocks.api.BlockResourceCreator; import eu.pb4.polymer.blocks.api.PolymerBlockModel; @@ -15,6 +16,7 @@ import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKeys; import net.minecraft.util.Identifier; +import net.minecraft.util.Unit; import xyz.nucleoid.plasmid.api.game.GameType; import xyz.nucleoid.plasmid.api.game.common.GameResourcePack; @@ -45,6 +47,7 @@ public class TestInitializer implements ModInitializer { @Override public void onInitialize() { GameType.register(Identifier.of(ID, "test"), TestConfig.CODEC, TestGame::open); + GameType.register(Identifier.of(ID, "persistent"), MapCodec.unit(Unit.INSTANCE), PersistentGame::open); GameType.register(Identifier.of(ID, "no_join"), TestConfig.CODEC, PlayerlessGame::open); GameType.register(Identifier.of(ID, "test_rp"), TestConfig.CODEC, TestGameWithResourcePack::open); GameType.register(Identifier.of(ID, "jank"), TestConfig.CODEC, JankGame::open); diff --git a/src/testmod/resources/data/testmod/plasmid/game/persistent.json b/src/testmod/resources/data/testmod/plasmid/game/persistent.json new file mode 100644 index 00000000..1a9b6c04 --- /dev/null +++ b/src/testmod/resources/data/testmod/plasmid/game/persistent.json @@ -0,0 +1,5 @@ +{ + "type": "testmod:persistent", + "icon": "dirt", + "description": [] +}