diff --git a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java index adc032a3..1416d53c 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java @@ -23,6 +23,7 @@ public class WorldEngine { private ISectionChangeCallback dirtyCallback; private ISectionSaveCallback saveCallback; private final int maxMipLevels; + private volatile boolean isLive = true; public void setDirtyCallback(ISectionChangeCallback callback) { this.dirtyCallback = callback; @@ -33,6 +34,7 @@ public class WorldEngine { } public Mapper getMapper() {return this.mapper;} + public boolean isLive() {return this.isLive;} public WorldEngine(SectionStorage storage, int cacheCount) { this(storage, MAX_LOD_LAYERS, cacheCount); } @@ -46,18 +48,22 @@ public class WorldEngine { } public WorldSection acquireIfExists(int lvl, int x, int y, int z) { + if (!this.isLive) throw new IllegalStateException("World is not live"); return this.sectionTracker.acquire(lvl, x, y, z, true); } public WorldSection acquire(int lvl, int x, int y, int z) { + if (!this.isLive) throw new IllegalStateException("World is not live"); return this.sectionTracker.acquire(lvl, x, y, z, false); } public WorldSection acquire(long pos) { + if (!this.isLive) throw new IllegalStateException("World is not live"); return this.sectionTracker.acquire(pos, false); } public WorldSection acquireIfExists(long pos) { + if (!this.isLive) throw new IllegalStateException("World is not live"); return this.sectionTracker.acquire(pos, true); } @@ -92,6 +98,7 @@ public class WorldEngine { } public void markDirty(WorldSection section, int changeState) { + if (!this.isLive) throw new IllegalStateException("World is not live"); if (this.dirtyCallback != null) { this.dirtyCallback.accept(section, changeState); } @@ -106,6 +113,7 @@ public class WorldEngine { //NOTE: THIS RUNS ON THE THREAD IT WAS EXECUTED ON, when this method exits, the calling method may assume that VoxelizedSection is no longer needed public void insertUpdate(VoxelizedSection section) {//TODO: add a bitset of levels to update and if it should force update + if (!this.isLive) throw new IllegalStateException("World is not live"); boolean shouldCheckEmptiness = false; WorldSection previousSection = null; @@ -200,6 +208,7 @@ public class WorldEngine { } public void shutdown() { + this.isLive = false; try {this.mapper.close();} catch (Exception e) {Logger.error(e);} try {this.storage.flush();} catch (Exception e) {Logger.error(e);} //Shutdown in this order to preserve as much data as possible diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java index 5594b38c..94acf9e1 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java +++ b/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java @@ -4,7 +4,6 @@ import me.cortex.voxy.client.core.WorldImportWrapper; import me.cortex.voxy.client.saver.ContextSelectionSystem; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.config.section.SectionStorage; -import me.cortex.voxy.common.config.storage.StorageBackend; import me.cortex.voxy.common.thread.ServiceThreadPool; import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.world.WorldEngine; @@ -74,13 +73,22 @@ public class VoxyInstance { return world; } - private static final ContextSelectionSystem SELECTOR = new ContextSelectionSystem(); + //There are 4 possible "states" for world selection/management + // 1) dedicated server + // 2) client singleplayer + // 3) client singleplayer as lan host (so also a server) + // 4) client multiplayer (remote server) + //The thing with singleplayer is that it is more efficent to make it bound to clientworld (think) + // so if make into singleplayer as host, would need to reload the system into that mode + // so that the world renderer uses the WorldEngine of the server + + private static final ContextSelectionSystem SELECTOR = new ContextSelectionSystem(); + public WorldImportWrapper importWrapper; public WorldEngine getOrMakeWorld(ClientWorld world) { var vworld = ((IVoxyWorldGetter)world).getWorldEngine(); if (vworld == null) { - vworld = new WorldEngine(SELECTOR.getBestSelectionOrCreate(world).createSectionStorageBackend(), 1024); - vworld.setSaveCallback(this.savingService::enqueueSave); + vworld = this.createWorld(SELECTOR.getBestSelectionOrCreate(world).createSectionStorageBackend()); ((IVoxyWorldSetter)world).setWorldEngine(vworld); this.importWrapper = new WorldImportWrapper(this.threadPool, vworld); } @@ -88,13 +96,24 @@ public class VoxyInstance { } - public WorldImportWrapper importWrapper; public void stopWorld(WorldEngine world) { + if (!this.activeWorlds.contains(world)) { + if (world.isLive()) { + throw new IllegalStateException("World cannot be live and not in world set"); + } + throw new IllegalStateException("Cannot close world which is not part of instance"); + } + if (!world.isLive()) { + throw new IllegalStateException("World cannot be in world set and not alive"); + } + if (this.importWrapper != null) { this.importWrapper.stopImporter(); } this.flush(); + world.shutdown(); + this.activeWorlds.remove(world); } } \ No newline at end of file