diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMain.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMain.java index 76ef5d1a8..506ba34d9 100644 --- a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMain.java +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMain.java @@ -5,6 +5,7 @@ import com.qouteall.immersive_portals.chunk_loading.EntitySync; import com.qouteall.immersive_portals.chunk_loading.NewChunkTrackingGraph; import com.qouteall.immersive_portals.chunk_loading.WorldInfoSender; +import com.qouteall.immersive_portals.miscellaneous.GcMonitor; import com.qouteall.immersive_portals.my_util.MyTaskList; import com.qouteall.immersive_portals.my_util.Signal; import com.qouteall.immersive_portals.portal.PortalExtension; @@ -56,6 +57,8 @@ public static void init() { PortalExtension.init(); + GcMonitor.initCommon(); + } } diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMainClient.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMainClient.java index 20f1c84a3..4b2ef8e7f 100644 --- a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMainClient.java +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/ModMainClient.java @@ -2,6 +2,7 @@ import com.qouteall.hiding_in_the_bushes.MyNetworkClient; import com.qouteall.hiding_in_the_bushes.O_O; +import com.qouteall.immersive_portals.miscellaneous.GcMonitor; import com.qouteall.immersive_portals.my_util.MyTaskList; import com.qouteall.immersive_portals.optifine_compatibility.OFBuiltChunkStorageFix; import com.qouteall.immersive_portals.optifine_compatibility.OFGlobal; @@ -122,6 +123,8 @@ public static void init() { showOptiFineWarning(); } + GcMonitor.initClient(); + Helper.log(OFInterface.isOptifinePresent ? "Optifine is present" : "Optifine is not present"); } diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/chunk_loading/NewChunkTrackingGraph.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/chunk_loading/NewChunkTrackingGraph.java index 3113633f5..560f0bfb0 100644 --- a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/chunk_loading/NewChunkTrackingGraph.java +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/chunk_loading/NewChunkTrackingGraph.java @@ -6,6 +6,7 @@ import com.qouteall.immersive_portals.McHelper; import com.qouteall.immersive_portals.ModMain; import com.qouteall.immersive_portals.ducks.IEEntity; +import com.qouteall.immersive_portals.miscellaneous.GcMonitor; import com.qouteall.immersive_portals.my_util.SignalBiArged; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.LongArrayList; @@ -268,7 +269,14 @@ private static boolean shouldUnload(long currTime, PlayerWatchRecord record) { if (record.player.removed) { return true; } - return currTime - record.lastWatchTime > (long) Global.chunkUnloadDelayTicks; + long unloadDelay = Global.chunkUnloadDelayTicks; + + if (GcMonitor.isMemoryNotEnough()) { + // does not delay unloading + unloadDelay = 21; + } + + return currTime - record.lastWatchTime > unloadDelay; } private static void tick() { diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/FPSMonitor.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/miscellaneous/FPSMonitor.java similarity index 93% rename from imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/FPSMonitor.java rename to imm_ptl_core/src/main/java/com/qouteall/immersive_portals/miscellaneous/FPSMonitor.java index f5cda3465..be9308623 100644 --- a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/FPSMonitor.java +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/miscellaneous/FPSMonitor.java @@ -1,4 +1,4 @@ -package com.qouteall.immersive_portals.render; +package com.qouteall.immersive_portals.miscellaneous; import java.util.ArrayDeque; diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/miscellaneous/GcMonitor.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/miscellaneous/GcMonitor.java new file mode 100644 index 000000000..a45576c0b --- /dev/null +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/miscellaneous/GcMonitor.java @@ -0,0 +1,97 @@ +package com.qouteall.immersive_portals.miscellaneous; + +import com.qouteall.hiding_in_the_bushes.O_O; +import com.qouteall.immersive_portals.Helper; +import com.qouteall.immersive_portals.McHelper; +import com.qouteall.immersive_portals.ModMain; +import com.qouteall.immersive_portals.my_util.LimitedLogger; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.MessageType; +import net.minecraft.server.MinecraftServer; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Util; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.WeakHashMap; + +public class GcMonitor { + private static boolean memoryNotEnough = false; + + private static final WeakHashMap lastCollectCount = + new WeakHashMap<>(); + + private static final LimitedLogger limitedLogger = new LimitedLogger(3); + + @Environment(EnvType.CLIENT) + public static void initClient() { + ModMain.preGameRenderSignal.connect(GcMonitor::update); + } + + public static void initCommon() { + ModMain.postServerTickSignal.connect(() -> { + MinecraftServer server = McHelper.getServer(); + if (server != null) { + if (server.isDedicated()) { + update(); + } + } + }); + } + + private static void update() { + + for (GarbageCollectorMXBean garbageCollectorMXBean : ManagementFactory.getGarbageCollectorMXBeans()) { + long currCount = garbageCollectorMXBean.getCollectionCount(); + + Long lastCount = lastCollectCount.get(garbageCollectorMXBean); + lastCollectCount.put(garbageCollectorMXBean, currCount); + + if (lastCount != null) { + if (lastCount != currCount) { + onGced(); + } + } + } + } + + private static void onGced() { + long maxMemory = Runtime.getRuntime().maxMemory(); + long totalMemory = Runtime.getRuntime().totalMemory(); + long freeMemory = Runtime.getRuntime().freeMemory(); + long usedMemory = totalMemory - freeMemory; + + double usage = ((double) usedMemory) / maxMemory; + + if (usage > 0.8) { + memoryNotEnough = true; + + + Helper.err( + "Memory not enough. Try to Shrink loading distance or allocate more memory." + + " If this happens with low loading distance, it usually indicates memory leak" + ); + + if (!O_O.isDedicatedServer()) { + informMemoryNotEnoughClient(); + } + } + } + + @Environment(EnvType.CLIENT) + private static void informMemoryNotEnoughClient() { + limitedLogger.invoke(() -> { + MinecraftClient.getInstance().inGameHud.addChatMessage( + MessageType.SYSTEM, + new TranslatableText("imm_ptl.memory_not_enough"), + Util.NIL_UUID + ); + }); + } + + public static boolean isMemoryNotEnough() { + return memoryNotEnough; + } +} diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/mixin/client/MixinMinecraftClient.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/mixin/client/MixinMinecraftClient.java index dcad6c676..920d8a313 100644 --- a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/mixin/client/MixinMinecraftClient.java +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/mixin/client/MixinMinecraftClient.java @@ -3,9 +3,9 @@ import com.qouteall.immersive_portals.CGlobal; import com.qouteall.immersive_portals.ModMain; import com.qouteall.immersive_portals.ducks.IEMinecraftClient; +import com.qouteall.immersive_portals.miscellaneous.FPSMonitor; import com.qouteall.immersive_portals.network.CommonNetwork; import com.qouteall.immersive_portals.network.CommonNetworkClient; -import com.qouteall.immersive_portals.render.FPSMonitor; import com.qouteall.immersive_portals.render.context_management.WorldRenderInfo; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; diff --git a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/context_management/RenderStates.java b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/context_management/RenderStates.java index 731e04ec4..cbab0b73d 100644 --- a/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/context_management/RenderStates.java +++ b/imm_ptl_core/src/main/java/com/qouteall/immersive_portals/render/context_management/RenderStates.java @@ -7,8 +7,8 @@ import com.qouteall.immersive_portals.OFInterface; import com.qouteall.immersive_portals.ducks.IEGameRenderer; import com.qouteall.immersive_portals.ducks.IEWorldRenderer; +import com.qouteall.immersive_portals.miscellaneous.FPSMonitor; import com.qouteall.immersive_portals.portal.PortalLike; -import com.qouteall.immersive_portals.render.FPSMonitor; import com.qouteall.immersive_portals.render.MyRenderHelper; import com.qouteall.immersive_portals.render.QueryManager; import net.minecraft.client.MinecraftClient; diff --git a/imm_ptl_core/src/main/resources/assets/immersive_portals/lang/en_us.json b/imm_ptl_core/src/main/resources/assets/immersive_portals/lang/en_us.json index 8ffcddb97..cc4d40b35 100644 --- a/imm_ptl_core/src/main/resources/assets/immersive_portals/lang/en_us.json +++ b/imm_ptl_core/src/main/resources/assets/immersive_portals/lang/en_us.json @@ -78,5 +78,6 @@ "imm_ptl.loop": "Loop", "imm_ptl.enabled": "Enabled", "imm_ptl.disabled": "Disabled", - "imm_ptl.loading_datapack_dimensions": "Loading Datapack Dimensions" + "imm_ptl.loading_datapack_dimensions": "Loading Datapack Dimensions", + "imm_ptl.memory_not_enough": "Memory not enough. You may experience GC lag. Try to Shrink render distance or allocate more memory." }