From 8a39610b8c7025c54a469ef21a4f36f4b64f8d9f Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 14 Mar 2026 14:32:19 -0500 Subject: [PATCH] move beacon render enabling to LodQuadTree Fixes beacons not always showing/hiding correctly --- .../core/render/QuadTree/LodQuadTree.java | 173 ++++++++++-- .../render/QuadTree/LodRenderSection.java | 146 ---------- .../render/renderer/BeaconRenderHandler.java | 254 +++++++++--------- .../core/sql/dto/BeaconBeamDTO.java | 14 + 4 files changed, 291 insertions(+), 296 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java index 393e156a7..ce3dca614 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java @@ -38,6 +38,8 @@ import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; import com.seibel.distanthorizons.core.render.renderer.BeaconRenderHandler; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.WorldGenUtil; @@ -55,10 +57,7 @@ import org.jetbrains.annotations.Nullable; import javax.annotation.WillNotClose; import java.awt.*; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; @@ -97,8 +96,19 @@ public class LodQuadTree extends QuadTree implements IDebugRen private final AtomicBoolean queueThreadRunningRef = new AtomicBoolean(false); + /** + * contains the list of beacons currently being rendered in this section + * if this list is modified the {@link LodQuadTree#beaconRenderHandler} should be updated to match. + */ + private final ArrayList beaconList = new ArrayList<>(); @Nullable - public final BeaconRenderHandler beaconRenderHandler; + private final BeaconRenderHandler beaconRenderHandler; + @Nullable + private final BeaconBeamRepo beaconBeamRepo; + /** used to prevent updating the beacons concurrently */ + @NotNull + private CompletableFuture beaconUpdateFuture = CompletableFuture.completedFuture(null); + /** the smallest numerical detail level number that can be rendered */ private byte maxLeafRenderDetailLevel; @@ -149,6 +159,8 @@ public class LodQuadTree extends QuadTree implements IDebugRen IDhGenericRenderer genericObjectRenderer = this.level.getGenericRenderer(); this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null; + this.beaconBeamRepo = this.level.getBeaconBeamRepo(); + Config.Common.WorldGenerator.enableDistantGeneration.addListener(this); Config.Server.enableServerGeneration.addListener(this); @@ -367,7 +379,6 @@ public class LodQuadTree extends QuadTree implements IDebugRen if (node == null || node.value == null) { continue; } node.value.setRenderingEnabled(false); - node.value.tryDisableBeacons(); } for (QuadNode node : this.tickNodeHolder.getEnableDeleteChildrenNodes()) @@ -389,7 +400,6 @@ public class LodQuadTree extends QuadTree implements IDebugRen if (childRenderSection != null) { childRenderSection.setRenderingEnabled(false); - childRenderSection.tryDisableBeacons(); childRenderSection.close(); } }); @@ -405,21 +415,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen //=================// //region - // must be handled after beacon disabling - // otherwise the beacons will be missing - - for (QuadNode node : this.tickNodeHolder.getEnabledNodes()) - { - if (node == null || node.value == null) { continue; } - - node.value.tryEnableBeacons(); - } - for (QuadNode node : this.tickNodeHolder.getEnableDeleteChildrenNodes()) - { - if (node == null || node.value == null) { continue; } - - node.value.tryEnableBeacons(); - } + this.tryRefreshRenderingBeaconsAsync(playerPos); //endregion @@ -872,6 +868,133 @@ public class LodQuadTree extends QuadTree implements IDebugRen + //=================// + // beacon handling // + //=================// + //region beacon handling + + /** gets the active beacon list and stops/starts beacon rendering as necessary */ + private void tryRefreshRenderingBeaconsAsync(DhBlockPos2D playerPos) + { + // do nothing if beacon rendering or repos are unavailable + if (this.beaconBeamRepo == null + || this.beaconRenderHandler == null) + { + return; + } + + AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor(); + if (executor == null) + { + // shouldn't normally happen, but just in case + return; + } + + if (!this.beaconUpdateFuture.isDone()) + { + return; + } + CompletableFuture future = new CompletableFuture<>(); + this.beaconUpdateFuture = future; + + try + { + executor.execute(() -> + { + this.refreshRenderingBeacons(playerPos); + + try { Thread.sleep(2_000); } catch (InterruptedException ignore) { } + future.complete(null); + }); + } + catch (RejectedExecutionException e) + { + // the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back + future.completeExceptionally(e); + } + } + private void refreshRenderingBeacons(DhBlockPos2D playerPos) + { + if (this.beaconBeamRepo == null + || this.beaconRenderHandler == null) + { + return; + } + + try + { + // Synchronized to prevent two threads for accessing the array at once + synchronized (this.beaconList) + { + // get beacons // + + int blockDistanceRadius = (this.blockRenderDistanceDiameter / 2); + int minBlockPosX = playerPos.x - blockDistanceRadius; + int minBlockPosZ = playerPos.z - blockDistanceRadius; + int maxBlockPosX = playerPos.x + blockDistanceRadius; + int maxBlockPosZ = playerPos.z + blockDistanceRadius; + + ArrayList dbBeacons = this.beaconBeamRepo.getAllBeamsInBlockPosRange( + minBlockPosX, maxBlockPosX, + minBlockPosZ, maxBlockPosZ + ); + + + // convert DB beacons // + + ArrayList newBeaconList = new ArrayList<>(this.beaconList.size()); + for (BeaconBeamDTO beaconBeam : dbBeacons) + { + byte beaconDetailLevel = this.calcExpectedDetailLevel(playerPos, beaconBeam.blockPos.getX(), beaconBeam.blockPos.getZ()); + newBeaconList.add(new BeaconRenderHandler.BeaconBeamWithWidth(beaconBeam, beaconDetailLevel)); + } + + + boolean replaceBeacons = false; + if (this.beaconList.size() != newBeaconList.size()) + { + // lists are different sizes + replaceBeacons = true; + } + else + { + // sort the beacons so they can be compared + this.beaconList.sort(BeaconRenderHandler.NegativeInfiniteBlockPosComparator.INSTANCE); + newBeaconList.sort(BeaconRenderHandler.NegativeInfiniteBlockPosComparator.INSTANCE); + + for (int i = 0; i < this.beaconList.size(); i++) + { + BeaconRenderHandler.BeaconBeamWithWidth oldBeam = this.beaconList.get(i); + BeaconRenderHandler.BeaconBeamWithWidth newBeam = newBeaconList.get(i); + if (!oldBeam.equals(newBeam)) + { + replaceBeacons = true; + break; + } + } + } + + + // only replace the beacons if something changed + // this is done to prevent constantly re-uploading the render data + if (replaceBeacons) + { + this.beaconList.clear(); + this.beaconList.addAll(newBeaconList); + this.beaconRenderHandler.replaceRenderingBeacons(this.beaconList); + } + } + } + catch (Exception e) + { + LOGGER.error("Unexpected issue updating beacons, error: ["+e.getMessage()+"].", e); + } + } + + //endregion beacon handling + + + //====================// // detail level logic // //====================// @@ -885,9 +1008,11 @@ public class LodQuadTree extends QuadTree implements IDebugRen * @return detail level of this section pos */ public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos) + { return this.calcExpectedDetailLevel(playerPos, DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos)); } + public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, int targetBlockPosX, int targetBlockPosZ) { - double blockDistance = playerPos.dist(DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos)); - return this.calcDetailLevelFromDistance(blockDistance); + double blockDistance = playerPos.dist(targetBlockPosX, targetBlockPosZ); + return this.calcDetailLevelFromDistance(blockDistance); } private void updateDetailLevelVariables() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java index cf138c89d..59d7392f1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodRenderSection.java @@ -73,21 +73,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable private final FullDataSourceProviderV2 fullDataSourceProvider; private final LodQuadTree quadTree; - /** - * contains the list of beacons currently being rendered in this section - * if this list is modified the {@link LodRenderSection#beaconRenderHandler} should be updated to match. - */ - private final ArrayList activeBeaconList = new ArrayList<>(); - @Nullable - 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; private boolean beaconsRendering = false; @@ -134,9 +119,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable this.levelWrapper = level.getClientLevelWrapper(); this.fullDataSourceProvider = fullDataSourceProvider; - this.beaconRenderHandler = this.quadTree.beaconRenderHandler; - this.beaconBeamRepo = this.level.getBeaconBeamRepo(); - DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus); } @@ -188,8 +170,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable { try { - this.refreshActiveBeaconList(); - LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData(); if (lodQuadBuilder == null) { @@ -376,130 +356,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable - //=================// - // beacon handling // - //=================// - //region beacon handling - - /** gets the active beacon list and stops/starts beacon rendering as necessary */ - private void refreshActiveBeaconList() - { - try - { - this.beaconRenderHandlingLock.lock(); - - // 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 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() - { - try - { - 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 - DEBUG_RENDERER.makeParticle( - new AbstractDebugWireframeRenderer.BoxParticle( - new AbstractDebugWireframeRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()), - 0.2, 32f - ) - ); - } - - synchronized (this.activeBeaconList) - { - this.beaconRenderHandler.stopRenderingBeaconsInRange(this.pos); - } - } - finally - { - this.beaconRenderHandlingLock.unlock(); - } - } - - public void tryEnableBeacons() - { - try - { - 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); - } - } - finally - { - this.beaconRenderHandlingLock.unlock(); - } - } - - //endregion beacon handling - - - //==============// // base methods // //==============// @@ -562,8 +418,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } - this.tryDisableBeacons(); - if (this.renderBufferContainer != null) { this.renderBufferContainer.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/BeaconRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/BeaconRenderHandler.java index 4acdc5d01..17d48b108 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/BeaconRenderHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/BeaconRenderHandler.java @@ -46,7 +46,6 @@ import java.util.*; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Predicate; public class BeaconRenderHandler { @@ -57,8 +56,6 @@ public class BeaconRenderHandler /** how often should we check if a beacon should be culled? */ private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000; - private static final Comparator NEGATIVE_BLOCKPOS_COMPARATOR = new NegativeInfiniteBlockPosComparator(); - private final ReentrantLock updateLock = new ReentrantLock(); @@ -67,8 +64,6 @@ public class BeaconRenderHandler private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup; /** contains all beacons that could be rendered (including those that are being culled) */ private final ArrayList fullBeaconBoxList = new ArrayList<>(); - /** contains all beacons that could be rendered */ - private final HashSet fullBeaconBlockPosSet = new HashSet<>(); private boolean cullingThreadRunning = false; private boolean updateRenderDataNextFrame = false; @@ -96,126 +91,12 @@ public class BeaconRenderHandler - //=================// - // render handling // - //=================// + //===============// + // before render // + //===============// //region - public void startRenderingBeacons(ArrayList beaconList, byte detailLevel) - { - try - { - this.updateLock.lock(); - - - // how wide should each beacon be? - int beaconBlockWidth = 1; - if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get()) - { - beaconBlockWidth = DhSectionPos.getBlockWidth(detailLevel); - } - - - ArrayList sortedBeaconList = new ArrayList<>(beaconList); - - // merge distant beams if requested - if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get()) - { - // sort beacons from neg inf -> pos inf - // so we can consistently merge adjacent beacons - sortedBeaconList.sort(NEGATIVE_BLOCKPOS_COMPARATOR); - - // go through each beacon... - for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++) - { - BeaconBeamDTO outerBeacon = sortedBeaconList.get(outerIndex); - DhBlockPos outerBlockPos = outerBeacon.blockPos; - - // ...and remove any beacons that are within the block width to prevent overlaps - for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++) - { - BeaconBeamDTO beaconToMerge = sortedBeaconList.get(mergeIndex); - DhBlockPos mergeBlockPos = beaconToMerge.blockPos; - - int xDiff = mergeBlockPos.getX() - outerBlockPos.getX(); - int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ(); - - // merge (remove) this beacon if - // it's close to the outer beacon - if (xDiff < beaconBlockWidth - && zDiff < beaconBlockWidth) - { - sortedBeaconList.remove(mergeIndex); - mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top - } - } - } - } - - - //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.fullBeaconBlockPosSet.add(beacon.blockPos)) - { - // skip already present beacons - continue; - } - - - int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get(); - DhApiRenderableBox beaconBox = new DhApiRenderableBox( - new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()), - new DhApiVec3d(beacon.blockPos.getX() + beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beaconBlockWidth), - beacon.color, - EDhApiBlockMaterial.ILLUMINATED - ); - - this.activeBeaconBoxRenderGroup.add(beaconBox); - this.fullBeaconBoxList.add(beaconBox); - this.activeBeaconBoxRenderGroup.triggerBoxChange(); - } - } - finally - { - this.updateLock.unlock(); - } - } - - public void stopRenderingBeaconsInRange(long pos) - { - try - { - this.updateLock.lock(); - - Predicate removeBoxPredicate = (DhApiRenderableBox box) -> - { - 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); - - this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos)); - - this.activeBeaconBoxRenderGroup.triggerBoxChange(); - } - finally - { - this.updateLock.unlock(); - } - } - - - private void beforeRender(DhApiRenderParam renderEventParam) + private void beforeRender(DhApiRenderParam renderEventParam) { if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get()) { @@ -237,7 +118,7 @@ public class BeaconRenderHandler private void tryUpdateBeaconCullingAsync() { ThreadPoolExecutor executor = ThreadPoolUtil.getBeaconCullingExecutor(); - if (executor != null + if (executor != null && !this.cullingThreadRunning) { this.cullingThreadRunning = true; @@ -304,15 +185,136 @@ public class BeaconRenderHandler + //==============// + // registration // + //==============// + //region + + public void replaceRenderingBeacons(ArrayList beaconList) + { + try + { + this.updateLock.lock(); + + ArrayList sortedBeaconList = new ArrayList<>(beaconList); + + // merge distant beams if requested + if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get()) + { + // sort beacons from neg inf -> pos inf + // so we can consistently merge adjacent beacons + sortedBeaconList.sort(NegativeInfiniteBlockPosComparator.INSTANCE); + + // go through each beacon... + for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++) + { + BeaconBeamWithWidth outerBeacon = sortedBeaconList.get(outerIndex); + DhBlockPos outerBlockPos = outerBeacon.blockPos; + + // ...and remove any beacons that are within the block width to prevent overlaps + for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++) + { + BeaconBeamWithWidth beaconToMerge = sortedBeaconList.get(mergeIndex); + DhBlockPos mergeBlockPos = beaconToMerge.blockPos; + + int xDiff = mergeBlockPos.getX() - outerBlockPos.getX(); + int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ(); + + // merge (remove) this beacon if + // it's close to the outer beacon + if (xDiff < beaconToMerge.beaconBlockWidth + && zDiff < beaconToMerge.beaconBlockWidth) + { + sortedBeaconList.remove(mergeIndex); + mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top + } + } + } + } + + + this.activeBeaconBoxRenderGroup.clear(); + this.fullBeaconBoxList.clear(); + + // add each beacon to the renderer + for (int i = 0; i < sortedBeaconList.size(); i++) + { + BeaconBeamWithWidth beacon = sortedBeaconList.get(i); + int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get(); + DhApiRenderableBox beaconBox = new DhApiRenderableBox( + new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()), + new DhApiVec3d(beacon.blockPos.getX() + beacon.beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beacon.beaconBlockWidth), + beacon.color, + EDhApiBlockMaterial.ILLUMINATED + ); + + this.activeBeaconBoxRenderGroup.add(beaconBox); + this.fullBeaconBoxList.add(beaconBox); + } + + this.activeBeaconBoxRenderGroup.triggerBoxChange(); + } + finally + { + this.updateLock.unlock(); + } + } + + //endregion + + + //================// // helper classes // //================// //region - private static class NegativeInfiniteBlockPosComparator implements Comparator + public static class BeaconBeamWithWidth extends BeaconBeamDTO { + public final int beaconBlockWidth; + + public BeaconBeamWithWidth(BeaconBeamDTO beaconBeamDTO, byte lodDetailLevel) + { + super(beaconBeamDTO.blockPos, beaconBeamDTO.color); + + + // how wide should each beacon be? + if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get()) + { + this.beaconBlockWidth = DhSectionPos.getBlockWidth(lodDetailLevel); + } + else + { + this.beaconBlockWidth = 1; + } + } + + @Override + public boolean equals(Object obj) + { + if (obj == null + || obj.getClass() != this.getClass()) + { + return false; + } + + BeaconBeamWithWidth that = (BeaconBeamWithWidth) obj; + if (that.beaconBlockWidth != this.beaconBlockWidth) + { + return false; + } + + return super.equals(that); + } + + } + + public static class NegativeInfiniteBlockPosComparator implements Comparator + { + public static final NegativeInfiniteBlockPosComparator INSTANCE = new NegativeInfiniteBlockPosComparator(); + @Override - public int compare(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2) + public int compare(BeaconBeamWithWidth beacon1, BeaconBeamWithWidth beacon2) { DhBlockPos blockPos1 = beacon1.blockPos; DhBlockPos blockPos2 = beacon2.blockPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java index 4defbb131..5b2b8c2c9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java @@ -72,6 +72,20 @@ public class BeaconBeamDTO implements IBaseDTO, INetworkObject @Override public DhBlockPos getKey() { return this.blockPos; } + @Override + public boolean equals(Object obj) + { + if (obj == null + || obj.getClass() != this.getClass()) + { + return false; + } + + BeaconBeamDTO that = (BeaconBeamDTO)obj; + return this.blockPos.equals(that.blockPos) + && this.color.equals(that.color); + } + @Override public void close() { /* no closing needed */ }