Fix beacons not reloading (and improve logic)

This commit is contained in:
James Seibel
2026-02-14 21:32:30 -06:00
parent 66a1d0296e
commit df52f41b87
4 changed files with 146 additions and 117 deletions
@@ -629,10 +629,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
continue;
}
// the section only needs to be updated if a buffer is currently present
LodRenderSection renderSection = this.tryGetValue(pos);
if (renderSection != null)
{
// the section only needs to be updated if a buffer is currently present
if (renderSection.gpuUploadComplete())
{
if (renderSection.gpuUploadInProgress()
@@ -50,9 +50,9 @@ import org.jetbrains.annotations.Nullable;
import javax.annotation.WillNotClose;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
* A render section represents an area that could be rendered.
@@ -82,6 +82,11 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
public final BeaconRenderHandler beaconRenderHandler;
@Nullable
public final BeaconBeamRepo beaconBeamRepo;
/**
* locking is necessary to prevent some weird threading issues
* causing beacons to appear/disappear at the wrong times.
*/
private final ReentrantLock beaconRenderHandlingLock = new ReentrantLock();
private boolean renderingEnabled = false;
@@ -386,76 +391,115 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
/** gets the active beacon list and stops/starts beacon rendering as necessary */
private void refreshActiveBeaconList()
{
// do nothing if beacon rendering or repos are unavailable
if (this.beaconBeamRepo == null
|| this.beaconRenderHandler == null)
try
{
return;
}
// Synchronized to prevent two threads for accessing the array at once
synchronized (this.activeBeaconList)
{
List<BeaconBeamDTO> activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos);
this.beaconRenderHandlingLock.lock();
// swap old and new active beacon list
this.activeBeaconList.clear();
this.activeBeaconList.addAll(activeBeacons);
// do nothing if beacon rendering or repos are unavailable
if (this.beaconBeamRepo == null
|| this.beaconRenderHandler == null)
{
return;
}
// Synchronized to prevent two threads for accessing the array at once
synchronized (this.activeBeaconList)
{
ArrayList<BeaconBeamDTO> activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos);
// swap old and new active beacon list
this.activeBeaconList.clear();
this.activeBeaconList.addAll(activeBeacons);
// if the beacons are currently rendering,
// re-create them so we can see any potential changes
if (this.beaconsRendering)
{
this.tryDisableBeacons();
this.tryEnableBeacons();
}
}
}
finally
{
this.beaconRenderHandlingLock.unlock();
}
}
public void tryDisableBeacons()
{
// do nothing if beacon rendering is unavailable
if (this.beaconRenderHandler == null)
try
{
return;
this.beaconRenderHandlingLock.lock();
// do nothing if beacon rendering is unavailable
if (this.beaconRenderHandler == null)
{
return;
}
if (!this.beaconsRendering)
{
return;
}
this.beaconsRendering = false;
if (Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus.get())
{
// show that this position has just been disabled
DebugRenderer.makeParticle(
new DebugRenderer.BoxParticle(
new DebugRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()),
0.2, 32f
)
);
}
synchronized (this.activeBeaconList)
{
this.beaconRenderHandler.stopRenderingBeaconsInRange(this.pos);
}
}
if (!this.beaconsRendering)
finally
{
return;
}
this.beaconsRendering = false;
if (Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus.get())
{
// show that this position has just been disabled
DebugRenderer.makeParticle(
new DebugRenderer.BoxParticle(
new DebugRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()),
0.2, 32f
)
);
}
synchronized (this.activeBeaconList)
{
this.beaconRenderHandler.stopRenderingBeacons(this.activeBeaconList);
this.beaconRenderHandlingLock.unlock();
}
}
public void tryEnableBeacons()
{
// do nothing if beacon rendering is unavailable
if (this.beaconRenderHandler == null)
try
{
return;
this.beaconRenderHandlingLock.lock();
// do nothing if beacon rendering is unavailable
if (this.beaconRenderHandler == null)
{
return;
}
if (this.beaconsRendering)
{
return;
}
this.beaconsRendering = true;
synchronized (this.activeBeaconList)
{
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
}
}
if (this.beaconsRendering)
finally
{
return;
}
this.beaconsRendering = true;
synchronized (this.activeBeaconList)
{
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
this.beaconRenderHandlingLock.unlock();
}
}
@@ -60,9 +60,12 @@ public class BeaconRenderHandler
private final ReentrantLock updateLock = new ReentrantLock();
private final IDhApiRenderableBoxGroup beaconBoxGroup;
/** only contains the beacons currently being rendered (culled beacons will be missing) */
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
/** contains all beacons that could be rendered (including those that are being culled) */
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
private final HashSet<DhBlockPos> beaconBlockPosSet = new HashSet<>();
/** contains all beacons that could be rendered */
private final HashSet<DhBlockPos> fullBeaconBlockPosSet = new HashSet<>();
private boolean cullingThreadRunning = false;
private boolean updateRenderDataNextFrame = false;
@@ -72,24 +75,28 @@ public class BeaconRenderHandler
//=============//
// constructor //
//=============//
//region
public BeaconRenderHandler(@NotNull GenericObjectRenderer renderer)
{
this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0));
this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setSsaoEnabled(false);
this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
this.beaconBoxGroup.setPreRenderFunc(this::beforeRender);
this.activeBeaconBoxRenderGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0));
this.activeBeaconBoxRenderGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.activeBeaconBoxRenderGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.activeBeaconBoxRenderGroup.setSsaoEnabled(false);
this.activeBeaconBoxRenderGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
this.activeBeaconBoxRenderGroup.setPreRenderFunc(this::beforeRender);
renderer.add(this.beaconBoxGroup);
renderer.add(this.activeBeaconBoxRenderGroup);
}
//endregion
//=================//
// render handling //
//=================//
//region
public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
{
@@ -143,12 +150,15 @@ public class BeaconRenderHandler
}
//LOGGER.info("startRenderingBeacons ["+sortedBeaconList+"]");
// add each beacon to the renderer
for (int i = 0; i < sortedBeaconList.size(); i++)
{
BeaconBeamDTO beacon = sortedBeaconList.get(i);
if (!this.beaconBlockPosSet.add(beacon.blockPos))
if (!this.fullBeaconBlockPosSet.add(beacon.blockPos))
{
// skip already present beacons
continue;
}
@@ -161,9 +171,9 @@ public class BeaconRenderHandler
EDhApiBlockMaterial.ILLUMINATED
);
this.beaconBoxGroup.add(beaconBox);
this.activeBeaconBoxRenderGroup.add(beaconBox);
this.fullBeaconBoxList.add(beaconBox);
this.beaconBoxGroup.triggerBoxChange();
this.activeBeaconBoxRenderGroup.triggerBoxChange();
}
}
finally
@@ -172,58 +182,28 @@ public class BeaconRenderHandler
}
}
public void stopRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList)
public void stopRenderingBeaconsInRange(long pos)
{
try
{
this.updateLock.lock();
for (int i = 0; i < beaconList.size(); i++)
Predicate<DhApiRenderableBox> removeBoxPredicate = (DhApiRenderableBox box) ->
{
BeaconBeamDTO beacon = beaconList.get(i);
DhBlockPos beaconPos = beacon.blockPos;
if (!this.beaconBlockPosSet.remove(beaconPos))
{
continue;
}
Predicate<DhApiRenderableBox> removePredicate = (DhApiRenderableBox box) ->
{
return box.minPos.x == beaconPos.getX()
&& box.minPos.y == beaconPos.getY() + 1 // plus 1 because the beam starts above the beacon
&& box.minPos.z == beaconPos.getZ();
};
this.beaconBoxGroup.removeIf(removePredicate);
this.fullBeaconBoxList.removeIf(removePredicate);
this.beaconBoxGroup.triggerBoxChange();
}
}
finally
{
this.updateLock.unlock();
}
}
public void updateBeaconColor(BeaconBeamDTO newBeam)
{
try
{
this.updateLock.lock();
DhBlockPos blockPos = new DhBlockPos((int)box.minPos.x, (int)box.minPos.y, (int)box.minPos.z);
boolean contains = DhSectionPos.contains(pos, blockPos);
//if (contains)
//{
// LOGGER.info("stopRenderingBeaconsInRange ["+DhSectionPos.toString(pos)+"] ["+blockPos+"]");
//}
return contains;
};
this.activeBeaconBoxRenderGroup.removeIf(removeBoxPredicate);
this.fullBeaconBoxList.removeIf(removeBoxPredicate);
DhBlockPos pos = newBeam.blockPos;
for (int i = 0; i < this.fullBeaconBoxList.size(); i++)
{
DhApiRenderableBox box = this.fullBeaconBoxList.get(i);
if (box.minPos.x == pos.getX()
&& box.minPos.y == pos.getY() + 1 // plus 1 because the beam starts above the beacon
&& box.minPos.z == pos.getZ())
{
box.color = newBeam.color;
this.beaconBoxGroup.triggerBoxChange();
break;
}
}
this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos));
this.activeBeaconBoxRenderGroup.triggerBoxChange();
}
finally
{
@@ -245,10 +225,10 @@ public class BeaconRenderHandler
// this must be called on the render thread to prevent concurrency issues
if (this.updateRenderDataNextFrame)
{
this.beaconBoxGroup.triggerBoxChange();
this.activeBeaconBoxRenderGroup.triggerBoxChange();
this.updateRenderDataNextFrame = false;
}
this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get());
this.activeBeaconBoxRenderGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get());
}
/** does nothing if the culling thread is already running */
private void tryUpdateBeaconCullingAsync()
@@ -284,7 +264,7 @@ public class BeaconRenderHandler
// Clear the existing box group so we can re-populate it.
// Since the box group is only used when we trigger an update, clearing it here
// and repopulating it is fine.
this.beaconBoxGroup.clear();
this.activeBeaconBoxRenderGroup.clear();
// While iterating over every beacon isn't a great way of doing this,
// when 940 beacons were tested this only took ~0.9 Milliseconds, so as long as
@@ -295,7 +275,7 @@ public class BeaconRenderHandler
double distance = Vec3d.getHorizontalDistance(cameraPos, box.minPos);
if (distance > dhFadeDistance)
{
this.beaconBoxGroup.add(box);
this.activeBeaconBoxRenderGroup.add(box);
}
}
@@ -317,11 +297,14 @@ public class BeaconRenderHandler
}
}
//endregion
//================//
// helper classes //
//================//
//region
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
{
@@ -340,6 +323,8 @@ public class BeaconRenderHandler
}
}
//endregion
}
@@ -181,7 +181,7 @@ public class BeaconBeamRepo extends AbstractDhRepo<DhBlockPos, BeaconBeamDTO>
);
}
public List<BeaconBeamDTO> getAllBeamsForPos(long pos)
public ArrayList<BeaconBeamDTO> getAllBeamsForPos(long pos)
{
int minBlockX = DhSectionPos.getMinCornerBlockX(pos);
int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos);
@@ -200,7 +200,7 @@ public class BeaconBeamRepo extends AbstractDhRepo<DhBlockPos, BeaconBeamDTO>
"WHERE " +
"? <= BlockPosX AND BlockPosX <= ? AND " +
"? <= BlockPosZ AND BlockPosZ <= ?";
public List<BeaconBeamDTO> getAllBeamsInBlockPosRange(
public ArrayList<BeaconBeamDTO> getAllBeamsInBlockPosRange(
int minBlockX, int maxBlockX,
int minBlockZ, int maxBlockZ
)