From 95220d4fd77bad93aa7bff79c0080d78cc4c8df0 Mon Sep 17 00:00:00 2001 From: tom lee Date: Fri, 4 Feb 2022 00:11:53 +0800 Subject: [PATCH] Add failsafe for low RAM issue. --- .../core/builders/lodBuilding/LodBuilder.java | 5 ++++ .../worldGeneration/BatchGenerator.java | 2 ++ .../lod/core/objects/lod/LodDimension.java | 27 ++++++++++++++++--- .../lod/core/util/DetailDistanceUtil.java | 9 ++++--- .../com/seibel/lod/core/util/LodUtil.java | 11 ++++++++ 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java index 18c81bd55..220fc0d97 100644 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java +++ b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java @@ -205,6 +205,11 @@ public class LodBuilder region.isWriting++; try { if (region.getMinDetailLevel()!= 0) { + if (!LodUtil.checkRamUsage(0.05, 16)) { + ClientApi.LOGGER.warn("LodBuilder: Not enough RAM avalible for building lods! Skipping..."); + return false; + } + LodRegion newRegion = lodDim.getRegionFromFile(region, (byte)0, region.getVerticalQuality()); if (region!=newRegion) throw new RuntimeException(); diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java index abf2e1b2c..77df3cbd9 100644 --- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java +++ b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java @@ -83,6 +83,8 @@ public class BatchGenerator { generationGroup.updateAllFutures(); if (!MC.hasSinglePlayerServer()) return; + if (!LodUtil.checkRamUsage(0.1, 64)) return; + int eventsCount = generationGroup.getEventCount(); // If we still all jobs running, return. if (eventsCount >= estimatedPointsToQueue) { diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java index eb2f00140..71dd5b2a3 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java @@ -26,6 +26,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import javax.crypto.spec.GCMParameterSpec; + import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.enums.config.DistanceGenerationMode; import com.seibel.lod.core.enums.config.DropoffQuality; @@ -316,12 +318,21 @@ public class LodDimension }; cutAndExpandThread.execute(thread); } - + + private boolean expandOrLoadPaused = false; /** Either expands or loads all regions in the rendered LOD area */ public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ) { if (isExpanding) return; + // We have less than 10% or 1MB ram left. Don't expend. + if (expandOrLoadPaused && !LodUtil.checkRamUsage(0.4, 512)) { + //ClientApi.LOGGER.info("Not enough ram for expandOrLoadThread. Skipping..."); + return; + } else if (expandOrLoadPaused) { + ClientApi.LOGGER.info("Enough ram for expandOrLoadThread. Restarting..."); + } isExpanding = true; + expandOrLoadPaused = false; VerticalQuality verticalQuality = CONFIG.client().graphics().quality().getVerticalQuality(); DropoffQuality dropoffQuality = CONFIG.client().graphics().quality().getDropoffQuality(); @@ -333,9 +344,19 @@ public class LodDimension // for the same location Runnable thread = () -> { //ClientApi.LOGGER.info("LodDim expend Region: " + playerPosX + "," + playerPosZ); - Pos minPos = regions.getMinInRange(); iterateWithSpiral((int x, int z) -> { + if (expandOrLoadPaused) return; + if (!LodUtil.checkRamUsage(0.1, 32)) { + Runtime.getRuntime().gc(); + if (!LodUtil.checkRamUsage(0.2, 64)) { + ClientApi.LOGGER.warn("Not enough ram for expandOrLoadThread. Pausing until Ram is freed..."); + // We have less than 10% or 1MB ram left. Don't expend. + expandOrLoadPaused = true; + saveDirtyRegionsToFile(false); + return; + } + } int regionX; int regionZ; LodRegion region; @@ -355,8 +376,8 @@ public class LodDimension playerPosZ); minDetail = DetailDistanceUtil.getDetailLevelFromDistance(minDistance); maxDetail = DetailDistanceUtil.getDetailLevelFromDistance(maxDistance); - boolean updated = false; + boolean expended = false; if (region == null) { region = getRegionFromFile(regionPos, minDetail, verticalQuality); regions.set(regionX, regionZ, region); diff --git a/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java index 76aee1808..7cfda903c 100644 --- a/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java +++ b/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java @@ -56,6 +56,7 @@ public class DetailDistanceUtil maxDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 16 * 8; } + /*// Need UPDATE and BUG FIX public static int baseDistanceFunction(int detail) { if (detail <= minGenDetail) @@ -79,11 +80,11 @@ public class DetailDistanceUtil public static int getDrawDistanceFromDetail(int detail) { return baseDistanceFunction(detail); - } + }*/ public static byte baseInverseFunction(int distance, byte minDetail) { - byte detail; + int detail; distance -= minDetailDistance; if (distance < 0 || CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality()) @@ -92,12 +93,12 @@ public class DetailDistanceUtil double scaledDistance = distance; scaledDistance /= distanceUnit; if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST) - detail = (byte) (scaledDistance); + detail = (int) (scaledDistance); else { double base = CONFIG.client().graphics().quality().getHorizontalQuality().quadraticBase; double logBase = Math.log(base); - detail = (byte) (Math.log(scaledDistance) / logBase); + detail = (int) (Math.log(scaledDistance) / logBase); } return (byte) LodUtil.clamp(minDetail, detail+minDetail, maxDetail - 1); } diff --git a/src/main/java/com/seibel/lod/core/util/LodUtil.java b/src/main/java/com/seibel/lod/core/util/LodUtil.java index d5644873c..53697714f 100644 --- a/src/main/java/com/seibel/lod/core/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/core/util/LodUtil.java @@ -434,4 +434,15 @@ public class LodUtil numb = Float.intBitsToFloat(i); return numb * (1.5F - half * numb * numb); } + + // True if the requested threshold pass, or false otherwise + // For details, see: + // https://stackoverflow.com/questions/3571203/what-are-runtime-getruntime-totalmemory-and-freememory + public static boolean checkRamUsage(double minFreeMemoryPercent, int minFreeMemoryMB) { + long freeMem = Runtime.getRuntime().freeMemory() + Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory(); + if (freeMem < minFreeMemoryMB * 1024 * 1024) return false; + long maxMem = Runtime.getRuntime().maxMemory(); + if (freeMem/(double)maxMem < minFreeMemoryPercent) return false; + return true; + } }