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 @@
+
+
+
@@ -9,4 +12,4 @@
-
+
\ 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": []
+}