diff --git a/src/main/java/me/cortex/voxy/client/VoxyClient.java b/src/main/java/me/cortex/voxy/client/VoxyClient.java index dd9cc13b..2369e208 100644 --- a/src/main/java/me/cortex/voxy/client/VoxyClient.java +++ b/src/main/java/me/cortex/voxy/client/VoxyClient.java @@ -3,6 +3,7 @@ package me.cortex.voxy.client; import me.cortex.voxy.client.core.VoxelCore; import me.cortex.voxy.client.saver.ContextSelectionSystem; import me.cortex.voxy.client.terrain.WorldImportCommand; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientWorldEvents; @@ -14,5 +15,6 @@ public class VoxyClient implements ClientModInitializer { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { dispatcher.register(WorldImportCommand.register()); }); + VoxyCommon.setInstanceFactory(VoxyClientInstance::new); } } diff --git a/src/main/java/me/cortex/voxy/client/VoxyClientInstance.java b/src/main/java/me/cortex/voxy/client/VoxyClientInstance.java new file mode 100644 index 00000000..0a8f0c63 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/VoxyClientInstance.java @@ -0,0 +1,37 @@ +package me.cortex.voxy.client; + +import me.cortex.voxy.client.core.WorldImportWrapper; +import me.cortex.voxy.client.saver.ContextSelectionSystem; +import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.commonImpl.IVoxyWorldGetter; +import me.cortex.voxy.commonImpl.IVoxyWorldSetter; +import me.cortex.voxy.commonImpl.VoxyInstance; +import net.minecraft.client.world.ClientWorld; + +public class VoxyClientInstance extends VoxyInstance { + private static final ContextSelectionSystem SELECTOR = new ContextSelectionSystem(); + public WorldImportWrapper importWrapper; + + public VoxyClientInstance() { + super(12); + } + + @Override + public void stopWorld(WorldEngine world) { + if (this.importWrapper != null) { + this.importWrapper.stopImporter(); + this.importWrapper = null; + } + super.stopWorld(world); + } + + public WorldEngine getOrMakeRenderWorld(ClientWorld world) { + var vworld = ((IVoxyWorldGetter)world).getWorldEngine(); + if (vworld == null) { + vworld = this.createWorld(SELECTOR.getBestSelectionOrCreate(world).createSectionStorageBackend()); + ((IVoxyWorldSetter)world).setWorldEngine(vworld); + this.importWrapper = new WorldImportWrapper(this.threadPool, vworld); + } + return vworld; + } +} diff --git a/src/main/java/me/cortex/voxy/client/mixin/joml/AccessFrustumIntersection.java b/src/main/java/me/cortex/voxy/client/mixin/joml/AccessFrustumIntersection.java deleted file mode 100644 index cc7d4692..00000000 --- a/src/main/java/me/cortex/voxy/client/mixin/joml/AccessFrustumIntersection.java +++ /dev/null @@ -1,11 +0,0 @@ -package me.cortex.voxy.client.mixin.joml; - -import org.joml.FrustumIntersection; -import org.joml.Vector4f; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(value = FrustumIntersection.class, remap = false) -public interface AccessFrustumIntersection { - @Accessor Vector4f[] getPlanes(); -} diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinClientPlayNetworkHandler.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinClientPlayNetworkHandler.java new file mode 100644 index 00000000..3c9d2e60 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinClientPlayNetworkHandler.java @@ -0,0 +1,23 @@ +package me.cortex.voxy.client.mixin.minecraft; + +import me.cortex.voxy.client.LoadException; +import me.cortex.voxy.client.config.VoxyConfig; +import me.cortex.voxy.commonImpl.VoxyCommon; +import net.minecraft.client.network.ClientCommonNetworkHandler; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.packet.Packet; +import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientPlayNetworkHandler.class) +public class MixinClientPlayNetworkHandler { + @Inject(method = "onGameJoin", at = @At(value = "NEW", target = "(Lnet/minecraft/client/network/ClientPlayNetworkHandler;Lnet/minecraft/client/world/ClientWorld$Properties;Lnet/minecraft/registry/RegistryKey;Lnet/minecraft/registry/entry/RegistryEntry;IILnet/minecraft/client/render/WorldRenderer;ZJI)Lnet/minecraft/client/world/ClientWorld;", shift = At.Shift.BEFORE)) + private void voxy$init(GameJoinS2CPacket packet, CallbackInfo ci) { + if (VoxyConfig.CONFIG.enabled) { + VoxyCommon.createInstance(); + } + } +} diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java index 02e677ac..5f83f085 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinMinecraftClient.java @@ -23,10 +23,11 @@ public class MixinMinecraftClient { VoxyCommon.shutdownInstance(); } + /* @Inject(method = "joinWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setWorld(Lnet/minecraft/client/world/ClientWorld;)V", shift = At.Shift.BEFORE)) private void voxy$injectInitialization(ClientWorld world, DownloadingTerrainScreen.WorldEntryReason worldEntryReason, CallbackInfo ci) { if (VoxyConfig.CONFIG.enabled) { VoxyCommon.createInstance(); } - } + }*/ } diff --git a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java index bdfdd830..978b0712 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/mixin/minecraft/MixinWorldRenderer.java @@ -1,11 +1,12 @@ package me.cortex.voxy.client.mixin.minecraft; -import me.cortex.voxy.client.VoxyClient; +import me.cortex.voxy.client.VoxyClientInstance; import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.core.IGetVoxyRenderSystem; import me.cortex.voxy.client.core.rendering.VoxyRenderSystem; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.commonImpl.IVoxyWorldGetter; import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyInstance; import net.minecraft.client.render.*; @@ -46,11 +47,25 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem { } } - @Inject(method = "setWorld", at = @At("TAIL")) + @Unique private ClientWorld refCopy; + + @Inject(method = "setWorld", at = @At("HEAD")) private void initVoxelCore(ClientWorld world, CallbackInfo ci) { + this.refCopy = this.world; + } + + @Inject(method = "setWorld", at = @At("TAIL")) + private void voxy$setWorld(ClientWorld world, CallbackInfo ci) { if (world == null) { this.shutdownRenderer(); } + //Release the client world + if (this.refCopy != null) { + var engine = ((IVoxyWorldGetter)this.refCopy).getWorldEngine(); + if (engine != null) { + VoxyCommon.getInstance().stopWorld(engine); + } + } } @Inject(method = "close", at = @At("HEAD")) @@ -73,12 +88,12 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem { Logger.info("Not creating renderer due to disabled"); return; } - var instance = VoxyCommon.getInstance(); + var instance = (VoxyClientInstance)VoxyCommon.getInstance(); if (instance == null) { Logger.error("Not creating renderer due to null instance"); return; } - WorldEngine world = instance.getOrMakeWorld(this.world); + WorldEngine world = instance.getOrMakeRenderWorld(this.world); if (world == null) { Logger.error("Null world selected"); return; diff --git a/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java b/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java index f2f55658..957d4eec 100644 --- a/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java +++ b/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java @@ -5,6 +5,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import me.cortex.voxy.client.VoxyClientInstance; import me.cortex.voxy.commonImpl.VoxyCommon; import me.cortex.voxy.commonImpl.VoxyInstance; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; @@ -47,7 +48,7 @@ public class WorldImportCommand { } private static boolean fileBasedImporter(File directory) { - var instance = VoxyCommon.getInstance(); + var instance = (VoxyClientInstance)VoxyCommon.getInstance(); if (instance == null) { return false; } @@ -133,7 +134,7 @@ public class WorldImportCommand { innerDir = ctx.getArgument("innerPath", String.class); } catch (Exception e) {} - var instance = VoxyCommon.getInstance(); + var instance = (VoxyClientInstance)VoxyCommon.getInstance(); if (instance == null) { return 1; } @@ -143,7 +144,7 @@ public class WorldImportCommand { } private static int cancelImport(CommandContext fabricClientCommandSourceCommandContext) { - var instance = VoxyCommon.getInstance(); + var instance = (VoxyClientInstance)VoxyCommon.getInstance(); if (instance == null) { return 1; } diff --git a/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java b/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java index 6e185a02..b32a1c82 100644 --- a/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java +++ b/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java @@ -164,4 +164,8 @@ public class ServiceSlice extends TrackedObject { this.threadPool.steal(this); return true; } + + public boolean isAlive() { + return this.alive; + } } diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java index 99beb40a..fbc72187 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java +++ b/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java @@ -26,7 +26,7 @@ public class VoxyCommon implements ModInitializer { } //This is hardcoded like this because people do not understand what they are doing - private static final boolean GlobalVerificationDisableOverride = false;//System.getProperty("voxy.verificationDisableOverride", "false").equals("true"); + private static final boolean GlobalVerificationDisableOverride = true;//System.getProperty("voxy.verificationDisableOverride", "false").equals("true"); public static boolean isVerificationFlagOn(String name) { return (!GlobalVerificationDisableOverride) && System.getProperty("voxy."+name, "true").equals("true"); } @@ -41,7 +41,14 @@ public class VoxyCommon implements ModInitializer { public interface IInstanceFactory {VoxyInstance create();} private static VoxyInstance INSTANCE; - private static IInstanceFactory FACTORY; + private static IInstanceFactory FACTORY = null; + + public static void setInstanceFactory(IInstanceFactory factory) { + if (FACTORY != null) { + throw new IllegalStateException("Cannot set instance factory more than once"); + } + FACTORY = factory; + } public static VoxyInstance getInstance() { return INSTANCE; diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java index 44a3c99e..2b69bc8a 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java +++ b/src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java @@ -23,6 +23,7 @@ public class VoxyInstance { protected final Set activeWorlds = new HashSet<>(); public VoxyInstance(int threadCount) { + Logger.info("Initializing voxy instance"); this.threadPool = new ServiceThreadPool(threadCount); this.savingService = new SectionSavingService(this.threadPool); this.ingestService = new VoxelIngestService(this.threadPool); @@ -36,9 +37,22 @@ public class VoxyInstance { public void shutdown() { Logger.info("Shutdown voxy instance"); + try {this.ingestService.shutdown();} catch (Exception e) {Logger.error(e);} try {this.savingService.shutdown();} catch (Exception e) {Logger.error(e);} + + if (!this.activeWorlds.isEmpty()) { + Logger.error("Not all worlds shutdown, force closing " + this.activeWorlds.size() + " worlds"); + for (var world : new HashSet<>(this.activeWorlds)) {//Create a clone + this.stopWorld(world); + } + } + try {this.threadPool.shutdown();} catch (Exception e) {Logger.error(e);} + + if (!this.activeWorlds.isEmpty()) { + throw new IllegalStateException("Not all worlds shutdown"); + } } public ServiceThreadPool getThreadPool() { @@ -94,29 +108,9 @@ public class VoxyInstance { throw new IllegalStateException("World cannot be in world set and not alive"); } - - if (this.importWrapper != null) { - this.importWrapper.stopImporter(); - this.importWrapper = null; - } - this.flush(); - world.free(); this.activeWorlds.remove(world); } - - private static final ContextSelectionSystem SELECTOR = new ContextSelectionSystem(); - public WorldImportWrapper importWrapper; - public WorldEngine getOrMakeWorld(ClientWorld world) { - var vworld = ((IVoxyWorldGetter)world).getWorldEngine(); - if (vworld == null) { - vworld = this.createWorld(SELECTOR.getBestSelectionOrCreate(world).createSectionStorageBackend()); - ((IVoxyWorldSetter)world).setWorldEngine(vworld); - this.importWrapper = new WorldImportWrapper(this.threadPool, vworld); - } - return vworld; - } - } \ No newline at end of file diff --git a/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java b/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java index dceb8b74..361aa4a6 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java +++ b/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java @@ -16,17 +16,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter { @Unique private WorldEngine voxyWorld; - @Inject(method = "close", at = @At("HEAD")) - private void closeVoxyWorld(CallbackInfo ci) { - if (this.voxyWorld != null) { - //TODO: FIXME: DONT DO THIS, this is a hack to ensure everything is saved - var instance = VoxyCommon.getInstance(); - try {instance.stopWorld(this.voxyWorld); this.voxyWorld = null;} catch (Exception e) { - Logger.error("Failed to shutdown voxy world engine.", e); - } - } - } - @Override public WorldEngine getWorldEngine() { return this.voxyWorld; diff --git a/src/main/resources/client.voxy.mixins.json b/src/main/resources/client.voxy.mixins.json index 6f99c93d..0ace0249 100644 --- a/src/main/resources/client.voxy.mixins.json +++ b/src/main/resources/client.voxy.mixins.json @@ -3,8 +3,8 @@ "package": "me.cortex.voxy.client.mixin", "compatibilityLevel": "JAVA_17", "client": [ - "joml.AccessFrustumIntersection", "minecraft.MixinClientCommonNetworkHandler", + "minecraft.MixinClientPlayNetworkHandler", "minecraft.MixinDebugHud", "minecraft.MixinMinecraftClient", "minecraft.MixinThreadExecutor",