From 89e92504e47f3e1a9639e13da6dda672e5b38a67 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 08:20:02 -0500 Subject: [PATCH 01/16] Fix null pointer in RenderDataMetaFile --- .../core/file/renderfile/RenderDataMetaFile.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java index 92a2dd6d9..71ab713a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java @@ -298,7 +298,6 @@ public class RenderDataMetaFile extends AbstractMetaDataContainerFile implements // cache handler // //===============// - // TODO public CompletableFuture updateRenderCacheAsync(ColumnRenderSource renderSource) { DebugRenderer.BoxWithLife debugBox = new DebugRenderer.BoxWithLife(new DebugRenderer.Box(renderSource.sectionPos, 74f, 86f, 0.1f, Color.red), 1.0, 32f, Color.green.darker()); @@ -326,7 +325,7 @@ public class RenderDataMetaFile extends AbstractMetaDataContainerFile implements // get the metaFile's version FullDataMetaFile renderSourceMetaFile = this.fullDataSourceProvider.getFileIfExist(this.pos); - if (renderSourceMetaFile != null) + if (renderSourceMetaFile != null && renderSourceMetaFile.baseMetaData != null) { renderDataVersionRef.value = renderSourceMetaFile.baseMetaData.checksum; } From 5b8649630ccd114858b2650c0ec98084e4a7cb1e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 08:32:22 -0500 Subject: [PATCH 02/16] WorldGenerationQueue remove unused code and cleanup --- .../core/generation/WorldGenerationQueue.java | 109 ++++-------------- 1 file changed, 20 insertions(+), 89 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 11d3da087..a52d8d43f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -199,9 +199,9 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender this.generationTargetPos = targetPos; // only start the queuing thread once - if (!generationQueueStarted) + if (!this.generationQueueStarted) { - startWorldGenQueuingThread(); + this.startWorldGenQueuingThread(); } } catch (Exception e) @@ -221,25 +221,17 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // loop until the generator is shutdown while (!Thread.interrupted()) { -// LOGGER.info("pre task count: " + this.numberOfTasksQueued); - - // recenter the generator tasks, this is done to prevent generating chunks where the player isn't - //this.waitingTaskQuadTree.setCenterBlockPos(this.generationTargetPos); // queue generation tasks until the generator is full, or there are no more tasks to generate boolean taskStarted = true; while (!this.generator.isBusy() && taskStarted) { - //this.removeGarbageCollectedTasks(); // TODO this is extremely slow taskStarted = this.startNextWorldGenTask(this.generationTargetPos); if (!taskStarted) { int debugPointOne = 0; } } - - -// LOGGER.info("after task count: " + this.numberOfTasksQueued); // if there aren't any new tasks, wait a second before checking again // TODO replace with a listener instead Thread.sleep(1000); @@ -257,28 +249,6 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender }); } -// /** Removes all {@link WorldGenTask}'s and {@link WorldGenTaskGroup}'s that have been garbage collected. */ -// private void removeGarbageCollectedTasks() // TODO remove, potential mystery errors caused by garbage collection isn't worth it (and may not be necessary any more now that we are using a quad tree to hold the tasks). // also this is very slow with the curent quad tree impelmentation -// { -// for (byte detailLevel = QuadTree.TREE_LOWEST_DETAIL_LEVEL; detailLevel < this.waitingTaskQuadTree.treeMaxDetailLevel; detailLevel++) -// { -// MovableGridRingList gridRingList = this.waitingTaskQuadTree.getRingList(detailLevel); -// Iterator taskIterator = gridRingList.iterator(); -// while (taskIterator.hasNext()) -// { -// // go through each WorldGenTask in the TaskGroup -// WorldGenTask genTask = taskIterator.next(); -// if (genTask != null && !genTask.taskTracker.isMemoryAddressValid()) -// { -// taskIterator.remove(); -// genTask.future.complete(WorldGenResult.CreateFail()); -// } -// } -// } -// } - - private final Set CheckingTasks = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private static class Mapper { public final WorldGenTask task; @@ -297,63 +267,30 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender */ private boolean startNextWorldGenTask(DhBlockPos2D targetPos) { - long closestGenDist = Long.MAX_VALUE; - - WorldGenTask closestTask = null; - //CheckingTasks.clear(); - -/* // TODO improve, having to go over every node isn't super efficient, removing null nodes from the tree would help - Iterator> nodeIterator = this.waitingTaskQuadTree.nodeIterator(); - while (nodeIterator.hasNext()) - { - QuadNode taskNode = nodeIterator.next(); - WorldGenTask newGenTask = taskNode.value; - DhSectionPos taskSectionPos = taskNode.sectionPos; - - if (newGenTask != null) // TODO add an option to skip leaves with null values and potentially auto-prune them - { - CheckingTasks.add(newGenTask); - if (!newGenTask.StillValid()) - { - // skip and remove out-of-bound tasks or tasks that are no longer valid - taskNode.value = null; - continue; - } - - - // use chebyShev distance in order to generate in rings around the target pos (also because it is a fast distance calculation) - int chebDistToTargetPos = newGenTask.pos.getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D()); - if (chebDistToTargetPos < closestGenDist) - { - // this task is closer than the last one - closestTask = newGenTask; - closestGenDist = chebDistToTargetPos; - } - } - }*/ - - waitingTasks.forEach((pos, task) -> { - if (!task.StillValid()) - { - waitingTasks.remove(pos); - task.future.complete(WorldGenResult.CreateFail()); - } - }); - - if (waitingTasks.size() == 0) + if (this.waitingTasks.size() == 0) { return false; } - Mapper closestTaskMap = waitingTasks.reduceEntries(1024, - v -> new Mapper(v.getValue(), v.getValue().pos.getSectionBBoxPos().getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), - (a, b) -> a.dist < b.dist ? a : b); + this.waitingTasks.forEach((pos, task) -> + { + if (!task.StillValid()) + { + this.waitingTasks.remove(pos); + task.future.complete(WorldGenResult.CreateFail()); + } + }); - closestTask = closestTaskMap.task; + + + Mapper closestTaskMap = this.waitingTasks.reduceEntries(1024, + entry -> new Mapper(entry.getValue(), entry.getValue().pos.getSectionBBoxPos().getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), + (aMapper, bMapper) -> aMapper.dist < bMapper.dist ? aMapper : bMapper); + + WorldGenTask closestTask = closestTaskMap.task; // remove the task we found, we are going to start it and don't want to run it multiple times - //WorldGenTask removedWorldGenTask = this.waitingTaskQuadTree.setValue(new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z), null); - waitingTasks.remove(closestTask.pos, closestTask); + this.waitingTasks.remove(closestTask.pos, closestTask); // do we need to modify this task to generate it? if (this.canGeneratePos((byte) 0, closestTask.pos)) // TODO should detail level 0 be replaced? @@ -399,13 +336,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender childFutures.add(newFuture); WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, childDhSectionPos.getDetailLevel(), finalClosestTask.taskTracker, newFuture); - waitingTasks.put(newGenTask.pos, newGenTask); - //this.waitingTaskQuadTree.setValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask); - - //boolean valueAdded = this.waitingTaskQuadTree.getValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ)) != null; - //LodUtil.assertTrue(valueAdded); // failed to add world gen task to quad tree, this means the quad tree was the wrong size - -// LOGGER.info("split feature "+sectionPos+" into "+childDhSectionPos+" "+(valueAdded ? "added" : "notAdded")); + this.waitingTasks.put(newGenTask.pos, newGenTask); }); // send the child futures to the future recipient, to notify them of the new tasks From 04e8a99320a1d4018480b59c2dd132ff0a0af766 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 08:44:01 -0500 Subject: [PATCH 03/16] Improve World Gen tick method names and put queue work on queue thread --- .../generation/IWorldGenerationQueue.java | 3 +- .../core/generation/WorldGenerationQueue.java | 38 +++++++------------ .../core/level/WorldGenModule.java | 5 +-- 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java index 127669592..352082edd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.core.generation; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import java.io.Closeable; @@ -37,7 +36,7 @@ public interface IWorldGenerationQueue extends Closeable void cancelGenTasks(Iterable positions); /** @param targetPos the position that world generation should be centered around, generally this will be the player's position. */ - void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos); + void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPos); int getWaitingTaskCount(); int getInProgressTaskCount(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index a52d8d43f..6e67176b4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -82,7 +82,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller one // 2. batch requests better. instead of sending 4 individual tasks of detail level N, send 1 task of detail level n+1 private final ExecutorService queueingThread = ThreadUtil.makeSingleThreadPool("World Gen Queue"); - private boolean generationQueueStarted = false; + private boolean generationQueueRunning = false; private DhBlockPos2D generationTargetPos = DhBlockPos2D.ZERO; /** can be used for debugging how many tasks are currently in the queue */ private int numberOfTasksQueued = 0; @@ -179,39 +179,26 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // TODO Should we cancel generation of chunks that were loaded by the player? } + + //===============// // running tasks // //===============// - public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) + public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPos) { - generator.preGeneratorTaskStart(); - try + // update the target pos + this.generationTargetPos = targetPos; + + // ensure the queuing thread is running + if (!this.generationQueueRunning) { - // the generator is shutting down, don't attempt to generate anything - if (this.generatorClosingFuture != null) - { - return; - } - - - // update the target pos - this.generationTargetPos = targetPos; - - // only start the queuing thread once - if (!this.generationQueueStarted) - { - this.startWorldGenQueuingThread(); - } - } - catch (Exception e) - { - LOGGER.error(e.getMessage(), e); + this.startWorldGenQueuingThread(); } } private void startWorldGenQueuingThread() { - this.generationQueueStarted = true; + this.generationQueueRunning = true; // queue world generation tasks on its own thread since this process is very slow and would lag the server thread this.queueingThread.execute(() -> @@ -221,6 +208,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // loop until the generator is shutdown while (!Thread.interrupted()) { + this.generator.preGeneratorTaskStart(); // queue generation tasks until the generator is full, or there are no more tasks to generate boolean taskStarted = true; @@ -244,7 +232,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender catch (Exception e) { LOGGER.error("queueing exception: " + e.getMessage(), e); - this.generationQueueStarted = false; + this.generationQueueRunning = false; } }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java index 912f8bd6c..2a6e8ac71 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -111,8 +111,7 @@ public class WorldGenModule implements Closeable AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); if (worldGenState != null) { - // queue new world generation requests - worldGenState.tick(targetPosForGeneration); + worldGenState.startGenerationQueueAndSetTargetPos(targetPosForGeneration); } } @@ -178,7 +177,7 @@ public class WorldGenModule implements Closeable } /** @param targetPosForGeneration the position that world generation should be centered around */ - public void tick(DhBlockPos2D targetPosForGeneration) { this.worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); } + public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPosForGeneration) { this.worldGenerationQueue.startGenerationQueueAndSetTargetPos(targetPosForGeneration); } } } From 1aa45bd9d6f79563d7c1fd415a2e290722745983 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 08:44:16 -0500 Subject: [PATCH 04/16] DhClientServerLevel reformat --- .../core/level/DhClientServerLevel.java | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 2043dd006..84df723ca 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -85,36 +85,34 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS } @Override - public void serverTick() - { - chunkToLodBuilder.tick(); - } + public void serverTick() { this.chunkToLodBuilder.tick(); } @Override public void doWorldGen() { - serverside.worldGeneratorEnabledConfig.pollNewValue(); - boolean shouldDoWorldGen = serverside.worldGeneratorEnabledConfig.get() && clientside.isRendering(); - boolean isWorldGenRunning = serverside.worldGenModule.isWorldGenRunning(); + this.serverside.worldGeneratorEnabledConfig.pollNewValue(); + boolean shouldDoWorldGen = this.serverside.worldGeneratorEnabledConfig.get() && this.clientside.isRendering(); + boolean isWorldGenRunning = this.serverside.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - serverside.worldGenModule.startWorldGen(serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); + this.serverside.worldGenModule.startWorldGen(this.serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); + this.serverside.worldGenModule.stopWorldGen(this.serverside.dataFileHandler); } - if (serverside.worldGenModule.isWorldGenRunning()) + if (this.serverside.worldGenModule.isWorldGenRunning()) { - ClientLevelModule.ClientRenderState renderState = clientside.ClientRenderStateRef.get(); + ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); if (renderState != null && renderState.quadtree != null) { - serverside.dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); + this.serverside.dataFileHandler.removeGenRequestIf(pos -> !renderState.quadtree.isSectionPosInBounds(pos)); } - serverside.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + + this.serverside.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); } } @@ -195,9 +193,9 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS @Override public void close() { - clientside.close(); + this.clientside.close(); super.close(); - serverside.close(); + this.serverside.close(); LOGGER.info("Closed " + this.getClass().getSimpleName() + " for " + this.getServerLevelWrapper()); } From 9d5bfc4f16a259b3fc3dee7ba7b7968e8ebca42e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 11:21:29 -0500 Subject: [PATCH 05/16] Remove unimplemented IDhLevel.dumpRamUsage() --- .../seibel/distanthorizons/core/api/internal/ClientApi.java | 5 ----- .../seibel/distanthorizons/core/level/DhClientLevel.java | 6 ------ .../distanthorizons/core/level/DhClientServerLevel.java | 6 ------ .../seibel/distanthorizons/core/level/DhServerLevel.java | 6 ------ .../com/seibel/distanthorizons/core/level/IDhLevel.java | 2 -- 5 files changed, 25 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index d397fcc3d..a6acbea4b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -504,11 +504,6 @@ public class ClientApi IDhClientWorld dhClientWorld = SharedApi.getIDhClientWorld(); IDhClientLevel level = dhClientWorld.getOrLoadClientLevel(levelWrapper); - if (prefLoggerEnabled) - { - level.dumpRamUsage(); - } - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index b48735765..1ea7d5504 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -120,12 +120,6 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel // misc helper functions // //=======================// - @Override - public void dumpRamUsage() - { - //TODO - } - @Override public IFullDataSourceProvider getFileHandler() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 84df723ca..8a845239f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -212,10 +212,4 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS this.clientside.reloadPos(pos); } - @Override - public void dumpRamUsage() - { - - } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index ef3370411..7b5b2489c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -66,12 +66,6 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel @Override public int getMinY() { return getLevelWrapper().getMinHeight(); } - @Override - public void dumpRamUsage() - { - //TODO - } - @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 5320db0e7..5881f962d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -31,8 +31,6 @@ public interface IDhLevel extends AutoCloseable int getMinY(); CompletableFuture saveAsync(); - void dumpRamUsage(); - /** * May return either a client or server level wrapper.
* Should not return null From 03efa3c1918ed7fdf8a125c19a0b40d29a5cfb86 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 11:25:12 -0500 Subject: [PATCH 06/16] GLProxy reformat --- .../core/render/glObject/GLProxy.java | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index 2403a71a3..46f88c953 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -61,7 +61,6 @@ import java.util.concurrent.TimeUnit; * https://stackoverflow.com/questions/63509735/massive-performance-loss-with-glmapbuffer

* * @author James Seibel - * @version 2022-10-1 */ public class GLProxy { @@ -102,6 +101,10 @@ public class GLProxy + //=============// + // constructor // + //=============// + private GLProxy() throws IllegalStateException { // this must be created on minecraft's render context to work correctly @@ -123,13 +126,13 @@ public class GLProxy //============================// // get Minecraft's context - minecraftGlContext = GLFW.glfwGetCurrentContext(); - minecraftGlCapabilities = GL.getCapabilities(); + this.minecraftGlContext = GLFW.glfwGetCurrentContext(); + this.minecraftGlCapabilities = GL.getCapabilities(); // crash the game if the GPU doesn't support OpenGL 3.2 - if (!minecraftGlCapabilities.OpenGL32) + if (!this.minecraftGlCapabilities.OpenGL32) { - String supportedVersionInfo = getFailedVersionInfo(minecraftGlCapabilities); + String supportedVersionInfo = this.getFailedVersionInfo(this.minecraftGlCapabilities); // See full requirement at above. String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName() @@ -137,11 +140,11 @@ public class GLProxy "Additional info:\n" + supportedVersionInfo; MC.crashMinecraft(errorMessage, new UnsupportedOperationException("Distant Horizon OpenGL requirements not met")); } - GL_LOGGER.info("minecraftGlCapabilities:\n" + getVersionInfo(minecraftGlCapabilities)); + GL_LOGGER.info("minecraftGlCapabilities:\n" + this.getVersionInfo(this.minecraftGlCapabilities)); if (Config.Client.Advanced.Debugging.overrideVanillaGLLogger.get()) { - GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, vanillaDebugMessageBuilder), true)); + GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, this.vanillaDebugMessageBuilder), true)); } @@ -183,20 +186,20 @@ public class GLProxy } // create the Lod Builder context - lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext); - if (lodBuilderGlContext == 0) + this.lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, this.minecraftGlContext); + if (this.lodBuilderGlContext == 0) { GL_LOGGER.error("ERROR: Failed to create GLFW context for OpenGL 3.2 with" + " Forward Compat Core Profile! Your OS may have not been able to support it!"); throw new UnsupportedOperationException("Forward Compat Core Profile 3.2 creation failure"); } // create the window - GLFW.glfwMakeContextCurrent(lodBuilderGlContext); + GLFW.glfwMakeContextCurrent(this.lodBuilderGlContext); // set and log the capabilities - lodBuilderGlCapabilities = GL.createCapabilities(); - GL_LOGGER.info("lodBuilderGlCapabilities:\n" + getVersionInfo(lodBuilderGlCapabilities)); + this.lodBuilderGlCapabilities = GL.createCapabilities(); + GL_LOGGER.info("lodBuilderGlCapabilities:\n" + this.getVersionInfo(this.lodBuilderGlCapabilities)); // override the GL logger - GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, lodBuilderDebugMessageBuilder), true)); + GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, this.lodBuilderDebugMessageBuilder), true)); // clear the context for the next stage GLFW.glfwMakeContextCurrent(0L); @@ -207,20 +210,20 @@ public class GLProxy //=================================// // create the proxyWorker's context - proxyWorkerGlContext = GLFW.glfwCreateWindow(64, 48, "LOD proxy worker Window", 0L, minecraftGlContext); - if (proxyWorkerGlContext == 0) + this.proxyWorkerGlContext = GLFW.glfwCreateWindow(64, 48, "LOD proxy worker Window", 0L, this.minecraftGlContext); + if (this.proxyWorkerGlContext == 0) { GL_LOGGER.error("ERROR: Failed to create GLFW context for OpenGL 3.2 with" + " Forward Compat Core Profile! Your OS may have not been able to support it!"); throw new UnsupportedOperationException("Forward Compat Core Profile 3.2 creation failure"); } // create the window - GLFW.glfwMakeContextCurrent(proxyWorkerGlContext); + GLFW.glfwMakeContextCurrent(this.proxyWorkerGlContext); // set and log the capabilities - proxyWorkerGlCapabilities = GL.createCapabilities(); - GL_LOGGER.info("proxyWorkerGlCapabilities:\n" + getVersionInfo(lodBuilderGlCapabilities)); + this.proxyWorkerGlCapabilities = GL.createCapabilities(); + GL_LOGGER.info("proxyWorkerGlCapabilities:\n" + this.getVersionInfo(this.lodBuilderGlCapabilities)); // override the GL logger - GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, proxyWorkerDebugMessageBuilder), true)); + GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, this.proxyWorkerDebugMessageBuilder), true)); // clear the context for the next stage GLFW.glfwMakeContextCurrent(0L); @@ -231,19 +234,19 @@ public class GLProxy //======================// // get capabilities from a context we use - setGlContext(EGLProxyContext.LOD_BUILDER); + this.setGlContext(EGLProxyContext.LOD_BUILDER); // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after - VertexAttributeBufferBindingSupported = minecraftGlCapabilities.glBindVertexBuffer != 0L; // Nullptr + this.VertexAttributeBufferBindingSupported = this.minecraftGlCapabilities.glBindVertexBuffer != 0L; // Nullptr // UNUSED currently // Check if we can use the named version of all calls, which is available in GL4.5 or after - namedObjectSupported = minecraftGlCapabilities.glNamedBufferData != 0L; //Nullptr + this.namedObjectSupported = this.minecraftGlCapabilities.glNamedBufferData != 0L; //Nullptr // get specific capabilities // Check if we can use the Buffer Storage, which is available in GL4.4 or after - bufferStorageSupported = minecraftGlCapabilities.glBufferStorage != 0L && lodBuilderGlCapabilities.glBufferStorage != 0L; // Nullptr - if (!bufferStorageSupported) + this.bufferStorageSupported = this.minecraftGlCapabilities.glBufferStorage != 0L && this.lodBuilderGlCapabilities.glBufferStorage != 0L; // Nullptr + if (!this.bufferStorageSupported) { GL_LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods."); } @@ -253,14 +256,14 @@ public class GLProxy if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE")) { // NVIDIA card - preferredUploadMethod = bufferStorageSupported ? EGpuUploadMethod.BUFFER_STORAGE : EGpuUploadMethod.SUB_DATA; + this.preferredUploadMethod = this.bufferStorageSupported ? EGpuUploadMethod.BUFFER_STORAGE : EGpuUploadMethod.SUB_DATA; } else { // AMD or Intel card - preferredUploadMethod = bufferStorageSupported ? EGpuUploadMethod.BUFFER_STORAGE : EGpuUploadMethod.DATA; + this.preferredUploadMethod = this.bufferStorageSupported ? EGpuUploadMethod.BUFFER_STORAGE : EGpuUploadMethod.DATA; } - GL_LOGGER.info("GPU Vendor [" + vendor + "], Preferred upload method is [" + preferredUploadMethod + "]."); + GL_LOGGER.info("GPU Vendor [" + vendor + "], Preferred upload method is [" + this.preferredUploadMethod + "]."); @@ -269,7 +272,7 @@ public class GLProxy //==========// // Since this is created on the render thread, make sure the Minecraft context is used in the end - setGlContext(EGLProxyContext.MINECRAFT); + this.setGlContext(EGLProxyContext.MINECRAFT); // GLProxy creation success GL_LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); @@ -281,7 +284,7 @@ public class GLProxy */ public void setGlContext(EGLProxyContext newContext) { - EGLProxyContext currentContext = getGlContext(); + EGLProxyContext currentContext = this.getGlContext(); // we don't have to change the context, we are already there. if (currentContext == newContext) @@ -294,18 +297,18 @@ public class GLProxy switch (newContext) { case LOD_BUILDER: - contextPointer = lodBuilderGlContext; - newGlCapabilities = lodBuilderGlCapabilities; + contextPointer = this.lodBuilderGlContext; + newGlCapabilities = this.lodBuilderGlCapabilities; break; case MINECRAFT: - contextPointer = minecraftGlContext; - newGlCapabilities = minecraftGlCapabilities; + contextPointer = this.minecraftGlContext; + newGlCapabilities = this.minecraftGlCapabilities; break; case PROXY_WORKER: - contextPointer = proxyWorkerGlContext; - newGlCapabilities = proxyWorkerGlCapabilities; + contextPointer = this.proxyWorkerGlContext; + newGlCapabilities = this.proxyWorkerGlCapabilities; break; default: // default should never happen, it is just here to make the compiler happy @@ -325,15 +328,15 @@ public class GLProxy long currentContext = GLFW.glfwGetCurrentContext(); - if (currentContext == lodBuilderGlContext) + if (currentContext == this.lodBuilderGlContext) { return EGLProxyContext.LOD_BUILDER; } - else if (currentContext == minecraftGlContext) + else if (currentContext == this.minecraftGlContext) { return EGLProxyContext.MINECRAFT; } - else if (currentContext == proxyWorkerGlContext) + else if (currentContext == this.proxyWorkerGlContext) { return EGLProxyContext.PROXY_WORKER; } @@ -346,9 +349,9 @@ public class GLProxy // hopefully this shouldn't happen throw new IllegalStateException(Thread.currentThread().getName() + " has a unknown OpenGl context: [" + currentContext + "]. " - + "Minecraft context [" + minecraftGlContext + "], " - + "LodBuilder context [" + lodBuilderGlContext + "], " - + "ProxyWorker context [" + proxyWorkerGlContext + "], " + + "Minecraft context [" + this.minecraftGlContext + "], " + + "LodBuilder context [" + this.lodBuilderGlContext + "], " + + "ProxyWorker context [" + this.proxyWorkerGlContext + "], " + "no context [0]."); } } @@ -368,13 +371,13 @@ public class GLProxy public EGpuUploadMethod getGpuUploadMethod() { EGpuUploadMethod method = Config.Client.Advanced.GpuBuffers.gpuUploadMethod.get(); - if (!bufferStorageSupported && method == EGpuUploadMethod.BUFFER_STORAGE) + if (!this.bufferStorageSupported && method == EGpuUploadMethod.BUFFER_STORAGE) { // if buffer storage isn't supported // default to DATA since that is the most compatible method = EGpuUploadMethod.DATA; } - return method == EGpuUploadMethod.AUTO ? preferredUploadMethod : method; + return method == EGpuUploadMethod.AUTO ? this.preferredUploadMethod : method; } /** @@ -386,14 +389,14 @@ public class GLProxy public void recordOpenGlCall(Runnable renderCall) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - workerThread.execute(() -> runnableContainer(renderCall, stackTrace)); + this.workerThread.execute(() -> this.runnableContainer(renderCall, stackTrace)); } private void runnableContainer(Runnable renderCall, StackTraceElement[] stackTrace) { try { // set up the context... - setGlContext(EGLProxyContext.PROXY_WORKER); + this.setGlContext(EGLProxyContext.PROXY_WORKER); // ...run the actual code... renderCall.run(); } @@ -406,7 +409,7 @@ public class GLProxy finally { // ...and make sure the context is released when the thread finishes - setGlContext(EGLProxyContext.NONE); + this.setGlContext(EGLProxyContext.NONE); } } @@ -460,9 +463,9 @@ public class GLProxy return false; } - namedObjectSupported = c.glNamedBufferStorage != 0; - bufferStorageSupported = c.glBufferStorage != 0; - VertexAttributeBufferBindingSupported = c.glVertexAttribBinding != 0; + this.namedObjectSupported = c.glNamedBufferStorage != 0; + this.bufferStorageSupported = c.glBufferStorage != 0; + this.VertexAttributeBufferBindingSupported = c.glVertexAttribBinding != 0; return true; } From 05a16e00ad849699ff4103b9ee11f783dd524691 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 11:51:42 -0500 Subject: [PATCH 07/16] Remove ReflectionHandler since it was only used by AbstractOptifineAccessor --- .../core/IReflectionHandler.java | 46 ---------- .../core/ReflectionHandler.java | 88 ------------------- .../modAccessor/AbstractOptifineAccessor.java | 9 +- 3 files changed, 2 insertions(+), 141 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/IReflectionHandler.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/ReflectionHandler.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/IReflectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/IReflectionHandler.java deleted file mode 100644 index 0f2bb9127..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/IReflectionHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core; - -import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; - -/** - * A singleton used to get variables from methods - * where they are private or potentially absent. - * Specifically the fog setting used by Optifine or the - * presence/absence of other mods. - *

- * This interface doesn't necessarily have to exist, but - * it makes using the singleton handler more uniform (always - * passing in interfaces), and it may be needed in the future if - * we find that reflection handlers need to be different for - * different MC versions. - * - * @author James Seibel - * @version 2022-11-24 - */ -public interface IReflectionHandler extends IBindable -{ - /** @return if Sodium (or a sodium like) mod is present. */ - boolean sodiumPresent(); - - boolean optifinePresent(); - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/ReflectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/ReflectionHandler.java deleted file mode 100644 index 317ba14f5..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/ReflectionHandler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core; - -import java.lang.invoke.MethodHandles; - -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; -import org.apache.logging.log4j.Logger; - -/** - * A singleton used to determine if a class is present or - * access variables from methods where they are private - * or potentially absent.

- * - * For example: presence/absence of Optifine. - * - * @author James Seibel - * @version 2022-11-24 - */ -public class ReflectionHandler implements IReflectionHandler -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); - - public static final ReflectionHandler INSTANCE = new ReflectionHandler(); - - // populated when the methods are called the first time - private Boolean sodiumPresent = null; - private Boolean optifinePresent = false; - - - - private ReflectionHandler() { } - - - - //===================// - // is [mod] present? // - //===================// - - @Override - public boolean optifinePresent() - { - if (this.optifinePresent == null) - { - // call the base accessor so we don't have duplicate code - this.optifinePresent = AbstractOptifineAccessor.isOptifinePresent(); - } - return this.optifinePresent; - } - - @Override - public boolean sodiumPresent() - { - if (this.sodiumPresent == null) - { - try - { - Class.forName("me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer"); - this.sodiumPresent = true; - } - catch (ClassNotFoundException e) - { - this.sodiumPresent = false; - } - } - - return this.sodiumPresent; - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/modAccessor/AbstractOptifineAccessor.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/modAccessor/AbstractOptifineAccessor.java index d4427cc16..e65002706 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/modAccessor/AbstractOptifineAccessor.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/modAccessor/AbstractOptifineAccessor.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor; import com.seibel.distanthorizons.api.enums.rendering.EFogDrawMode; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.ReflectionHandler; import java.lang.reflect.Field; @@ -66,11 +65,7 @@ public abstract class AbstractOptifineAccessor implements IOptifineAccessor return null; } - /** - * Should not be called frequently since this uses reflection calls to determine if Optifine is present.
- * Use {@link ReflectionHandler#optifinePresent()} instead. - */ - public static boolean isOptifinePresent() { return getOptifineFogField() != null; } + public static boolean optifinePresent() { return getOptifineFogField() != null; } @@ -115,7 +110,7 @@ public abstract class AbstractOptifineAccessor implements IOptifineAccessor } } - + @Override public double getRenderResolutionMultiplier() { /* From a61317aa57e5b2e70866491b8c9b33c08a5fe95a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 11:53:44 -0500 Subject: [PATCH 08/16] Remove unhelpful GLProxy test configs --- .../api/enums/config/EGlProfileMode.java | 30 ------------------- .../distanthorizons/core/config/Config.java | 10 ------- .../core/render/glObject/GLProxy.java | 22 ++------------ 3 files changed, 3 insertions(+), 59 deletions(-) delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java deleted file mode 100644 index acf2f8181..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.enums.config; - -/** - * @since API 1.0.0 - */ -public enum EGlProfileMode -{ - CORE, - COMPAT, - ANY; -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 13fbae605..57cd4b8a2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1173,16 +1173,6 @@ public class Config .comment("") .build(); - // TODO temporary test, remove me - public static ConfigEntry glProfileMode = new ConfigEntry.Builder() - .set(EGlProfileMode.CORE) - .comment("") - .build(); - // TODO temporary test, remove me - public static ConfigEntry glForwardCompatibilityMode = new ConfigEntry.Builder() - .set(true) - .comment("") - .build(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index 46f88c953..b4b674587 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -21,7 +21,6 @@ package com.seibel.distanthorizons.core.render.glObject; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.seibel.distanthorizons.api.enums.config.EGLErrorHandlingMode; -import com.seibel.distanthorizons.api.enums.config.EGlProfileMode; import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -165,25 +164,10 @@ public class GLProxy GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 2); GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); - // TODO remove me - boolean useForwardCompatibility = Config.Client.Advanced.Debugging.glForwardCompatibilityMode.get(); - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, useForwardCompatibility ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); - - // TODO remove me - EGlProfileMode profileMode = Config.Client.Advanced.Debugging.glProfileMode.get(); - if (profileMode == EGlProfileMode.CORE) - { - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); - } - else if (profileMode == EGlProfileMode.ANY) - { - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_ANY_PROFILE); - } - else - { - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_COMPAT_PROFILE); - } + LOGGER.info(this.minecraftGlCapabilities.toString()); // create the Lod Builder context this.lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, this.minecraftGlContext); From 1fe209d72a6b69ea8108b8203f7e876ce52ba703 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 12:13:41 -0500 Subject: [PATCH 09/16] Add some additional logging to GLProxy around GLFW context creation --- .../core/render/glObject/GLProxy.java | 13 +++-- .../core/util/ReflectionUtil.java | 57 +++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/ReflectionUtil.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index b4b674587..703437c8e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.enums.EGLProxyContext; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.ReflectionUtil; import com.seibel.distanthorizons.core.util.objects.GLMessage; import com.seibel.distanthorizons.core.util.objects.GLMessageOutputStream; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -167,14 +168,13 @@ public class GLProxy GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE); GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); - LOGGER.info(this.minecraftGlCapabilities.toString()); - // create the Lod Builder context this.lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, this.minecraftGlContext); if (this.lodBuilderGlContext == 0) { - GL_LOGGER.error("ERROR: Failed to create GLFW context for OpenGL 3.2 with" - + " Forward Compat Core Profile! Your OS may have not been able to support it!"); + GL_LOGGER.error("ERROR: Failed to create LodBuilder GLFW context for OpenGL 3.2 with Forward compatible Core Profile! Your OS may have not been able to support it."); + GL_LOGGER.error("Minecraft GL Capabilities:\n [\n"+ReflectionUtil.getAllFieldValuesAsString(this.minecraftGlCapabilities)+"\n]\n"); + throw new UnsupportedOperationException("Forward Compat Core Profile 3.2 creation failure"); } // create the window @@ -197,8 +197,9 @@ public class GLProxy this.proxyWorkerGlContext = GLFW.glfwCreateWindow(64, 48, "LOD proxy worker Window", 0L, this.minecraftGlContext); if (this.proxyWorkerGlContext == 0) { - GL_LOGGER.error("ERROR: Failed to create GLFW context for OpenGL 3.2 with" - + " Forward Compat Core Profile! Your OS may have not been able to support it!"); + GL_LOGGER.error("ERROR: Failed to create GLProxy Worker GLFW context for OpenGL 3.2 with Forward compatible Core Profile! Your OS may have not been able to support it."); + GL_LOGGER.error("Minecraft GL Capabilities:\n [\n"+ReflectionUtil.getAllFieldValuesAsString(this.minecraftGlCapabilities)+"\n]\n"); + throw new UnsupportedOperationException("Forward Compat Core Profile 3.2 creation failure"); } // create the window diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ReflectionUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ReflectionUtil.java new file mode 100644 index 000000000..ed2acec93 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ReflectionUtil.java @@ -0,0 +1,57 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util; + +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; +import org.apache.logging.log4j.Logger; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; + +public class ReflectionUtil +{ + + public static String getAllFieldValuesAsString(Object obj) + { + StringBuilder stringBuilder = new StringBuilder(); + + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) + { + String fieldName = field.getName();; + String fieldStringValue; + try + { + field.setAccessible(true); + fieldStringValue = field.get(obj) + ""; + } + catch (Exception e) + { + fieldStringValue = "ERROR:[" + e.getMessage() + "]"; + } + + stringBuilder.append(fieldName+" - "+fieldStringValue+"\n"); + } + + return stringBuilder.toString(); + } + +} From 19cde1bbd4faf067a6690a15e48ae48f2ac0a28e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 16:05:56 -0500 Subject: [PATCH 10/16] Add a config for synchronous GPU uploading hopefully to help with Sodium/AMD issues --- .../distanthorizons/core/config/Config.java | 12 +++- .../bufferBuilding/ColumnRenderBuffer.java | 61 ++++++++++++++++--- .../ColumnRenderBufferBuilder.java | 12 +--- .../core/render/glObject/buffer/GLBuffer.java | 2 +- .../glObject/buffer/GLVertexBuffer.java | 14 +++-- .../assets/distanthorizons/lang/en_us.json | 4 ++ 6 files changed, 81 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 57cd4b8a2..99de6a13f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -965,7 +965,7 @@ public class Config + "How long should a buffer wait per Megabyte of data uploaded? \n" + "Helpful resource for frame times: https://fpstoms.com \n" + "\n" - + "Longer times may reduce stuttering but will make fake chunks \n" + + "Longer times may reduce stuttering but will make LODs \n" + "transition and load slower. Change this to [0] for no timeout. \n" + "\n" + "NOTE:\n" @@ -973,6 +973,16 @@ public class Config + "") .build(); + public static ConfigEntry gpuUploadAsync = new ConfigEntry.Builder() + .set(true) + .comment("" + + "If true geometry data will be uploaded on a DH controlled thread, reducing FPS stuttering. \n" + + "If false uploading will be done on Minecraft's main rendering thread. \n" + + "\n" + + "Setting this to false may reduce crashes or corrupted geometry on systems with an AMD GPU when Sodium is installed.\n" + + "") + .build(); + // deprecated and not implemented, can be made public if we ever re-implement it @Deprecated private static ConfigEntry rebuildTimes = new ConfigEntry.Builder() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java index 873745cef..2d3404ca4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -20,22 +20,22 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.enums.EGLProxyContext; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.AbstractRenderBuffer; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; -import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.StatsMap; import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod; import com.seibel.distanthorizons.core.util.*; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.Logger; -import java.awt.*; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.concurrent.*; @@ -48,6 +48,7 @@ import java.util.concurrent.*; public class ColumnRenderBuffer extends AbstractRenderBuffer { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftClientWrapper minecraftClient = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; @@ -83,20 +84,66 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer // buffer uploading // //==================// - public void uploadBuffer(LodQuadBuilder builder, EGpuUploadMethod method) throws InterruptedException + /** Should be run on a DH thread. */ + public void uploadBuffer(LodQuadBuilder builder, EGpuUploadMethod gpuUploadMethod) throws InterruptedException { - if (method.useEarlyMapping) + LodUtil.assertTrue(Thread.currentThread().getName().startsWith(ThreadUtil.THREAD_NAME_PREFIX), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads."); + + + // the async is relative to MC's render thread + boolean uploadAsync = Config.Client.Advanced.GpuBuffers.gpuUploadAsync.get(); + if (uploadAsync) { - this.uploadBuffersMapped(builder, method); + // upload here on a DH thread + GLProxy glProxy = GLProxy.getInstance(); + EGLProxyContext oldContext = glProxy.getGlContext(); + glProxy.setGlContext(EGLProxyContext.LOD_BUILDER); + try + { + this.uploadBuffersUsingUploadMethod(builder, gpuUploadMethod); + } + finally + { + glProxy.setGlContext(oldContext); + } } else { - this.uploadBuffersDirect(builder, method); + // upload on MC's render thread + CompletableFuture uploadFuture = new CompletableFuture<>(); + minecraftClient.executeOnRenderThread(() -> + { + try + { + this.uploadBuffersUsingUploadMethod(builder, gpuUploadMethod); + uploadFuture.complete(null); + } + catch (InterruptedException e) + { + throw new CompletionException(e); + } + }); + + // freeze this DH thread while we wait for MC to upload the buffer + uploadFuture.join(); + } + } + private void uploadBuffersUsingUploadMethod(LodQuadBuilder builder, EGpuUploadMethod gpuUploadMethod) throws InterruptedException + { + if (gpuUploadMethod.useEarlyMapping) + { + this.uploadBuffersMapped(builder, gpuUploadMethod); + } + else + { + this.uploadBuffersDirect(builder, gpuUploadMethod); } this.buffersUploaded = true; } + + private void uploadBuffersMapped(LodQuadBuilder builder, EGpuUploadMethod method) { // opaque vbos // diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index a5b25e158..ed8481c3b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -117,12 +117,8 @@ public class ColumnRenderBufferBuilder try { EVENT_LOGGER.trace("RenderRegion start Upload @ " + renderSource.sectionPos); - GLProxy glProxy = GLProxy.getInstance(); - EGpuUploadMethod method = GLProxy.getInstance().getGpuUploadMethod(); - EGLProxyContext oldContext = glProxy.getGlContext(); - glProxy.setGlContext(EGLProxyContext.LOD_BUILDER); - ColumnRenderBuffer buffer = renderBufferRef.swap(null); + ColumnRenderBuffer buffer = renderBufferRef.swap(null); if (buffer == null) { buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.sectionPos.getMinCornerLodPos().getCornerBlockPos(), clientLevel.getMinY()), renderSource.sectionPos); @@ -130,7 +126,7 @@ public class ColumnRenderBufferBuilder try { - buffer.uploadBuffer(quadBuilder, method); + buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); LodUtil.assertTrue(buffer.buffersUploaded); EVENT_LOGGER.trace("RenderRegion end Upload @ " + renderSource.sectionPos); return buffer; @@ -140,10 +136,6 @@ public class ColumnRenderBufferBuilder buffer.close(); throw e; } - finally - { - glProxy.setGlContext(oldContext); - } } catch (InterruptedException e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java index 8ba6fd245..9b4291f4b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java @@ -145,7 +145,7 @@ public class GLBuffer implements AutoCloseable GL32.glBufferSubData(getBufferBindingTarget(), 0, bb); } - // Requires already binded + /** Assumes the GL Context is already bound */ public void uploadBuffer(ByteBuffer bb, EGpuUploadMethod uploadMethod, int maxExpansionSize, int bufferHint) { LodUtil.assertTrue(!uploadMethod.useEarlyMapping, "UploadMethod signal that this should use Mapping instead of uploadBuffer!"); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLVertexBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLVertexBuffer.java index adf087798..614c8cbba 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLVertexBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLVertexBuffer.java @@ -61,16 +61,20 @@ public class GLVertexBuffer extends GLBuffer return GL32.GL_ARRAY_BUFFER; } - public void uploadBuffer(ByteBuffer bb, int vertCount, EGpuUploadMethod uploadMethod, int maxExpensionSize) + public void uploadBuffer(ByteBuffer byteBuffer, int vertCount, EGpuUploadMethod uploadMethod, int maxExpensionSize) { - if (vertCount < 0) throw new IllegalArgumentException("VertCount is negative!"); + if (vertCount < 0) + { + throw new IllegalArgumentException("VertCount is negative!"); + } + // If size is zero, just ignore it. - if (bb.limit() - bb.position() != 0) + if (byteBuffer.limit() - byteBuffer.position() != 0) { boolean useBuffStorage = uploadMethod.useBufferStorage; - super.uploadBuffer(bb, uploadMethod, maxExpensionSize, useBuffStorage ? 0 : GL32.GL_STATIC_DRAW); + super.uploadBuffer(byteBuffer, uploadMethod, maxExpensionSize, useBuffStorage ? 0 : GL32.GL_STATIC_DRAW); } - vertexCount = vertCount; + this.vertexCount = vertCount; } public ByteBuffer mapBuffer(int targetSize, EGpuUploadMethod uploadMethod, int maxExpensionSize) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 341cd807c..0acd693d0 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -441,6 +441,10 @@ "GPU upload speed (milliseconds)", "distanthorizons.config.client.advanced.buffers.gpuUploadPerMegabyteInMilliseconds.@tooltip": "How long should a buffer wait per Megabyte of data uploaded?\nMay be increased if there is frame stuttering.", + "distanthorizons.config.client.advanced.buffers.gpuUploadAsync": + "GPU upload Async", + "distanthorizons.config.client.advanced.buffers.gpuUploadAsync.@tooltip": + "If true geometry data will be uploaded on a DH controlled thread, reducing FPS stuttering. \nIf false uploading will be done on Minecraft's main rendering thread. \n\nSetting this to false may reduce crashes or corrupted geometry on systems with an AMD GPU when Sodium is installed.", From cb0b610812745665b26d8ba67b829dcf33092d38 Mon Sep 17 00:00:00 2001 From: coolGi Date: Sun, 24 Sep 2023 11:09:08 +0930 Subject: [PATCH 11/16] Made config use our lang for everything --- .../assets/distanthorizons/lang/en_us.json | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 0acd693d0..01d67fcde 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -3,7 +3,15 @@ "Distant Horizons", - + + "distanthorizons.general.true": + "True", + "distanthorizons.general.false": + "False", + "distanthorizons.general.yes": + "Yes", + "distanthorizons.general.no": + "No", "distanthorizons.general.back": "Back", "distanthorizons.general.next": @@ -12,10 +20,8 @@ "Done", "distanthorizons.general.cancel": "Cancel", - "distanthorizons.general.yes": - "Yes", - "distanthorizons.general.no": - "No", + "distanthorizons.general.reset": + "Reset", From 7d7202950eefbc09183189d7cca34e9eec742fb8 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 20:40:43 -0500 Subject: [PATCH 12/16] Improve initial level loading time --- .../fullDatafile/FullDataFileHandler.java | 73 ++--- .../fullDatafile/IFullDataSourceProvider.java | 2 - .../renderfile/ILodRenderSourceProvider.java | 2 - .../renderfile/RenderSourceFileHandler.java | 106 ++------ .../core/util/MetaFileScanUtil.java | 257 ------------------ 5 files changed, 50 insertions(+), 390 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index ab63d40f4..e7ba34c06 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -34,7 +34,6 @@ import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.util.MetaFileScanUtil; import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; @@ -50,16 +49,13 @@ import java.util.function.Consumer; public class FullDataFileHandler implements IFullDataSourceProvider { - public static final boolean USE_LAZY_LOADING = true; - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected static ExecutorService fileHandlerThreadPool; protected static ConfigChangeListener configListener; - private final ConcurrentHashMap unloadedFileBySectionPos = new ConcurrentHashMap<>(); - /** contains the loaded {@link FullDataMetaFile}'s */ - private final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); + private final ConcurrentHashMap loadedMetaFileBySectionPos = new ConcurrentHashMap<>(); + private final Set missingSectionPos = Collections.newSetFromMap(new ConcurrentHashMap<>()); protected final IDhLevel level; protected final File saveDir; @@ -80,29 +76,6 @@ public class FullDataFileHandler implements IFullDataSourceProvider { LOGGER.warn("Unable to create full data folder, file saving may fail."); } - MetaFileScanUtil.scanFullDataFiles(saveStructure, level.getLevelWrapper(), this); - } - - @Override - public void addScannedFiles(Collection detectedFiles) - { - MetaFileScanUtil.ICreateMetadataFunc createMetadataFunc = (file) -> FullDataMetaFile.createFromExistingFile(this, this.level, file); - - MetaFileScanUtil.IAddUnloadedFileFunc addUnloadedFileFunc = (pos, file) -> - { - this.unloadedFileBySectionPos.put(pos, file); - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); - }; - MetaFileScanUtil.IAddLoadedMetaFileFunc addLoadedMetaFileFunc = (pos, loadedMetaFile) -> - { - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); - this.metaFileBySectionPos.put(pos, (FullDataMetaFile) loadedMetaFile); - }; - - - MetaFileScanUtil.addScannedFiles(detectedFiles, USE_LAZY_LOADING, FullDataMetaFile.FILE_SUFFIX, - createMetadataFunc, - addUnloadedFileFunc, addLoadedMetaFileFunc); } @@ -151,23 +124,25 @@ public class FullDataFileHandler implements IFullDataSourceProvider public FullDataMetaFile getFileIfExist(DhSectionPos pos) { return this.getLoadOrMakeFile(pos, false); } protected FullDataMetaFile getLoadOrMakeFile(DhSectionPos pos, boolean allowCreateFile) { - FullDataMetaFile metaFile = this.metaFileBySectionPos.get(pos); + FullDataMetaFile metaFile = this.loadedMetaFileBySectionPos.get(pos); if (metaFile != null) { return metaFile; } - File fileToLoad = this.unloadedFileBySectionPos.get(pos); // File does exist, but not loaded yet. - if (fileToLoad != null) + File fileToLoad = this.computeDataFilePath(pos); + if (fileToLoad.exists()) { synchronized (this) { + // A file exists, but isn't loaded yet. + // Double check locking for loading file, as loading file means also loading the metadata, which // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. - metaFile = this.metaFileBySectionPos.get(pos); + metaFile = this.loadedMetaFileBySectionPos.get(pos); if (metaFile != null) { return metaFile; // someone else loaded it already. @@ -177,17 +152,17 @@ public class FullDataFileHandler implements IFullDataSourceProvider { metaFile = FullDataMetaFile.createFromExistingFile(this, this.level, fileToLoad); this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); - this.metaFileBySectionPos.put(pos, metaFile); + this.loadedMetaFileBySectionPos.put(pos, metaFile); return metaFile; } catch (IOException e) { - LOGGER.error("Failed to read data meta file at " + fileToLoad + ": ", e); + LOGGER.error("Failed to read meta data file at " + fileToLoad + ": ", e); FileUtil.renameCorruptedFile(fileToLoad); } finally { - this.unloadedFileBySectionPos.remove(pos); + this.missingSectionPos.remove(pos); } } } @@ -195,6 +170,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider if (!allowCreateFile) { + this.missingSectionPos.add(pos); return null; } @@ -213,8 +189,9 @@ public class FullDataFileHandler implements IFullDataSourceProvider this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); - // This is a CAS with expected null value. - FullDataMetaFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); + // This is a Compare And Swap with expected null value. + FullDataMetaFile metaFileCas = this.loadedMetaFileBySectionPos.putIfAbsent(pos, metaFile); + this.missingSectionPos.remove(pos); return metaFileCas == null ? metaFile : metaFileCas; } @@ -254,8 +231,8 @@ public class FullDataFileHandler implements IFullDataSourceProvider continue; } - // check if a file for this pos exists, either loaded and unloaded - if (this.metaFileBySectionPos.containsKey(subPos) || this.unloadedFileBySectionPos.containsKey(subPos)) + // check if a file for this pos is loaded or exists + if (this.loadedMetaFileBySectionPos.containsKey(subPos) || this.computeDataFilePath(subPos).exists()) { allEmpty = false; break outerLoop; @@ -284,14 +261,14 @@ public class FullDataFileHandler implements IFullDataSourceProvider DhSectionPos childPos = pos.getChildByIndex(childIndex); if (CompleteFullDataSource.firstDataPosCanAffectSecond(basePos, childPos)) { - // load the file if it isn't already - if (this.unloadedFileBySectionPos.containsKey(childPos)) + // get or load the file if necessary + if (!this.loadedMetaFileBySectionPos.containsKey(childPos) && this.computeDataFilePath(childPos).exists()) { this.getLoadOrMakeFile(childPos, true); } - FullDataMetaFile metaFile = this.metaFileBySectionPos.get(childPos); + FullDataMetaFile metaFile = this.loadedMetaFileBySectionPos.get(childPos); if (metaFile != null) { // we have reached a populated leaf node in the quad tree @@ -310,7 +287,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider } } - public void ForEachFile(Consumer consumer) { this.metaFileBySectionPos.values().forEach(consumer); } + public void ForEachFile(Consumer consumer) { this.loadedMetaFileBySectionPos.values().forEach(consumer); } @@ -330,7 +307,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider } private void writeChunkDataToMetaFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData) { - FullDataMetaFile metaFile = this.metaFileBySectionPos.get(sectionPos); + FullDataMetaFile metaFile = this.loadedMetaFileBySectionPos.get(sectionPos); if (metaFile != null) { // there is a file for this position @@ -349,7 +326,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider public CompletableFuture flushAndSave() { ArrayList> futures = new ArrayList<>(); - for (FullDataMetaFile metaFile : this.metaFileBySectionPos.values()) + for (FullDataMetaFile metaFile : this.loadedMetaFileBySectionPos.values()) { futures.add(metaFile.flushAndSaveAsync()); } @@ -359,7 +336,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider @Override public CompletableFuture flushAndSave(DhSectionPos sectionPos) { - FullDataMetaFile metaFile = this.metaFileBySectionPos.get(sectionPos); + FullDataMetaFile metaFile = this.loadedMetaFileBySectionPos.get(sectionPos); if (metaFile == null) { return CompletableFuture.completedFuture(null); @@ -497,7 +474,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider FileUtil.renameCorruptedFile(metaFile.file); // remove the FullDataMetaFile since the old one was corrupted - this.metaFileBySectionPos.remove(pos); + this.loadedMetaFileBySectionPos.remove(pos); // create a new FullDataMetaFile to write new data to return this.getLoadOrMakeFile(pos, true); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java index b6b242cd8..679b6ed64 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java @@ -31,8 +31,6 @@ import java.util.concurrent.ExecutorService; public interface IFullDataSourceProvider extends AutoCloseable { - void addScannedFiles(Collection detectedFiles); - CompletableFuture readAsync(DhSectionPos pos); void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData); CompletableFuture flushAndSave(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java index db6e5fb3c..56cfae961 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java @@ -35,8 +35,6 @@ import java.util.concurrent.CompletableFuture; */ public interface ILodRenderSourceProvider extends AutoCloseable { - void addScannedFiles(Collection detectedFiles); - CompletableFuture readAsync(DhSectionPos pos); void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index 9dc6a8bdc..1ed0c2fcb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -27,10 +27,8 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.level.IDhClientLevel; -import com.seibel.distanthorizons.core.util.MetaFileScanUtil; import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.Logger; import java.io.File; @@ -41,16 +39,12 @@ import java.util.concurrent.atomic.AtomicInteger; public class RenderSourceFileHandler implements ILodRenderSourceProvider { - public static final boolean USE_LAZY_LOADING = true; - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private final ThreadPoolExecutor fileHandlerThreadPool; private final F3Screen.NestedMessage threadPoolMsg; - private final ConcurrentHashMap unloadedFileBySectionPos = new ConcurrentHashMap<>(); - /** contains the loaded {@link RenderDataMetaFile}'s */ - private final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); + protected final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); //loadedMetaFileBySectionPos private final IDhClientLevel clientLevel; private final File saveDir; @@ -79,36 +73,6 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider this.threadPoolMsg = new F3Screen.NestedMessage(this::f3Log); - - MetaFileScanUtil.scanRenderFiles(saveStructure, clientLevel.getLevelWrapper(), this); - } - - /** - * Caller must ensure that this method is called only once, - * and that the given files are not used before this method is called.

- * - * Used by {@link MetaFileScanUtil#scanRenderFiles(AbstractSaveStructure, ILevelWrapper, ILodRenderSourceProvider)} - */ - @Override - public void addScannedFiles(Collection detectedFiles) - { - MetaFileScanUtil.ICreateMetadataFunc createMetadataFunc = (file) -> RenderDataMetaFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, file); - - MetaFileScanUtil.IAddUnloadedFileFunc addUnloadedFileFunc = (pos, file) -> - { - this.unloadedFileBySectionPos.put(pos, file); - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); - }; - MetaFileScanUtil.IAddLoadedMetaFileFunc addLoadedMetaFileFunc = (pos, loadedMetaFile) -> - { - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); - this.metaFileBySectionPos.put(pos, (RenderDataMetaFile) loadedMetaFile); - }; - - - MetaFileScanUtil.addScannedFiles(detectedFiles, USE_LAZY_LOADING, RenderDataMetaFile.FILE_SUFFIX, - createMetadataFunc, - addUnloadedFileFunc, addLoadedMetaFileFunc); } @@ -129,6 +93,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); RenderDataMetaFile metaFile = this.getLoadOrMakeFile(pos); if (metaFile == null) { @@ -158,39 +123,26 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider RenderDataMetaFile metaFile = this.metaFileBySectionPos.get(pos); if (metaFile != null) { - // return the loaded file return metaFile; } - // we don't have a loaded file, for that pos, - // do we have an unloaded file for that pos? - File fileToLoad = this.unloadedFileBySectionPos.get(pos); - if (fileToLoad != null && !fileToLoad.exists()) + File fileToLoad = this.computeRenderFilePath(pos); + if (fileToLoad.exists()) { - fileToLoad = null; - this.unloadedFileBySectionPos.remove(pos); - } - - - if (fileToLoad != null) - { - // A file exists, but isn't loaded yet. - - // Double check locking for loading file, as loading file means also loading the metadata, which - // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the - // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. synchronized (this) { - // check if another thread already finished loading this file + // A file exists, but isn't loaded yet. + + // Double check locking for loading file, as loading file means also loading the metadata, which + // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the + // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. metaFile = this.metaFileBySectionPos.get(pos); if (metaFile != null) { - return metaFile; + return metaFile; // someone else loaded it already. } - - // attempt to load the file try { metaFile = RenderDataMetaFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, fileToLoad); @@ -200,41 +152,36 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } catch (IOException e) { - LOGGER.error("Failed to read render meta file at " + fileToLoad + ": ", e); + LOGGER.error("Failed to read meta data file at " + fileToLoad + ": ", e); FileUtil.renameCorruptedFile(fileToLoad); } - finally - { - this.unloadedFileBySectionPos.remove(pos); - } } } - // Either no file exists for this position - // or the existing file was corrupted. - // Create a new file. + // File does not exist, create it. + // In this case, since 'creating' a file object doesn't actually do anything heavy on IO yet, we use CAS + // to avoid overhead of 'synchronized', and eat the mini-overhead of possibly creating duplicate objects. try { - // createFromExistingOrNewFile() is used instead of createFromExistingFile() - // due to a rare issue where the file may already exist but isn't in the file list - metaFile = RenderDataMetaFile.createFromExistingOrNewFile(this.clientLevel, this.fullDataSourceProvider, pos, this.computeRenderFilePath(pos)); - - this.topDetailLevelRef.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.getDetailLevel())); - - // Compare And Swap to handle a concurrency issue where multiple threads created the same Meta File at the same time - RenderDataMetaFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); - return (metaFileCas == null) ? metaFile : metaFileCas; + metaFile = RenderDataMetaFile.createNewFileForPos(this.fullDataSourceProvider, this.clientLevel, pos, fileToLoad); } catch (IOException e) { - LOGGER.error("IOException on creating new data file at "+pos, e); + LOGGER.error("IOException on creating new render data file at "+pos, e); return null; } + + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); + + // This is a Compare And Swap with expected null value. + RenderDataMetaFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); + return (metaFileCas == null) ? metaFile : metaFileCas; } + //=============// // data saving // //=============// @@ -282,16 +229,13 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider @Override public CompletableFuture flushAndSaveAsync() { - LOGGER.info("Shutting down " + RenderSourceFileHandler.class.getSimpleName() + "..."); - ArrayList> futures = new ArrayList<>(); for (RenderDataMetaFile metaFile : this.metaFileBySectionPos.values()) { futures.add(metaFile.flushAndSaveAsync()); } - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .whenComplete((voidObj, exception) -> LOGGER.info("Finished saving " + RenderSourceFileHandler.class.getSimpleName())); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } @@ -305,7 +249,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { ArrayList lines = new ArrayList<>(); lines.add("Render Source File Handler [" + this.clientLevel.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); - lines.add(" Loaded files: " + this.metaFileBySectionPos.size() + " / " + (this.unloadedFileBySectionPos.size() + this.metaFileBySectionPos.size())); + lines.add(" Loaded files: " + this.metaFileBySectionPos.size()); lines.add(" Thread pool tasks: " + this.fileHandlerThreadPool.getQueue().size() + " (completed: " + this.fileHandlerThreadPool.getCompletedTaskCount() + ")"); int totalFutures = this.taskTracker.size(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java deleted file mode 100644 index 726c92d0a..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.util; - -import com.google.common.collect.HashMultimap; -import com.seibel.distanthorizons.core.file.fullDatafile.FullDataFileHandler; -import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; -import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; -import com.seibel.distanthorizons.core.file.metaData.AbstractMetaDataContainerFile; -import com.seibel.distanthorizons.core.file.renderfile.ILodRenderSourceProvider; -import com.seibel.distanthorizons.core.file.renderfile.RenderDataMetaFile; -import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** Used to pull in the initial files used by both {@link IFullDataSourceProvider} and {@link ILodRenderSourceProvider}s. */ -public class MetaFileScanUtil -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final int MAX_SCAN_DEPTH = 5; - - - - //===============// - // file scanning // - //===============// - - // file scanning means to find all File's in a given directory - - // TODO merge with the below method - public static void scanFullDataFiles(AbstractSaveStructure saveStructure, ILevelWrapper levelWrapper, IFullDataSourceProvider dataSourceProvider) - { - try (Stream pathStream = Files.walk(saveStructure.getFullDataFolder(levelWrapper).toPath(), MAX_SCAN_DEPTH)) - { - List files = pathStream.filter( - path -> path.toFile().getName().endsWith(FullDataMetaFile.FILE_SUFFIX) && path.toFile().isFile() - ).map(Path::toFile).collect(Collectors.toList()); - LOGGER.info("Found " + files.size() + " full data files for " + levelWrapper + " in " + saveStructure); - dataSourceProvider.addScannedFiles(files); - } - catch (Exception e) - { - LOGGER.error("Failed to scan and collect full data files for " + levelWrapper + " in " + saveStructure, e); - } - } - - // TODO merge with the above method - public static void scanRenderFiles(AbstractSaveStructure saveStructure, ILevelWrapper levelWrapper, ILodRenderSourceProvider renderSourceProvider) - { - try (Stream pathStream = Files.walk(saveStructure.getRenderCacheFolder(levelWrapper).toPath(), MAX_SCAN_DEPTH)) - { - List files = pathStream.filter( - path -> path.toFile().getName().endsWith(RenderDataMetaFile.FILE_SUFFIX) && path.toFile().isFile() - ).map(Path::toFile).collect(Collectors.toList()); - LOGGER.info("Found " + files.size() + " render cache files for " + levelWrapper + " in " + saveStructure); - renderSourceProvider.addScannedFiles(files); - } - catch (Exception e) - { - LOGGER.error("Failed to scan and collect cache files for " + levelWrapper + " in " + saveStructure, e); - } - } - - - - //======================// - // Adding scanned files // - //======================// - - /** - * Caller must ensure that this method is called only once, - * and that the {@link FullDataFileHandler} is not used before this method is called. - */ - public static void addScannedFiles( - Collection detectedFiles, boolean useLazyLoading, String fileSuffix, - ICreateMetadataFunc createMetadataFunc, - IAddUnloadedFileFunc addUnloadedFileFunc, IAddLoadedMetaFileFunc addLoadedMetaFileFunc) - { - if (useLazyLoading) - { - lazyAddScannedFile(detectedFiles, fileSuffix, addUnloadedFileFunc); - } - else - { - immediateAddScannedFile(detectedFiles, createMetadataFunc, addLoadedMetaFileFunc); - } - } - private static void lazyAddScannedFile(Collection detectedFiles, String fileSuffix, IAddUnloadedFileFunc addUnloadedFileFunc) - { - for (File file : detectedFiles) - { - if (file == null || !file.exists()) - { - // can rarely happen if the user rapidly travels between dimensions - LOGGER.warn("Null or non-existent file: " + ((file != null) ? file.getPath() : "NULL")); - continue; - } - - try - { - DhSectionPos pos = decodePositionFromFileName(file, fileSuffix); - if (pos != null) - { - addUnloadedFileFunc.addFile(pos, file); - } - } - catch (Exception e) - { - LOGGER.error("Failed to read data meta file at " + file + ": ", e); - FileUtil.renameCorruptedFile(file); - } - } - } - private static void immediateAddScannedFile( - Collection detectedFiles, - ICreateMetadataFunc createMetadataFunc, IAddLoadedMetaFileFunc addLoadedMetaFileFunc) - { - HashMultimap filesByPos = HashMultimap.create(); - { // Sort files by pos. - for (File file : detectedFiles) - { - try - { - AbstractMetaDataContainerFile metaFile = createMetadataFunc.createFile(file); - filesByPos.put(metaFile.pos, metaFile); - } - catch (IOException e) - { - LOGGER.error("Failed to read data meta file at " + file + ": ", e); - FileUtil.renameCorruptedFile(file); - } - } - } - - - // Warn for multiple files with the same pos, and then select the one with the latest timestamp. - for (DhSectionPos pos : filesByPos.keySet()) - { - Collection metaFiles = filesByPos.get(pos); - AbstractMetaDataContainerFile metaFileToUse; - if (metaFiles.size() > 1) - { - // sort by the file's last modified date - metaFileToUse = Collections.max(metaFiles, Comparator.comparingLong(fullDataMetaFile -> fullDataMetaFile.file.lastModified())); - - // log the duplicate files - StringBuilder duplicateMessage = new StringBuilder(); - duplicateMessage.append("Multiple files with the same pos: ").append(pos).append("\n"); - for (AbstractMetaDataContainerFile metaFile : metaFiles) - { - duplicateMessage.append("\t").append(metaFile.file).append("\n"); - } - duplicateMessage.append("\tUsing: ").append(metaFileToUse.file).append("\n"); - duplicateMessage.append("(Other files will be renamed by appending \".old\" to their name.)"); - LOGGER.warn(duplicateMessage.toString()); - - - - // Rename all other files with the same pos to .old - for (AbstractMetaDataContainerFile metaFile : metaFiles) - { - if (metaFile == metaFileToUse) - { - continue; - } - - - File oldFile = new File(metaFile.file + ".old"); - try - { - if (!metaFile.file.renameTo(oldFile)) - { - throw new RuntimeException("Renaming failed"); - } - } - catch (Exception e) - { - LOGGER.error("Failed to rename file: " + metaFile.file + " to " + oldFile, e); - } - } - } - else - { - metaFileToUse = metaFiles.iterator().next(); - } - - // Add file to the list of files. - addLoadedMetaFileFunc.addFile(pos, metaFileToUse); - } - } - - - - //================// - // helper methods // - //================// - - /** @return null if the file name can't be parsed into a {@link DhSectionPos} */ - @Nullable - public static DhSectionPos decodePositionFromFileName(File file, String fileSuffix) - { - String fileName = file.getName(); - if (!fileName.endsWith(fileSuffix)) - { - return null; - } - - fileName = fileName.substring(0, fileName.length() - 4); - return DhSectionPos.deserialize(fileName); - } - - - - //===================// - // helper interfaces // - //===================// - - @FunctionalInterface - public interface ICreateMetadataFunc { AbstractMetaDataContainerFile createFile(File file) throws IOException; } - - @FunctionalInterface - public interface IAddUnloadedFileFunc { void addFile(DhSectionPos pos, File file); } - @FunctionalInterface - public interface IAddLoadedMetaFileFunc { void addFile(DhSectionPos pos, AbstractMetaDataContainerFile metaFile); } - -} From 72e396bbcd63ed258ed326b9847c0aa0042b404f Mon Sep 17 00:00:00 2001 From: coolGi Date: Sun, 24 Sep 2023 11:20:30 +0930 Subject: [PATCH 13/16] =?UTF-8?q?Added=20Russian=20language=20file=20(Thx?= =?UTF-8?q?=20noname24=5F,=20pixsym=20(sym),=20and=20pshsh=20(=D0=BF=D1=88?= =?UTF-8?q?=D1=88))?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/distanthorizons/lang/ru_ru.json | 500 ++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 core/src/main/resources/assets/distanthorizons/lang/ru_ru.json diff --git a/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json b/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json new file mode 100644 index 000000000..9f3a0e78a --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json @@ -0,0 +1,500 @@ +{ + "distanthorizons.title": + "Distant Horizons", + + + "distanthorizons.general.true": + "Вкл.", + "distanthorizons.general.false": + "Выкл.", + "distanthorizons.general.yes": + "Да", + "distanthorizons.general.no": + "Нет", + "distanthorizons.general.back": + "Назад", + "distanthorizons.general.next": + "Следующий", + "distanthorizons.general.done": + "Готово", + "distanthorizons.general.cancel": + "Отмена", + "distanthorizons.general.reset": + "Сброc", + + + + + "distanthorizons.updater.title": + "Автоматическое обновление Distant Horizons", + "distanthorizons.updater.text1": + "§lДоступно новое обновление!", + "distanthorizons.updater.text2": + "§fВы хотите перейти с %s§f на %s§f?", + "distanthorizons.updater.later": + "Не сейчас", + "distanthorizons.updater.never": + "Не показывать снова", + "distanthorizons.updater.update": + "Обновить", + "distanthorizons.updater.update.@tooltip": + "Обновить мод 1 раз\n(обновляется при закрытии игры)", + "distanthorizons.updater.silent": + "Всегда тихое обновление", + "distanthorizons.updater.silent.@tooltip": + "Каждый раз, когда доступно обновление, оно будет обновляться\n(§6ПРЕДУПРЕЖДЕНИЕ§r: Оно не будет запрашивать у пользователя разрешение на обновление, но будет поддерживать мод в актуальном состоянии)", + "distanthorizons.updater.waitingForClose": + "Обновление Distant Horizons завершится после перезапуска игры", + + + + + "distanthorizons.config.title": + "Конфигурация Distant Horizons", + "distanthorizons.config.client": + "Клиент", + + "distanthorizons.config.client.quickEnableRendering": + "Включить рендеринг", + "distanthorizons.config.client.quickEnableRendering.@tooltip": + "Определяет, отображает ли Distant Horizons LODs.", + + "distanthorizons.config.client.qualityPresetSetting": + "Предустановленное качество", + "distanthorizons.config.client.qualityPresetSetting.@tooltip": + "Изменяет ряд графических настроек для быстрого изменения качества рендеринга Distant Horizons. \n\nУменьшите этот параметр, если ваш графический процессор максимально загружен или у вас возникли проблемы с частотой кадров.", + "distanthorizons.config.client.threadPresetSetting": + "Загруженность процессора", + "distanthorizons.config.client.threadPresetSetting.@tooltip": + "Изменяет количество потоков, которые будут использоваться в Distant Horizons. \n\nУвеличение этого параметра улучшит скорость удаленного генератора (Distant Generator) и скорость загрузки LOD, \nно также увеличит использование процессора/памяти и может привести к заиканию. \n\nПримечание: на процессорах с 4 ядрами или меньше эти настройки будут менее эффективными, \nи некоторые настройки дадут аналогичные результаты.", + + "distanthorizons.config.client.optionsButton": + "Отобразить кнопку Параметров", + "distanthorizons.config.client.optionsButton.@tooltip": + "Показать кнопку Параметров слева от кнопки FOV.", + + + + "distanthorizons.config.client.advanced": + "Расширенные параметры", + + "distanthorizons.config.client.advanced.graphics": + "Графика", + "distanthorizons.config.client.advanced.graphics.quality": + "Качество рендеринга", + + "distanthorizons.config.client.advanced.graphics.quality.maxHorizontalResolution": "Максимальное горизонтальное разрешение", + "distanthorizons.config.client.advanced.graphics.quality.maxHorizontalResolution.@tooltip": "Изображения с максимальной детализацией отображаются на.\n\n§6Самый быстрый:§r Чанк\n§6Самый красивый:§r Блок", + "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistance": "Расстояние рендеринга LOD", + "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistance.@tooltip": "Расстояние рендеринга Distant Horizons, измеряемое в чанках. \n\nПримечание: это максимально возможное число. \nДальность рендеринга может быть выше или ниже этого числа \nв зависимости от других графических настроек.", + "distanthorizons.config.client.advanced.graphics.quality.verticalQuality": "Вертикальное качество", + "distanthorizons.config.client.advanced.graphics.quality.verticalQuality.@tooltip": "На сколько хорошо LOD (уровень детализации) отображает выступы, пещеры, обрывы и т.д.\n\nБолее высокие параметры будут увеличивать использование памяти и GPU.", + "distanthorizons.config.client.advanced.graphics.quality.horizontalScale": "Горизонтальная шкала", + "distanthorizons.config.client.advanced.graphics.quality.horizontalScale.@tooltip": "На сколько быстро уровни детализации (LOD) снижают качество.\n\nБольшие значения улучшат вид дальней местности, \nно увеличат использование памяти и GPU.", + "distanthorizons.config.client.advanced.graphics.quality.horizontalQuality": "Горизонтальное качество", + "distanthorizons.config.client.advanced.graphics.quality.horizontalQuality.@tooltip": "На сколько далеко друг от друга находятся уровни качества.\n\nБолее высокие настройки увеличивают расстояние между уровнями,\nно также увеличивают использование памяти и GPU.", + "distanthorizons.config.client.advanced.graphics.quality.transparency": "Прозрачность", + "distanthorizons.config.client.advanced.graphics.quality.blocksToIgnore": "Игнорируемые блоки", + "distanthorizons.config.client.advanced.graphics.quality.blocksToIgnore.@tooltip": "Определяет типы блоков, которые следует игнорировать при генерации LODs.", + "distanthorizons.config.client.advanced.graphics.quality.tintWithAvoidedBlocks": "Окрашивать с игнорируемыми блоками", + "distanthorizons.config.client.advanced.graphics.quality.tintWithAvoidedBlocks.@tooltip": "§4Примечание: делает снег, ковры и люки очень плохо выглядящими.§r\nДолжны ли блоки под игнорируемыми блоками приобретать цвет игнорируемого блока?\n§6Вкл.:§r красный цвет цветка на траве окрасит траву под ним в красный цвет\n§6Выкл.:§r пропущенные блоки не изменят цвет поверхности под ними", + "distanthorizons.config.client.advanced.graphics.quality.lodBiomeBlending": "Смешивание биомов для LOD", + "distanthorizons.config.client.advanced.graphics.quality.lodBiomeBlending.@tooltip": "Это то же самое, что и настройки смешивания биомов ванильного мира для области LOD. \n\nОбратите внимание, что значения, отличные от '0', существенно влияют на время создания LOD\nи увеличивают количество треугольников. Стоимость на скорость создания чанков также \nдовольно велика, если установлена слишком высокая плотность.\n\n'0' соответствует ванильному смешиванию биомов '1x1', \n'1' соответствует ванильному смешиванию биомов '3x3', \n'2' соответствует ванильному смешиванию биомов '5x5'... ", + + + "distanthorizons.config.client.advanced.graphics.fog": "Туман", + + "distanthorizons.config.client.advanced.graphics.fog.drawMode": "Режим отображения тумана", + "distanthorizons.config.client.advanced.graphics.fog.drawMode.@tooltip": "Когда будет отрисовываться туман на LODs (уровнях детализации).", + "distanthorizons.config.client.advanced.graphics.fog.distance": "Дальность тумана", + "distanthorizons.config.client.advanced.graphics.fog.distance.@tooltip": "Дальность(и), на которой туман будет отрисовываться на LODs.", + "distanthorizons.config.client.advanced.graphics.fog.colorMode": "Режим цвета тумана", + "distanthorizons.config.client.advanced.graphics.fog.colorMode.@tooltip": "Цвет тумана на LODs.", + "distanthorizons.config.client.advanced.graphics.fog.disableVanillaFog": "Отключить ванильный туман", + "distanthorizons.config.client.advanced.graphics.fog.disableVanillaFog.@tooltip": "§6Вкл.:§r отключает туман Minecraft на ванильных чанках. \n§6Выкл.:§r Minecraft отрисовывает туман как обычно. \n\nМожет вызывать проблемы с другими модами, изменяющими туман. \nОтключите, если ванильные чанки полностью покрыты туманом.", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog": "Дополнительные настройки тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogStart": "Начало дальнего тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogStart.@tooltip": "С какого места должен начинаться дальний туман? \n\n'0.0': Туман начинается от позиции игрока. \n'1.0': Круг начала тумана точно вписывается в квадрат дальности отрисовки LOD.\n'1.414': Квадрат дальности отрисовки LOD точно вписывается в круг начала тумана.\n", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogEnd": "Конец дальнего тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogEnd.@tooltip": "С какого места должен заканчиваться дальний туман? \n\n'0.0': Туман заканчивается у позиции игрока. \n'1.0': Круг окончания тумана точно вписывается в квадрат дальности отрисовки LOD.\n'1.414': Квадрат дальности отрисовки LOD точно вписывается в круг окончания тумана.\n", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogMin": "Минимальная толщина дальнего тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogMin.@tooltip": "Какая должна быть минимальная толщина дальнего тумана? \n\n'0.0': Нет тумана. \n'1.0': Полностью цветной туман.\n", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogMax": "Максимальная толщина дальнего тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogMax.@tooltip": "Какая должна быть максимальная толщина дальнего тумана? \n\n'0.0': Нет тумана. \n'1.0': Полностью цветной туман.\n", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogFalloff": "Затухание дальнего тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogFalloff.@tooltip": "Как должна рассчитываться толщина дальнего тумана? \n\nЛИНЕЙНО: Линейно на основе расстояния (игнорирует 'плотность') \nЭКСПОНЕНЦИАЛЬНО: 1/(e^(расстояние*плотность)) \nКВАДРАТ ЭКСПОНЕНЦИАЛЬНО: 1/(e^((расстояние*плотность)^2) \n", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogDensity": "Плотность дальнего тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogDensity.@tooltip": "Какая должна быть плотность дальнего тумана? ", + + + "distanthorizons.config.client.advanced.graphics.ssao": "Окружающая окклюзия (Ambient Occlusion)", + + "distanthorizons.config.client.advanced.graphics.ssao.enabled": "Включить окружающую окклюзию (Ambient Occlusion)", + "distanthorizons.config.client.advanced.graphics.ssao.enabled.@tooltip": "Окружающая окклюзия добавляет глубину к освещению блоков.", + "distanthorizons.config.client.advanced.graphics.ssao.sampleCount": "Количество образцов", + "distanthorizons.config.client.advanced.graphics.ssao.sampleCount.@tooltip": "Определяет, сколько точек в пространстве отбирается для теста окклюзии. \nБольшее количество улучшит качество и уменьшит полосирование, но увеличит нагрузку на ГПУ.", + "distanthorizons.config.client.advanced.graphics.ssao.radius": "Радиус", + "distanthorizons.config.client.advanced.graphics.ssao.radius.@tooltip": "Определяет радиус применения эффекта окружающей окклюзии в блоках.", + "distanthorizons.config.client.advanced.graphics.ssao.strength": "Сила", + "distanthorizons.config.client.advanced.graphics.ssao.strength.@tooltip": "Определяет, насколько темным будет эффект окружающей окклюзии в пространстве экрана.", + "distanthorizons.config.client.advanced.graphics.ssao.bias": "Смещение", + "distanthorizons.config.client.advanced.graphics.ssao.bias.@tooltip": "Увеличение этого значения может уменьшить полосирование, но за счет уменьшения силы эффекта.", + "distanthorizons.config.client.advanced.graphics.ssao.minLight": "Минимальная яркость", + "distanthorizons.config.client.advanced.graphics.ssao.minLight.@tooltip": "Определяет, насколько темными могут быть тени окклюзии. \n0 = полностью черные в углах \n1 = без тени", + "distanthorizons.config.client.advanced.graphics.ssao.blurRadius": "Радиус размытия", + "distanthorizons.config.client.advanced.graphics.ssao.blurRadius.@tooltip": "Радиус, измеряемый в пикселях, для которого вычисляется размытие окружающей окклюзии. \nБольшие значения уменьшат полосирование, но увеличат нагрузку на ГПУ.", + + + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog": "Параметры высотного тумана", + + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMixMode": "Режим смешивания высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMixMode.@tooltip": "Как высота должна влиять на толщину тумана, совмещаясь с обычной функцией? \n\nОСНОВНОЙ: Нет специального эффекта высотного тумана. Туман рассчитывается на основе расстояния от камеры. \nИГНОРИРОВАТЬ_ВЫСОТУ: Полное игнорирование высоты. Туман рассчитывается на основе горизонтального расстояния. \nСЛОЖЕНИЕ: высотный туман + обычный туман \nМАКСИМУМ: максимум из высотного тумана и обычного тумана \nУМНОЖЕНИЕ: высотный_туман * обычный_туман \nОБРАТНОЕ_УМНОЖЕНИЕ: 1 - (1-высотный_туман) * (1-обычный_туман) \nОГРАНИЧЕННОЕ_СЛОЖЕНИЕ: обычный_туман + максимум_из_обычного_и_высотного_тумана \nУМНОЖЕНИЕ_И_СЛОЖЕНИЕ: обычный_туман + обычный_туман * высотный_туман \nОБРАТНОЕ_УМНОЖЕНИЕ_И_СЛОЖЕНИЕ: обычный_туман + 1 - (1-высотный_туман) * (1-обычный_туман) \nСРЕДНЕЕ: обычный_туман * 0.5 + высотный_туман * 0.5 \n\nОбратите внимание, что для режимов 'ОСНОВНОЙ' и 'ИГНОРИРОВАТЬ_ВЫСОТУ' настройки тумана для высотного тумана не имеют эффекта.", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMode": "Режим высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMode.@tooltip": "Где должен находиться высотный туман? \n\nНАД_КАМЕРОЙ: Высотный туман начинается от камеры до неба \nПОД_КАМЕРОЙ: Высотный туман начинается от камеры до пустоты \nНАД_И_ПОД_КАМЕРОЙ: Высотный туман начинается от камеры как до неба, так и до пустоты \nНАД_УСТАНОВЛЕННОЙ_ВЫСОТОЙ: Высотный туман начинается от установленной высоты до неба \nПОД_УСТАНОВЛЕННОЙ_ВЫСОТОЙ: Высотный туман начинается от установленной высоты до пустоты \nНАД_И_ПОД_УСТАНОВЛЕННОЙ_ВЫСОТОЙ: Высотный туман начинается от установленной высоты как до неба, так и до пустоты", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogBaseHeight": "Базовая высота высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogBaseHeight.@tooltip": "Если высотный туман рассчитывается относительно установленной высоты, то какая это высота?", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogStart": "Начало высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogStart.@tooltip": "Как далеко должно начинаться начало высотного тумана? \n\n'0.0': Начало тумана без смещения.\n'1.0': Начало тумана с смещением на всю высоту мира. (Включая глубину)", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogEnd": "Конец высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogEnd.@tooltip": "Как далеко должен распространяться конец высотного тумана? \n\n'0.0': Конец тумана без смещения.\n'1.0': Конец тумана с смещением на всю высоту мира. (Включая глубину)", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMin": "Минимальная толщина высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMin.@tooltip": "Какая минимальная толщина тумана должна быть? \n\n'0.0': Вообще нет тумана.\n'1.0': Полностью цвет тумана.", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMax": "Максимальная толщина высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogMax.@tooltip": "Какая максимальная толщина тумана должна быть? \n\n'0.0': Вообще нет тумана.\n'1.0': Полностью цвет тумана.", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogFalloff": "Затухание высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogFalloff.@tooltip": "Как должна рассчитываться толщина высотного тумана? \n\nЛИНЕЙНОЕ: Линейно в зависимости от высоты (игнорирует 'плотность')\nЭКСПОНЕНЦИАЛЬНОЕ: 1/(e^(высота*плотность)) \nКВАДРАТ ЭКСПОНЕНЦИАЛЬНОЕ: 1/(e^((высота*плотность)^2) \n", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogDensity": "Плотность высотного тумана", + "distanthorizons.config.client.advanced.graphics.fog.advancedFog.heightFog.heightFogDensity.@tooltip": "Какая плотность у высотного тумана?", + + + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings": "Текстура шума", + + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseEnabled": "Включить текстуру шума", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseEnabled.@tooltip": "Если включено, текстура шума будет применяться к LOD для имитации текстур.", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseSteps": "Шаги шума", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseSteps.@tooltip": "Разрешение шума, применяемого к каждому блоку на LOD", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseIntensity": "Интенсивность шума", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseIntensity.@tooltip": "Определяет насколько интенсивной будет текстура шума.", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseDropoff": "Затухание шума", + "distanthorizons.config.client.advanced.graphics.noiseTextureSettings.noiseDropoff.@tooltip": "Определяет, на каком расстоянии текстура шума будет затухать (в блоках). \nУстановите значение 0, чтобы отключить затухание текстуры шума.", + + + "distanthorizons.config.client.advanced.graphics.advancedGraphics": "Расширенные параметры графики", + + "distanthorizons.config.client.advanced.graphics.advancedGraphics.overdrawPrevention": "Предотвращение избыточной отрисовки", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.overdrawPrevention.@tooltip": "Включение этой опции предотвратит некоторые проблемы с избыточной отрисовкой, \nно может вызвать некорректную отрисовку близких LOD, \nособенно когда они находятся рядом с листвой или неполными блоками. \nМенее заметно при увеличении стандартной дистанции отрисовки Minecraft.", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.seamlessOverdraw": "(Экспериментально) Безшовная избыточная отрисовка", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.seamlessOverdraw.@tooltip": "Экспериментальная функция, которая попытается согласовать ближнюю и дальнюю плоскости отсечения \nDistant Horizons и Minecraft для уменьшения избыточной отрисовки. \n\nПримечание: работает только с Fabric.", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.brightnessMultiplier": "Множитель яркости", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.brightnessMultiplier.@tooltip": "Определяет, насколько яркими будут цвета LOD. \n\n0 = черный \n1 = нормальный \n2 = почти белый", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.saturationMultiplier": "Множитель насыщенности", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.saturationMultiplier.@tooltip": "Определяет, насколько насыщенными будут цвета LOD. \n\n0 = черно-белые \n1 = нормальные \n2 = насыщенные", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.enableCaveCulling": "Отсечение пещер", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.enableCaveCulling.@tooltip": "Если включено, пещеры будут отсекаться. \n\nДополнительная информация: В настоящее время это отсекает все грани с уровнем света 0 под уровнем отсечения пещер в измерениях, не имеющих потолка.", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.caveCullingHeight": "Уровень отсечения пещер", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.caveCullingHeight.@tooltip": "На какой высоте начинается отсечение пещер?", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.earthCurveRatio": "Коэффициент кривизны Земли §6(ЭКСПЕРИМЕНТАЛЬНО)§r", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.earthCurveRatio.@tooltip": "Значение 1 эквивалентно кривизне Земли в реальной жизни. \nМинимально допустимое значение - 50, максимальное - 5000. \nЗначения от 1 до 49 будут округлены до 50.", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias": "Смещение LOD §6(Влияет на ванильный мир)§r", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias.@tooltip": "Устанавливает значение смещения LOD ванильного мира. \nПожалуйста, нажмите F3+T, чтобы перезагрузить текстурные пакеты и применить это изменение.", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodShading": "Затенение LOD", + "distanthorizons.config.client.advanced.graphics.advancedGraphics.lodShading.@tooltip": "Определяет, как следует затенять LOD. \nМожет использоваться для улучшения совместимости с шейдерами.", + + + "distanthorizons.config.client.advanced.worldGenerator": "Генератор мира", + + "distanthorizons.config.client.advanced.worldGenerator.enableDistantGeneration": "Включить удаленную генерацию", + "distanthorizons.config.client.advanced.worldGenerator.enableDistantGeneration.@tooltip": "§6Вкл.:§r в одиночной игре LOD будут генерироваться за пределами стандартной дистанции отрисовки Minecraft.\nПримечание: это может потреблять большое количество ресурсов CPU.\n\n§6Выкл.:§r LOD будут генерироваться только в пределах стандартной дистанции отрисовки Minecraft.", + "distanthorizons.config.client.advanced.worldGenerator.distantGeneratorMode": "Режим удаленной генерации", + "distanthorizons.config.client.advanced.worldGenerator.distantGeneratorMode.@tooltip": "Настройка сложности генерации при создании LOD за пределами стандартной дистанции отрисовки Minecraft. \n\n§6§6Самый быстрый:§r только биомы \n§6Наилучшее качество:§r объекты (рекомендуется)", + "distanthorizons.config.client.advanced.worldGenerator.worldGenLightingEngine": "Световой движок", + "distanthorizons.config.client.advanced.worldGenerator.worldGenLightingEngine.@tooltip": "§6Minecraft:§r использует световой движок Minecraft, обеспечивает точное освещение.\n§6Distant Horizons:§r оценивает освещение, тени не будут такими плавными, но более стабильны.\n\nЕсли LOD отображаются черными, установите это значение на §6Distant Horizons§r.", + "distanthorizons.config.client.advanced.worldGenerator.worldGenerationTimeoutLengthInSeconds": "Длительность ожидания генерации мира (в секундах)", + "distanthorizons.config.client.advanced.worldGenerator.worldGenerationTimeoutLengthInSeconds.@tooltip": "Как долго поток генератора мира должен работать перед завершением по тайм-ауту? \nПримечание: если у вас возникают ошибки по тайм-ауту, лучше сначала уменьшить использование CPU через настройки потоков, а затем уже менять это значение.", + "distanthorizons.config.client.advanced.worldGenerator.generationPriority": "Приоритет генерации", + "distanthorizons.config.client.advanced.worldGenerator.generationPriority.@tooltip": "Приоритет генерации чанков вокруг игрока.", + + + "distanthorizons.config.client.advanced.multiplayer": "Многопользовательская игра", + + "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Режим имени папки сервера", + "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": "Определяет формат имени папки для локальных данных многопользовательских игр.\n\n§6Только имя:§r\nИспользует имя сервера в браузере серверов. Например, \"Сервер Minecraft\"\n§6Имя и IP:§r\n\"Сервер Minecraft, IP 192.168.1.40\"\n§6Имя, IP и порт:§r\n\"Сервер Minecraft, IP 192.168.1.40:25565\"\n§6Имя, IP, порт и версия MC:§r\n\"Сервер Minecraft, IP 192.168.1.40:25565, Версия игры 1.18.1\"\n\n§c§lПредупреждение:§r изменение во время подключения к многопользовательскому серверу может вызвать глюки.", + "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent": "Требуемое сходство для мультивселенной, %", + "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent.@tooltip": "При сопоставлении миров одного типа измерения проверяемые чанки должны быть по крайней мере на этот процент одинаковыми, чтобы считаться одним и тем же миром.\n\nПримечание: если вы используете порталы для входа в измерение в двух разных местах, эта система может считать, что это два разных мира.\n\n§61.0:§r чанки должны быть идентичными.\n§60.5:§r чанки должны быть наполовину одинаковыми.\n§60.0:§r отключает поддержку мультивселенных\n будет использоваться только один мир на измерение.", + "distanthorizons.config.client.advanced.multiplayer.enableMultiverseNetworking": "Поддержка сети для мультивселенных", + "distanthorizons.config.client.advanced.multiplayer.enableMultiverseNetworking.@tooltip": "Если установлено в true, Distant Horizons будет пытаться взаимодействовать с подключенным сервером для улучшения поддержки мультивселенных.", + "distanthorizons.config.client.advanced.multiplayer.enableServerNetworking": "§4Нереализовано, только для разработчиков§r - Поддержка сервера", + "distanthorizons.config.client.advanced.multiplayer.enableServerNetworking.@tooltip": "§6Внимание:§r это только для разработчиков и пока не реализовано.\n\nЕсли установлено в \"Вкл\", Distant Horizons будет пытаться взаимодействовать с подключенным сервером для загрузки LOD за пределами вашей обычной дистанции отрисовки Minecraft.\n\nПримечание: для работы этой функции требуется установить DH на сервере.", + + + "distanthorizons.config.client.advanced.multiThreading": "Многопоточность", + + "distanthorizons.config.client.advanced.multiThreading.numberOfWorldGenerationThreads": "Количество потоков генерации мира", + "distanthorizons.config.client.advanced.multiThreading.numberOfWorldGenerationThreads.@tooltip": "Сколько потоков следует использовать при генерации LODs вне обычной зоны отрисовки? \n\nЕсли это число меньше 1, оно будет рассматриваться как процент времени, в течение которого один поток может работать, прежде чем перейти в режим ожидания. \n\nЕсли у вас есть проблемы с прерываниями при генерации дальних LODs, уменьшите это число. Если вы хотите увеличить скорость генерации LOD, увеличьте это число. \n\nКоличество потоков для генерации LOD и количество потоков для построения буферов независимы друг от друга. Если их сумма больше количества ядер вашего процессора, это нормально.", + "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForWorldGenerationThreads": "Процент времени выполнения для потоков генерации мира", + + "distanthorizons.config.client.advanced.multiThreading.numberOfBufferBuilderThreads": "Количество потоков построения буферов", + "distanthorizons.config.client.advanced.multiThreading.numberOfBufferBuilderThreads.@tooltip": "Количество потоков, используемых при построении данных геометрии. \nМожет быть только от 1 до количества процессорных ядер вашего CPU.", + "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForBufferBuilderThreads": "Процент времени выполнения для потоков построения буферов", + + "distanthorizons.config.client.advanced.multiThreading.numberOfFileHandlerThreads": "Количество потоков обработки файлов", + "distanthorizons.config.client.advanced.multiThreading.numberOfFileHandlerThreads.@tooltip": "Количество потоков, используемых при построении буферов вершин (вещей, отправляемых на ваш GPU для отрисовки LODs). \nМожет быть только от 1 до количества процессорных ядер вашего CPU.", + "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForFileHandlerThreads": "Процент времени выполнения для потоков обработки файлов", + + "distanthorizons.config.client.advanced.multiThreading.numberOfDataTransformerThreads": "Количество потоков преобразования данных", + "distanthorizons.config.client.advanced.multiThreading.numberOfDataTransformerThreads.@tooltip": "Количество потоков, используемых при преобразовании данных ID в данные, подходящие для отрисовки. \n(Это, как правило, происходит при генерации нового ландшафта или изменении настроек графики). \nМожет быть только от 1 до количества процессорных ядер вашего CPU.", + "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForDataTransformerThreads": "Процент времени выполнения для потоков преобразования данных", + + "distanthorizons.config.client.advanced.multiThreading.numberOfChunkLodConverterThreads": "Количество потоков преобразования LOD чанков", + "distanthorizons.config.client.advanced.multiThreading.numberOfChunkLodConverterThreads.@tooltip": "Сколько потоков следует использовать для преобразования чанков Minecraft в данные LOD? \nЭти потоки работают как при генерации мира, так и при загрузке, выгрузке и модификации чанков.", + "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForChunkLodConverterThreads": "Процент времени выполнения для потоков преобразования LOD чанков", + + + + "distanthorizons.config.client.advanced.debugging": "Отладка", + "distanthorizons.config.client.advanced.debugging.rendererMode": "Режим рендера", + "distanthorizons.config.client.advanced.debugging.debugRendering": "Отладка рендера", + "distanthorizons.config.client.advanced.debugging.debugMode.@tooltip": "Активный режим отладки.", + "distanthorizons.config.client.advanced.debugging.renderWireframe": "Отображение проволочной модели", + "distanthorizons.config.client.advanced.debugging.renderWireframe.@tooltip": "Если включено, уровни детализации будут отображаться как проволочные модели.", + "distanthorizons.config.client.advanced.debugging.enableDebugKeybindings": "Включить клавиши отладки", + "distanthorizons.config.client.advanced.debugging.enableDebugKeybindings.@tooltip": "§6Вкл.:§r клавиши отладки могут быть использованы для изменения режима отладки в игре.", + "distanthorizons.config.client.advanced.debugging.lodOnlyMode": "Рендерить только уровни детализации (LOD)", + "distanthorizons.config.client.advanced.debugging.lodOnlyMode.@tooltip": "Если включено, это отключит (большую часть) стандартного рендера Minecraft.\n\nПРИМЕЧАНИЕ: Не сообщайте о проблемах, когда этот режим включен!\nЭта настройка предназначена только для развлечения и отладки.\nСовместимость с модами не гарантируется.", + "distanthorizons.config.client.advanced.debugging.enableWhiteWorld": "Включить белый мир", + "distanthorizons.config.client.advanced.debugging.enableWhiteWorld.@tooltip": "Если включено, цвет вершин не будет передаваться.\nПолезно для отладки шейдеров.", + "distanthorizons.config.client.advanced.debugging.allowUnsafeValues": "Разрешить небезопасные значения в пользовательском интерфейсе", + "distanthorizons.config.client.advanced.debugging.allowUnsafeValues.@tooltip": "Если включено, будет выполняться очень ограниченная проверка входных данных конфигурации.\n\nПредупреждение: включение этой опции может вызвать нестабильность или сбои, используйте на свой страх и риск.\nПримечание: эта опция не сохраняется между сеансами.", + "distanthorizons.config.client.advanced.debugging.overrideVanillaGLLogger": "Переопределить встроенный регистратор ошибок OpenGL", + "distanthorizons.config.client.advanced.debugging.overrideVanillaGLLogger.@tooltip": "Требуется перезагрузка Minecraft для применения изменений.", + "distanthorizons.config.client.advanced.debugging.glErrorHandlingMode": "Режим обработки ошибок OpenGL", + "distanthorizons.config.client.advanced.debugging.glErrorHandlingMode.@tooltip": "Определяет, как обрабатываются ошибки OpenGL.\nМожет неправильно обнаруживать ошибки OpenGL, вызванные другими модами.", + "distanthorizons.config.client.advanced.debugging.glProfileMode": "Режим профиля OpenGL", + "distanthorizons.config.client.advanced.debugging.glProfileMode.@tooltip": "Требуется перезагрузка Minecraft для применения изменений.", + "distanthorizons.config.client.advanced.debugging.glForwardCompatibilityMode": "Режим совместимости впереди OpenGL", + "distanthorizons.config.client.advanced.debugging.glForwardCompatibilityMode.@tooltip": "Требуется перезагрузка Minecraft для применения изменений.", + + + "distanthorizons.config.client.advanced.buffers": "Буферы", + "distanthorizons.config.client.advanced.buffers.gpuUploadMethod": "Метод загрузки на GPU", + "distanthorizons.config.client.advanced.buffers.gpuUploadMethod.@tooltip": "Метод загрузки геометрии на GPU.\n\nЕсли у вас возникают тормоза при низкой загрузке CPU и GPU, попробуйте изменить эту настройку.\nПримечание: если вы находитесь в мире, вам может потребоваться выйти и зайти заново, чтобы увидеть полный эффект.", + "distanthorizons.config.client.advanced.buffers.gpuUploadPerMegabyteInMilliseconds": "Скорость загрузки на GPU (миллисекунды)", + "distanthorizons.config.client.advanced.buffers.gpuUploadPerMegabyteInMilliseconds.@tooltip": "Сколько времени должен ждать буфер на каждый мегабайт загруженных данных?\nМожет быть увеличено, если наблюдается прерывание кадров.", + + + "distanthorizons.config.client.advanced.autoUpdater": "Автообновление", + "distanthorizons.config.client.advanced.autoUpdater.enableAutoUpdater": "Включить автоматические обновления", + "distanthorizons.config.client.advanced.autoUpdater.enableSilentUpdates": "Включить бесшумные обновления", + "distanthorizons.config.client.advanced.autoUpdater.enableSilentUpdates.@tooltip": "Автоматически обновляет мод, когда доступно обновление", + + "distanthorizons.config.client.advanced.logging": "Логирование", + "distanthorizons.config.client.advanced.logging.@tooltip": "Управляет тем, как должно обрабатываться логирование.", + + "distanthorizons.config.client.advanced.logging.logWorldGenEvent": "События генерации мира", + "distanthorizons.config.client.advanced.logging.logWorldGenPerformance": "Производительность генерации мира", + "distanthorizons.config.client.advanced.logging.logWorldGenLoadEvent": "События загрузки генерации мира", + "distanthorizons.config.client.advanced.logging.logLodBuilderEvent": "События построения LOD", + "distanthorizons.config.client.advanced.logging.logRendererBufferEvent": "События буфера отрисовки", + "distanthorizons.config.client.advanced.logging.logRendererGLEvent": "События OpenGL", + "distanthorizons.config.client.advanced.logging.logFileReadWriteEvent": "События чтения/записи файла", + "distanthorizons.config.client.advanced.logging.logFileSubDimEvent": "События подразделения файла", + "distanthorizons.config.client.advanced.logging.logNetworkEvent": "События сети", + + + + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen": "Отладка настройки экрана", + + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.debugConfigScreenNote": "Этот экран предназначен для отладки функций экрана настройки", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.boolTest": "Тест логического типа (bool)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.byteTest": "Тест байтов (byte)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.intTest": "Тест целых чисел (int)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.doubleTest": "Тест чисел с плавающей точкой (double)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.shortTest": "Тест коротких чисел (short)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.longTest": "Тест длинных чисел (long)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.floatTest": "Тест чисел с плавающей точкой (float)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.stringTest": "Тест строк (string)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.uiButtonTest": "Тест кнопки интерфейса", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.listTest": "Тест списка (ArrayList)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.mapTest": "Тест карты (HashMap)", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.categoryTest": "Тест категории", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.linkableTest": "Тест ссылок", + "distanthorizons.config.client.advanced.debugging.exampleConfigScreen.linkableTest.@tooltip": "Значение этой строки должно совпадать с значением в категории теста", + + "distanthorizons.config.client.advanced.debugging.debugWireframe": "Отладочная сетка", + + "distanthorizons.config.client.advanced.debugging.debugWireframe.enableRendering": "Включить отладку отрисовки сетки", + "distanthorizons.config.client.advanced.debugging.debugWireframe.enableRendering.@tooltip": "При включении будут отрисованы различные сетки для отладки внутренних функций.", + "distanthorizons.config.client.advanced.debugging.debugWireframe.showWorldGenQueue": "Показать очередь генерации мира", + "distanthorizons.config.client.advanced.debugging.debugWireframe.showRenderSectionStatus": "Показать статус раздела отрисовки", + "distanthorizons.config.client.advanced.debugging.debugWireframe.showFullDataFileStatus": "Показать статус полного файла данных", + "distanthorizons.config.client.advanced.debugging.debugWireframe.showFullDataFileSampling": "Показать выборку полного файла данных", + "distanthorizons.config.client.advanced.debugging.debugWireframe.showRenderDataFileStatus": "Показать статус файла данных отрисовки", + + "distanthorizons.config.client.resetSettingsConfirmation": "Сбросить все настройки?", + + "distanthorizons.config.client.resetSettingsConfirmation.resetConfirmationNote": "Вы уверены? Это действие нельзя отменить!", + "distanthorizons.config.client.resetSettingsConfirmation.resetAllSettings": "Сбросить все настройки", + + + + "distanthorizons.config.enum.EQualityPreset.CUSTOM": "Пользовательское", + "distanthorizons.config.enum.EQualityPreset.MINIMUM": "Минимальный", + "distanthorizons.config.enum.EQualityPreset.LOW": "Низкий", + "distanthorizons.config.enum.EQualityPreset.MEDIUM": "Средний", + "distanthorizons.config.enum.EQualityPreset.HIGH": "Высокий", + "distanthorizons.config.enum.EQualityPreset.EXTREME": "Экстремальный", + + "distanthorizons.config.enum.EThreadPreset.CUSTOM": "Пользовательское", + "distanthorizons.config.enum.EThreadPreset.MINIMAL_IMPACT": "Минимальное влияние", + "distanthorizons.config.enum.EThreadPreset.LOW_IMPACT": "Низкое влияние", + "distanthorizons.config.enum.EThreadPreset.BALANCED": "Среднее влияние", + "distanthorizons.config.enum.EThreadPreset.AGGRESSIVE": "Высокое влияние", + "distanthorizons.config.enum.EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU": "Я Заплатил За Весь ПРОЦЕССОР", + + "distanthorizons.config.enum.EMaxHorizontalResolution.BLOCK": "Блок", + "distanthorizons.config.enum.EMaxHorizontalResolution.TWO_BLOCKS": "2 блока", + "distanthorizons.config.enum.EMaxHorizontalResolution.FOUR_BLOCKS": "4 блока", + "distanthorizons.config.enum.EMaxHorizontalResolution.HALF_CHUNK": "Половина чанка", + "distanthorizons.config.enum.EMaxHorizontalResolution.CHUNK": "Чанк", + + "distanthorizons.config.enum.EVerticalQuality.HEIGHT_MAP": "Карта высот", + "distanthorizons.config.enum.EVerticalQuality.LOW": "Низкое", + "distanthorizons.config.enum.EVerticalQuality.MEDIUM": "Среднее", + "distanthorizons.config.enum.EVerticalQuality.HIGH": "Высокое", + "distanthorizons.config.enum.EVerticalQuality.EXTREME": "Экстремальное", + "distanthorizons.config.enum.EHorizontalQuality.LOWEST": "Самое низкое", + "distanthorizons.config.enum.EHorizontalQuality.LOW": "Низкое", + "distanthorizons.config.enum.EHorizontalQuality.MEDIUM": "Среднее", + "distanthorizons.config.enum.EHorizontalQuality.HIGH": "Высокое", + "distanthorizons.config.enum.EHorizontalQuality.EXTREME": "Экстремальное", + "distanthorizons.config.enum.EHorizontalQuality.UNLIMITED": "Неограниченное", + + "distanthorizons.config.enum.ETransparency.DISABLED": "Отключено", + "distanthorizons.config.enum.ETransparency.FAKE": "Фейковое", + "distanthorizons.config.enum.ETransparency.COMPLETE": "Полное", + + "distanthorizons.config.enum.EFogDistance.NEAR": "Близко", + "distanthorizons.config.enum.EFogDistance.FAR": "Далеко", + "distanthorizons.config.enum.EFogDistance.NEAR_AND_FAR": "Близко и далеко", + + "distanthorizons.config.enum.EFogDrawMode.USE_OPTIFINE_SETTING": "Использовать настройки мода", + "distanthorizons.config.enum.EFogDrawMode.FOG_ENABLED": "Включено", + "distanthorizons.config.enum.EFogDrawMode.FOG_DISABLED": "Выключено", + "distanthorizons.config.enum.EFogColorMode.USE_WORLD_FOG_COLOR": "Использовать цвет мира", + "distanthorizons.config.enum.EFogColorMode.USE_SKY_COLOR": "Использовать цвет неба", + + "distanthorizons.config.enum.EFogFalloff.LINEAR": "Линейное", + "distanthorizons.config.enum.EFogFalloff.EXPONENTIAL": "Экспоненциальное", + "distanthorizons.config.enum.EFogFalloff.EXPONENTIAL_SQUARED": "Экспоненциальное в квадрате", + + "distanthorizons.config.enum.EHeightFogMixMode.BASIC": "Базовое", + "distanthorizons.config.enum.EHeightFogMixMode.IGNORE_HEIGHT": "Игнорировать высоту", + "distanthorizons.config.enum.EHeightFogMixMode.ADDITION": "Сложение", + "distanthorizons.config.enum.EHeightFogMixMode.MAX": "Максимум", + "distanthorizons.config.enum.EHeightFogMixMode.MULTIPLY": "Умножение", + "distanthorizons.config.enum.EHeightFogMixMode.INVERSE_MULTIPLY": "Обратное умножение", + "distanthorizons.config.enum.EHeightFogMixMode.LIMITED_ADDITION": "Ограниченное сложение", + "distanthorizons.config.enum.EHeightFogMixMode.MULTIPLY_ADDITION": "Умножение и сложение", + "distanthorizons.config.enum.EHeightFogMixMode.INVERSE_MULTIPLY_ADDITION": "Обратное умножение и сложение", + "distanthorizons.config.enum.EHeightFogMixMode.AVERAGE": "Среднее", + + "distanthorizons.config.enum.EHeightFogMode.ABOVE_CAMERA": "Выше камеры", + "distanthorizons.config.enum.EHeightFogMode.BELOW_CAMERA": "Ниже камеры", + "distanthorizons.config.enum.EHeightFogMode.ABOVE_AND_BELOW_CAMERA": "Выше и ниже камеры", + "distanthorizons.config.enum.EHeightFogMode.ABOVE_SET_HEIGHT": "Выше заданной высоты", + "distanthorizons.config.enum.EHeightFogMode.BELOW_SET_HEIGHT": "Ниже заданной высоты", + "distanthorizons.config.enum.EHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT": "Выше и ниже заданной высоты", + + "distanthorizons.config.enum.EVanillaOverdraw.NEVER": "Никогда", + "distanthorizons.config.enum.EVanillaOverdraw.DYNAMIC": "Динамический", + "distanthorizons.config.enum.EVanillaOverdraw.ALWAYS": "Всегда", + + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.NONE": "Только существующие", + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY": "Только существующие части", + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.BIOME_ONLY": "Только биомы", + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.BIOME_ONLY_SIMULATE_HEIGHT": "Только биомы, моделировать высоту", + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.SURFACE": "Поверхность", + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.FEATURES": "Особенности", + "distanthorizons.config.enum.EDhApiDistantGeneratorMode.FULL": "Полный", + + "distanthorizons.config.enum.ELightGenerationMode.DISTANT_HORIZONS": "Далекие горизонты", + "distanthorizons.config.enum.ELightGenerationMode.MINECRAFT": "Майнкрафт", + + "distanthorizons.config.enum.EGenerationPriority.AUTO": "Авто", + "distanthorizons.config.enum.EGenerationPriority.NEAR_FIRST": "Ближние впереди", + "distanthorizons.config.enum.EGenerationPriority.BALANCED": "Сбалансировано", + "distanthorizons.config.enum.EGenerationPriority.FAR_FIRST": "Дальние впереди", + + "distanthorizons.config.enum.EBlocksToAvoid.NONE": "Нет", + "distanthorizons.config.enum.EBlocksToAvoid.NON_COLLIDING": "Без коллизий", + + "distanthorizons.config.enum.EOverdrawPrevention.NONE": "Нет", + "distanthorizons.config.enum.EOverdrawPrevention.LIGHT": "Легкая", + "distanthorizons.config.enum.EOverdrawPrevention.MEDIUM": "Средняя", + "distanthorizons.config.enum.EOverdrawPrevention.HEAVY": "Высокая", + + "distanthorizons.config.enum.EServerFolderNameMode.NAME_ONLY": "Только имя", + "distanthorizons.config.enum.EServerFolderNameMode.NAME_IP": "Имя и IP", + "distanthorizons.config.enum.EServerFolderNameMode.NAME_IP_PORT": "Имя, IP и порт", + "distanthorizons.config.enum.EServerFolderNameMode.NAME_IP_PORT_MC_VERSION": "Имя, IP, порт и версия MC", + + "distanthorizons.config.enum.ERendererMode.DEFAULT": "По умолчанию", + "distanthorizons.config.enum.ERendererMode.DEBUG": "Отладка", + "distanthorizons.config.enum.ERendererMode.DISABLED": "Отключено", + + "distanthorizons.config.enum.EDebugRendering.OFF": "Выкл.", + "distanthorizons.config.enum.EDebugRendering.SHOW_DETAIL": "Показать детали", + "distanthorizons.config.enum.EDebugRendering.SHOW_GENMODE": "Показать режим генерации", + "distanthorizons.config.enum.EDebugRendering.SHOW_OVERLAPPING_QUADS": "Показать перекрывающиеся квадраты", + "distanthorizons.config.enum.EDebugRendering.SHOW_RENDER_SOURCE_FLAG": "Показать флаг источника рендера", + + "distanthorizons.config.enum.EGLErrorHandlingMode.IGNORE": "Игнорировать", + "distanthorizons.config.enum.EGLErrorHandlingMode.LOG": "Логировать", + "distanthorizons.config.enum.EGLErrorHandlingMode.LOG_THROW": "Логировать и выбросить (THROW)", + + "distanthorizons.config.enum.EGlProfileMode.CORE": "Основной", + "distanthorizons.config.enum.EGlProfileMode.COMPAT": "Совместимый", + "distanthorizons.config.enum.EGlProfileMode.ANY": "Любой", + + "distanthorizons.config.enum.ELoggerMode.DISABLED": "Отключено", + "distanthorizons.config.enum.ELoggerMode.LOG_ALL_TO_FILE": "Файл: Все, Чат: Выкл.", + "distanthorizons.config.enum.ELoggerMode.LOG_ERROR_TO_CHAT": "Файл: Все, Чат: Ошибки", + "distanthorizons.config.enum.ELoggerMode.LOG_WARNING_TO_CHAT": "Файл: Все, Чат: Предупреждения", + "distanthorizons.config.enum.ELoggerMode.LOG_INFO_TO_CHAT": "Файл: Все, Чат: Информация", + "distanthorizons.config.enum.ELoggerMode.LOG_DEBUG_TO_CHAT": "Файл: Все, Чат: Отладка", + "distanthorizons.config.enum.ELoggerMode.LOG_ALL_TO_CHAT": "Файл: Все, Чат: Все", + "distanthorizons.config.enum.ELoggerMode.LOG_ERROR_TO_CHAT_AND_FILE": "Файл: Ошибки, Чат: Ошибки", + "distanthorizons.config.enum.ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE": "Файл: Предупреждения, Чат: Предупреждения", + "distanthorizons.config.enum.ELoggerMode.LOG_INFO_TO_CHAT_AND_FILE": "Файл: Информация, Чат: Информация", + "distanthorizons.config.enum.ELoggerMode.LOG_DEBUG_TO_CHAT_AND_FILE": "Файл: Отладка, Чат: Отладка", + "distanthorizons.config.enum.ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE": "Файл: Информация, Чат: Предупреждения", + "distanthorizons.config.enum.ELoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE": "Файл: Информация, Чат: Ошибки", + + "distanthorizons.config.enum.EGpuUploadMethod.AUTO": + "Авто", + "distanthorizons.config.enum.EGpuUploadMethod.BUFFER_STORAGE": + "Хранение буфера", + "distanthorizons.config.enum.EGpuUploadMethod.SUB_DATA": + "Данные SUB", + "distanthorizons.config.enum.EGpuUploadMethod.BUFFER_MAPPING": + "Отображение буфера", + "distanthorizons.config.enum.EGpuUploadMethod.DATA": + "Данные", + + "distanthorizons.config.enum.EBufferRebuildTimes.CONSTANT": + "Постоянно", + "distanthorizons.config.enum.EBufferRebuildTimes.FREQUENT": + "Часто", + "distanthorizons.config.enum.EBufferRebuildTimes.NORMAL": + "Обычно", + "distanthorizons.config.enum.EBufferRebuildTimes.RARE": + "Редко", + + "distanthorizons.config.enum.ELodShading.MINECRAFT": + "Майнкрафт-овское", + "distanthorizons.config.enum.ELodShading.OLD_LIGHTING": + "Старое освещение", + "distanthorizons.config.enum.ELodShading.NONE": + "Ничего" + +} From a5d0035f8187eb350e0cbfadbf6caa89e3434bad Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 22:05:14 -0500 Subject: [PATCH 14/16] refactor and optimize full/render file handlers --- .../fullDatafile/FullDataFileHandler.java | 15 +++----------- .../renderfile/RenderSourceFileHandler.java | 20 +++++++++---------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index e7ba34c06..b3bef09eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -55,7 +55,6 @@ public class FullDataFileHandler implements IFullDataSourceProvider protected static ConfigChangeListener configListener; private final ConcurrentHashMap loadedMetaFileBySectionPos = new ConcurrentHashMap<>(); - private final Set missingSectionPos = Collections.newSetFromMap(new ConcurrentHashMap<>()); protected final IDhLevel level; protected final File saveDir; @@ -131,14 +130,12 @@ public class FullDataFileHandler implements IFullDataSourceProvider } - // File does exist, but not loaded yet. + // check if the file exists, but hasn't been loaded File fileToLoad = this.computeDataFilePath(pos); if (fileToLoad.exists()) { synchronized (this) { - // A file exists, but isn't loaded yet. - // Double check locking for loading file, as loading file means also loading the metadata, which // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. @@ -160,17 +157,12 @@ public class FullDataFileHandler implements IFullDataSourceProvider LOGGER.error("Failed to read meta data file at " + fileToLoad + ": ", e); FileUtil.renameCorruptedFile(fileToLoad); } - finally - { - this.missingSectionPos.remove(pos); - } } } if (!allowCreateFile) { - this.missingSectionPos.add(pos); return null; } @@ -191,7 +183,6 @@ public class FullDataFileHandler implements IFullDataSourceProvider // This is a Compare And Swap with expected null value. FullDataMetaFile metaFileCas = this.loadedMetaFileBySectionPos.putIfAbsent(pos, metaFile); - this.missingSectionPos.remove(pos); return metaFileCas == null ? metaFile : metaFileCas; } @@ -262,9 +253,9 @@ public class FullDataFileHandler implements IFullDataSourceProvider if (CompleteFullDataSource.firstDataPosCanAffectSecond(basePos, childPos)) { // get or load the file if necessary - if (!this.loadedMetaFileBySectionPos.containsKey(childPos) && this.computeDataFilePath(childPos).exists()) + if (!this.loadedMetaFileBySectionPos.containsKey(childPos)) { - this.getLoadOrMakeFile(childPos, true); + this.getLoadOrMakeFile(childPos, false); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index 1ed0c2fcb..4ecba2fa8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -44,7 +44,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider private final ThreadPoolExecutor fileHandlerThreadPool; private final F3Screen.NestedMessage threadPoolMsg; - protected final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); //loadedMetaFileBySectionPos + protected final ConcurrentHashMap loadedMetaFileBySectionPos = new ConcurrentHashMap<>(); private final IDhClientLevel clientLevel; private final File saveDir; @@ -120,7 +120,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider /** @return null if there was an issue */ private RenderDataMetaFile getLoadOrMakeFile(DhSectionPos pos) { - RenderDataMetaFile metaFile = this.metaFileBySectionPos.get(pos); + RenderDataMetaFile metaFile = this.loadedMetaFileBySectionPos.get(pos); if (metaFile != null) { return metaFile; @@ -137,7 +137,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider // Double check locking for loading file, as loading file means also loading the metadata, which // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. - metaFile = this.metaFileBySectionPos.get(pos); + metaFile = this.loadedMetaFileBySectionPos.get(pos); if (metaFile != null) { return metaFile; // someone else loaded it already. @@ -147,7 +147,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { metaFile = RenderDataMetaFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, fileToLoad); this.topDetailLevelRef.updateAndGet(currentTopDetailLevel -> Math.max(currentTopDetailLevel, pos.getDetailLevel())); - this.metaFileBySectionPos.put(pos, metaFile); + this.loadedMetaFileBySectionPos.put(pos, metaFile); return metaFile; } catch (IOException e) @@ -175,7 +175,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); // This is a Compare And Swap with expected null value. - RenderDataMetaFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); + RenderDataMetaFile metaFileCas = this.loadedMetaFileBySectionPos.putIfAbsent(pos, metaFile); return (metaFileCas == null) ? metaFile : metaFileCas; } @@ -210,7 +210,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider for (int zOffset = 0; zOffset < width; zOffset++) { fileSectionPos.mutate(sectionDetailLevel, minSectionPos.getX() + xOffset, minSectionPos.getZ() + zOffset); - RenderDataMetaFile metaFile = this.metaFileBySectionPos.get(fileSectionPos); // bypass the getLoadOrMakeFile() since we only want cached files. + RenderDataMetaFile metaFile = this.loadedMetaFileBySectionPos.get(fileSectionPos); // bypass the getLoadOrMakeFile() since we only want cached files. if (metaFile != null) { metaFile.updateChunkIfSourceExistsAsync(chunk); @@ -230,7 +230,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider public CompletableFuture flushAndSaveAsync() { ArrayList> futures = new ArrayList<>(); - for (RenderDataMetaFile metaFile : this.metaFileBySectionPos.values()) + for (RenderDataMetaFile metaFile : this.loadedMetaFileBySectionPos.values()) { futures.add(metaFile.flushAndSaveAsync()); } @@ -249,7 +249,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { ArrayList lines = new ArrayList<>(); lines.add("Render Source File Handler [" + this.clientLevel.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); - lines.add(" Loaded files: " + this.metaFileBySectionPos.size()); + lines.add(" Loaded files: " + this.loadedMetaFileBySectionPos.size()); lines.add(" Thread pool tasks: " + this.fileHandlerThreadPool.getQueue().size() + " (completed: " + this.fileHandlerThreadPool.getCompletedTaskCount() + ")"); int totalFutures = this.taskTracker.size(); @@ -293,7 +293,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider @Override public void close() { - LOGGER.info("Closing " + this.getClass().getSimpleName() + " with [" + this.metaFileBySectionPos.size() + "] files..."); + LOGGER.info("Closing " + this.getClass().getSimpleName() + " with [" + this.loadedMetaFileBySectionPos.size() + "] files..."); this.fileHandlerThreadPool.shutdown(); this.threadPoolMsg.close(); } @@ -314,7 +314,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } // clear the cached files - this.metaFileBySectionPos.clear(); + this.loadedMetaFileBySectionPos.clear(); } From 752c4af67697f5a9e4d996dba9db2330472769bd Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 22:31:30 -0500 Subject: [PATCH 15/16] Add a todo comment --- .../core/file/fullDatafile/GeneratedFullDataFileHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index b6aaaf224..28a343e33 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -119,7 +119,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler //========// @Nullable - private CompletableFuture tryStartGenTask(FullDataMetaFile file, IIncompleteFullDataSource dataSource) + private CompletableFuture tryStartGenTask(FullDataMetaFile file, IIncompleteFullDataSource dataSource) // TODO after generation is finished, save and free any full datasources that aren't in use (IE high detail ones below the top) { IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); // breaks down the missing positions into the desired detail level that the gen queue could accept From c46e12fb7ab91583813bc3dc1f10a84be58dc9ed Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 23 Sep 2023 22:32:44 -0500 Subject: [PATCH 16/16] remove unused commented out code in FullDataMetaFile --- .../file/fullDatafile/FullDataMetaFile.java | 69 ------------------- 1 file changed, 69 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index c8b42fa8d..a0020773a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -633,75 +633,6 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I return completionFuture; } - /* - - private void applyWriteQueueAndSave(IFullDataSource fullDataSourceToUpdate) - { - boolean dataChanged = this.applyWriteQueueToFullDataSource(fullDataSourceToUpdate); - this.needsUpdate = false; - - // attempt to promote the data source - if (fullDataSourceToUpdate instanceof IIncompleteFullDataSource) - { - IFullDataSource newSource = ((IIncompleteFullDataSource) fullDataSourceToUpdate).tryPromotingToCompleteDataSource(); - dataChanged |= (newSource != fullDataSourceToUpdate); - fullDataSourceToUpdate = newSource; - } - - - // the provider may need to modify other files based on this data source changing - IFullDataSourceProvider.DataFileUpdateResult dataFileUpdateResult = this.fullDataSourceProvider.onDataFileUpdateAsync(fullDataSourceToUpdate, this, dataChanged); - IFullDataSource fullDataSource = dataFileUpdateResult.fullDataSource; - boolean dataSourceChanged = dataFileUpdateResult.dataSourceChanged; - - - // only save to file if something was changed - if (dataSourceChanged) - { - this.writeDataSource(fullDataSource); - } - - // keep track of non-null data sources - if (fullDataSource != null) - { - new DataObjTracker(fullDataSource); - new DataObjSoftTracker(this, fullDataSource); - } - - - boolean showFullDataFileStatus = Config.Client.Advanced.Debugging.DebugWireframe.showFullDataFileStatus.get(); - boolean showFullDataFileSampling = Config.Client.Advanced.Debugging.DebugWireframe.showFullDataFileSampling.get(); - if (showFullDataFileStatus || showFullDataFileSampling) - { - Color color; - if (this.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) - { - color = Color.GREEN; - } - else - { - color = Color.GREEN.darker().darker(); - } - - DebugRenderer.makeParticle(new DebugRenderer.BoxParticle( - new DebugRenderer.Box(this.pos, 64f, 72f, 0.03f, color), - 0.2, 32f)); - } - - - // save the updated data source - this.cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this, fullDataSource); - - - if (this.needsUpdate) - { - // another update was requested while this update was being processed - this.getOrLoadCachedDataSourceAsync(); - } - } - - */ - /** @return true if the queue was not empty and chunk data was applied to this meta file's {@link IFullDataSource}. */