Fix errors when rapidly changing config options

This commit is contained in:
James Seibel
2023-06-13 19:26:40 -05:00
parent 24a335565e
commit 98ee3f9e5f
2 changed files with 89 additions and 60 deletions
@@ -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. <br> <br>
*
* 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);
}
}
@@ -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<LodRenderSection> implements AutoClose
private final ConfigChangeListener<EHorizontalQuality> horizontalScaleChangeListener;
private ReentrantLock treeReadWriteLock = new ReentrantLock();
@@ -78,16 +80,24 @@ public class LodQuadTree extends QuadTree<LodRenderSection> 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<LodRenderSection> implements AutoClose
*/
public void clearRenderDataCache()
{
// TODO this causes some (harmless) file errors when called
LOGGER.info("Clearing render cache...");
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
while (nodeIterator.hasNext())
if (this.treeReadWriteLock.tryLock())
{
QuadNode<LodRenderSection> 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<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
while (nodeIterator.hasNext())
{
QuadNode<LodRenderSection> 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<LodRenderSection> 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<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
while (nodeIterator.hasNext())
{
QuadNode<LodRenderSection> quadNode = nodeIterator.next();
if (quadNode.value != null)
{
quadNode.value.disposeRenderData();
quadNode.value = null;
}
}
this.clearRenderDataCache();
}
@@ -386,19 +403,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> 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()
{