diff --git a/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java b/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java
index 3417b9438..9374426c2 100644
--- a/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java
+++ b/core/src/main/java/com/seibel/lod/core/config/eventHandlers/RenderCacheConfigEventHandler.java
@@ -7,14 +7,15 @@ import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.config.listeners.IConfigListener;
import com.seibel.lod.core.util.DetailDistanceUtil;
+import java.sql.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* Listens to the config and will automatically
* clear the current render cache if certain settings are changed.
*
* Note: if additional settings should clear the render cache, add those to this listener, don't create a new listener
- *
- * @author James Seibel
- * @version 2023-2-9
*/
public class RenderCacheConfigEventHandler implements IConfigListener
{
@@ -24,6 +25,9 @@ public class RenderCacheConfigEventHandler implements IConfigListener
private EVerticalQuality previousVerticalQualitySetting = null;
private EHorizontalResolution previousHorizontalResolution = null;
+ /** how long to wait in milliseconds before applying the config changes */
+ private static final long TIMEOUT_IN_MS = 1000L;
+ private Timer cacheClearingTimer;
/** private since we only ever need one handler at a time */
@@ -33,7 +37,7 @@ public class RenderCacheConfigEventHandler implements IConfigListener
@Override
public void onConfigValueSet()
- {
+ {
// confirm a setting was actually changed
boolean refreshRenderData = false;
@@ -56,9 +60,7 @@ public class RenderCacheConfigEventHandler implements IConfigListener
if (refreshRenderData)
{
- // TODO add a timeout to prevent rapidly changing settings causing the render data thrashing.
- DetailDistanceUtil.updateSettings();
- DhApiMain.Delayed.renderProxy.clearRenderDataCache();
+ this.refreshRenderDataAfterTimeout();
}
}
@@ -66,4 +68,27 @@ public class RenderCacheConfigEventHandler implements IConfigListener
@Override
public void onUiModify() { /* do nothing, we only care about modified config values */ }
+
+ /** Calling this method multiple times will reset the timer */
+ private void refreshRenderDataAfterTimeout()
+ {
+ // stop the previous timer if one exists
+ if (this.cacheClearingTimer != null)
+ {
+ this.cacheClearingTimer.cancel();
+ }
+
+ // create a new timer task
+ TimerTask timerTask = new TimerTask()
+ {
+ public void run()
+ {
+ DetailDistanceUtil.updateSettings();
+ DhApiMain.Delayed.renderProxy.clearRenderDataCache();
+ }
+ };
+ this.cacheClearingTimer = new Timer("RenderCacheConfig-Timeout-Timer");
+ this.cacheClearingTimer.schedule(timerTask, TIMEOUT_IN_MS);
+ }
+
}
diff --git a/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java
index a3e813222..2160fbcd0 100644
--- a/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java
+++ b/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java
@@ -15,8 +15,9 @@ import com.seibel.lod.core.util.objects.quadTree.QuadTree;
import org.apache.logging.log4j.Logger;
import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
/**
* This quadTree structure is our core data structure and holds
@@ -41,6 +42,7 @@ public class LodQuadTree extends QuadTree implements AutoClose
private final ConfigChangeListener horizontalScaleChangeListener;
+ private ReentrantLock treeReadWriteLock = new ReentrantLock();
@@ -78,16 +80,24 @@ public class LodQuadTree extends QuadTree implements AutoClose
}
- try
+ // don't traverse the tree if it is being modified
+ if (this.treeReadWriteLock.tryLock())
{
- // recenter if necessary, removing out of bounds sections
- this.setCenterBlockPos(playerPos, LodRenderSection::dispose);
-
- updateAllRenderSections(playerPos);
- }
- catch (Exception e)
- {
- LOGGER.error("Quad Tree tick exception for dimension: "+this.level.getClientLevelWrapper().getDimensionType().getDimensionName()+", exception: "+e.getMessage(), e);
+ try
+ {
+ // recenter if necessary, removing out of bounds sections
+ this.setCenterBlockPos(playerPos, LodRenderSection::dispose);
+
+ this.updateAllRenderSections(playerPos);
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + ", exception: " + e.getMessage(), e);
+ }
+ finally
+ {
+ this.treeReadWriteLock.unlock();
+ }
}
}
private void updateAllRenderSections(DhBlockPos2D playerPos)
@@ -317,24 +327,46 @@ public class LodQuadTree extends QuadTree implements AutoClose
*/
public void clearRenderDataCache()
{
- // TODO this causes some (harmless) file errors when called
- LOGGER.info("Clearing render cache...");
-
- Iterator> nodeIterator = this.nodeIterator();
- while (nodeIterator.hasNext())
+ if (this.treeReadWriteLock.tryLock())
{
- QuadNode quadNode = nodeIterator.next();
- if (quadNode.value != null)
+ try
{
- quadNode.value.disposeRenderData();
- quadNode.value = null;
+ LOGGER.info("Clearing render cache...");
+
+
+ // changing this while the tree is being traversed can cause (harmless) errors,
+ // where the traversal goes deeper into the tree than it should.
+ // so it should also be inside the tree lock
+ DetailDistanceUtil.updateSettings();
+
+
+ // clear the tree
+ Iterator> nodeIterator = this.nodeIterator();
+ while (nodeIterator.hasNext())
+ {
+ QuadNode quadNode = nodeIterator.next();
+ if (quadNode.value != null)
+ {
+ quadNode.value.disposeRenderData();
+ quadNode.value = null;
+ }
+ }
+
+ // delete the cache files
+ // TODO this will only delete the files for this level/world
+ this.renderSourceProvider.deleteRenderCache();
+
+ LOGGER.info("Render cache invalidated, please wait a moment for everything to reload...");
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Unexpected error when clearing LodQuadTree render cache: " + e.getMessage(), e);
+ }
+ finally
+ {
+ this.treeReadWriteLock.unlock();
}
}
-
- // delete the cache files
- this.renderSourceProvider.deleteRenderCache();
-
- LOGGER.info("Render cache invalidated");
}
/**
@@ -362,22 +394,7 @@ public class LodQuadTree extends QuadTree implements AutoClose
private void onHorizontalQualityChange()
{
- // TODO this Util should probably be somewhere else or handled differently, but it works for now
- // Updating this util is necessary whenever the horizontal quality is changed, since it handles the detail drop-off
- DetailDistanceUtil.updateSettings();
-
-
- // flush the current render data to make sure the new settings are used
- Iterator> nodeIterator = this.nodeIterator();
- while (nodeIterator.hasNext())
- {
- QuadNode quadNode = nodeIterator.next();
- if (quadNode.value != null)
- {
- quadNode.value.disposeRenderData();
- quadNode.value = null;
- }
- }
+ this.clearRenderDataCache();
}
@@ -386,19 +403,6 @@ public class LodQuadTree extends QuadTree implements AutoClose
// base methods //
//==============//
-// public String getDebugString()
-// {
-// StringBuilder sb = new StringBuilder();
-// for (byte i = 0; i < this.renderSectionRingLists.length; i++)
-// {
-// sb.append("Layer ").append(i + TREE_LOWEST_DETAIL_LEVEL).append(":\n");
-// sb.append(this.renderSectionRingLists[i].toDetailString());
-// sb.append("\n");
-// sb.append("\n");
-// }
-// return sb.toString();
-// }
-
@Override
public void close()
{