diff --git a/.gitmodules b/.gitmodules index 43aaadcf..ef2aaf96 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "logging"] path = logging url = https://github.com/Cubicake/CubiLogging.git +[submodule "leafpile"] + path = leafpile + url = https://github.com/Cubicake/ParallelepipedLeafPile.git + branch = java-21-main diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 387a5adb..ae5ee5d2 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -22,7 +22,10 @@ dependencies { compileOnly("org.spongepowered:configurate-core:4.2.0") compileOnly("org.spongepowered:configurate-yaml:4.2.0") compileOnly("it.unimi.dsi:fastutil:8.5.18") + implementation(project(":locatable-lib")) + + compileOnly(project(":leafpile")) compileOnly(project(":logging")) testImplementation(platform("org.junit:junit-bom:5.10.2")) @@ -32,7 +35,7 @@ dependencies { testImplementation("it.unimi.dsi:fastutil:8.5.18") } -val coreVersion = "0.5.2-SNAPSHOT" +val coreVersion = "0.5.4-SNAPSHOT" val isRelease = gradle.startParameter.taskNames.any { it.contains("buildRelease") diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/engine/SimpleEngine.java b/core/src/main/java/games/cubi/raycastedantiesp/core/engine/SimpleEngine.java index 6e550941..03be93fe 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/engine/SimpleEngine.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/engine/SimpleEngine.java @@ -1,5 +1,6 @@ package games.cubi.raycastedantiesp.core.engine; +import games.cubi.locatables.ImmutableLocatable; import games.cubi.locatables.Locatable; import games.cubi.logs.Logger; import games.cubi.raycastedantiesp.core.config.ConfigManager; @@ -324,6 +325,9 @@ private void processTickForPlayers(List playerDataList, EntityConfig boolean debugParticles, int currentTick, TickTimingBatch timings) { for (PlayerData playerData : playerDataList) { + if (!playerData.isConnected()) { + continue; + } playerData.nettyData().markPendingPostSpawnTasksForEviction(); if (playerData.hasBypassPermission()) { timings.incrementBypassSkippedPlayers(); @@ -333,7 +337,7 @@ private void processTickForPlayers(List playerDataList, EntityConfig BlockView blockView = playerData.blockView(); Locatable playerLocation = playerData.ownLocation(); - if (playerLocation == null) { + if (playerLocation == null || playerLocation.world() == null) { timings.incrementNullLocationSkippedPlayers(); continue; } @@ -360,24 +364,23 @@ private void processTickForPlayers(List playerDataList, EntityConfig private void checkEntities(PlayerData player, Locatable playerLocation, EntityConfig entityConfig, boolean debugParticles, BlockView blockView, int currentTick, TickTimingBatch timings) { EntityView entityView = player.entityView(); - int checked = entityView.forEachNeedingRecheck(entityConfig.getVisibleRecheckIntervalTicks(), currentTick, entityUUID -> { - boolean wasVisible = entityView.isVisible(entityUUID); - //todo Instead of passing by UUID reference everywhere (attachedToSelf and getLocation) have forEachNeedingRecheck pass the entity reference itself. - if (attachedToSelf(player, entityView, entityUUID, currentTick)) { + int checked = entityView.forEachNeedingRecheckEntity(entityConfig.getVisibleRecheckIntervalTicks(), currentTick, !(timings instanceof TickTimingBatchNoOp), entity -> { + boolean wasVisible = entity.visible(); + if (attachedToSelf(player, entityView, entity, currentTick)) { return; } - Locatable entityLocation = entityView.getLocation(entityUUID); + ImmutableLocatable entityLocation = entity.getOffsetEntityLocation(); if (entityLocation == null) { timings.incrementEntityNullTargets(); Logger.debug("SimpleEngine.checkEntities skipped-null-location viewer=" + player.getPlayerUUID() - + " target=" + entityUUID + + " target=" + entity.entityUUID() + " wasVisible=" + wasVisible + " tick=" + currentTick); return; } timings.incrementEntityRaycasts(); boolean canSee = RaycastUtil.raycast(player, playerLocation, entityLocation, entityConfig.getMaxOccludingCount(), entityConfig.getAlwaysShowRadius(), entityConfig.getRaycastRadius(), debugParticles, blockView, 1, particleSpawner); - entityView.setVisibility(entityUUID, canSee, currentTick); + entityView.setVisibility(entity, canSee, currentTick); }); timings.addEntityChecked(checked); } @@ -385,32 +388,28 @@ private void checkEntities(PlayerData player, Locatable playerLocation, EntityCo private void checkPlayers(PlayerData player, Locatable playerLocation, PlayerConfig playerConfig, boolean debugParticles, BlockView blockView, int currentTick, TickTimingBatch timings) { EntityView playerView = player.playerView(); - int checked = playerView.forEachNeedingRecheck(playerConfig.getVisibleRecheckIntervalTicks(), currentTick, otherPlayerUUID -> { - boolean wasVisible = playerView.isVisible(otherPlayerUUID); - if (attachedToSelf(player, playerView, otherPlayerUUID, currentTick)) { + int checked = playerView.forEachNeedingRecheckEntity(playerConfig.getVisibleRecheckIntervalTicks(), currentTick, !(timings instanceof TickTimingBatchNoOp), otherPlayer -> { + boolean wasVisible = otherPlayer.visible(); + if (attachedToSelf(player, playerView, otherPlayer, currentTick)) { return; } - Locatable otherPlayerLocation = playerView.getLocation(otherPlayerUUID); + ImmutableLocatable otherPlayerLocation = otherPlayer.getOffsetEntityLocation(); if (otherPlayerLocation == null) { timings.incrementPlayerNullTargets(); Logger.debug("SimpleEngine.checkPlayers skipped-null-location viewer=" + player.getPlayerUUID() - + " target=" + otherPlayerUUID + + " target=" + otherPlayer.entityUUID() + " wasVisible=" + wasVisible + " tick=" + currentTick); return; } timings.incrementPlayerRaycasts(); boolean canSee = RaycastUtil.raycast(player, playerLocation, otherPlayerLocation, playerConfig.getMaxOccludingCount(), playerConfig.getAlwaysShowRadius(), playerConfig.getRaycastRadius(), debugParticles, blockView, 1, particleSpawner); - playerView.setVisibility(otherPlayerUUID, canSee, currentTick); + playerView.setVisibility(otherPlayer, canSee, currentTick); }); timings.addPlayerChecked(checked); } - private boolean attachedToSelf(PlayerData player, EntityView view, UUID entityUUID, int currentTick) { - if (!(view.getEntity(entityUUID) instanceof NettyEntityLocatable entity)) { - Logger.error(new RuntimeException("EntityView returned a non-NettyEntityLocatable for UUID=" + entityUUID + " when checking for self-attachment. This should never happen."), 1, SimpleEngine.class); - return false; - } + private boolean attachedToSelf(PlayerData player, EntityView view, NettyEntityLocatable entity, int currentTick) { int selfEntityID = player.nettyData().getSelfEntityID(); if (!player.nettyData().isSelfEntityID(entity.leashingEntity()) && !player.nettyData().isSelfEntityID(entity.vehicleID()) @@ -422,20 +421,19 @@ private boolean attachedToSelf(PlayerData player, EntityView view, UUID entit } private void checkTileEntities(PlayerData player, Locatable playerLocation, TileEntityConfig tileEntityConfig, boolean debugParticles, BlockView blockView, int currentTick, TickTimingBatch timings) { - int checked = blockView.forEachNeedingRecheck(tileEntityConfig.getVisibleRecheckIntervalTicks(), currentTick, tileEntityLocation -> { + int checked = blockView.updateVisibilityForEachNeedingRecheck(tileEntityConfig.getVisibleRecheckIntervalTicks(), currentTick, tileEntityLocation -> { if (tileEntityLocation.world() == null || !tileEntityLocation.world().equals(playerLocation.world())) { timings.incrementTileWorldSkipped(); - return; + return BlockView.VisibilityResolver.SKIPPED; } if (playerLocation.distanceSquared(tileEntityLocation) > (double) tileEntityConfig.getRaycastRadius() * tileEntityConfig.getRaycastRadius()) { timings.incrementTileRadiusSkipped(); - blockView.setVisibility(tileEntityLocation, false, currentTick); - return; + return BlockView.VisibilityResolver.HIDE; } timings.incrementTileRaycasts(); boolean canSee = RaycastUtil.raycast(player, playerLocation, tileEntityLocation, tileEntityConfig.getMaxOccludingCount() + 1, tileEntityConfig.getAlwaysShowRadius(), tileEntityConfig.getRaycastRadius(), debugParticles, blockView, 1, particleSpawner); - blockView.setVisibility(tileEntityLocation, canSee, currentTick); + return canSee ? BlockView.VisibilityResolver.SHOW : BlockView.VisibilityResolver.HIDE; }); timings.addTileChecked(checked); } diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/EntityLocatable.java b/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/EntityLocatable.java index 3c2d27c9..3cee8b07 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/EntityLocatable.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/EntityLocatable.java @@ -1,5 +1,6 @@ package games.cubi.raycastedantiesp.core.locatables; +import games.cubi.locatables.ImmutableLocatable; import games.cubi.locatables.MutableLocatable; import org.jetbrains.annotations.Nullable; @@ -62,6 +63,8 @@ public interface EntityLocatable extends MutableLo PacketReplayData packetReplayData(); EntityLocatable setPacketReplayData(PacketReplayData packetReplayData); + ImmutableLocatable getOffsetEntityLocation(); + /** * For use when the player disconnects, clears all data. */ diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/NettyEntityLocatable.java b/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/NettyEntityLocatable.java index 2e898483..1bad380a 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/NettyEntityLocatable.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/locatables/NettyEntityLocatable.java @@ -1,7 +1,8 @@ package games.cubi.raycastedantiesp.core.locatables; -import games.cubi.locatables.Locatable; +import games.cubi.locatables.ImmutableLocatable; import games.cubi.locatables.MutableLocatable; +import games.cubi.locatables.implementations.ImmutableLocatableImpl; import games.cubi.locatables.implementations.MutableLocatableImpl; import games.cubi.raycastedantiesp.core.players.PlayerData; import games.cubi.raycastedantiesp.core.utils.Clearable; @@ -33,10 +34,10 @@ public abstract class NettyEntityLocatable selfEntity; - private int selfEntityID = NO_SELF_ENTITY_ID; + private final NettyEntityLocatable selfEntity; + private final int selfEntityID; - public NettyEntityLocatable getSelfEntity() { - return selfEntity; + public NettyData(NettyEntityLocatable selfEntity) { + this.selfEntity = selfEntity; + this.selfEntityID = selfEntity.entityID(); } - public NettyData setSelfEntity(NettyEntityLocatable selfEntity) { - if (this.selfEntity != null && this.selfEntity != selfEntity) { - this.selfEntity.clear(); - } - this.selfEntity = selfEntity; - selfEntityID = selfEntity == null ? NO_SELF_ENTITY_ID : selfEntity.entityID(); - return this; + public NettyEntityLocatable getSelfEntity() { + return selfEntity; } public int getSelfEntityID() { @@ -271,7 +266,7 @@ public int getSelfEntityID() { } public boolean isSelfEntityID(int entityID) { - return selfEntityID != NO_SELF_ENTITY_ID && entityID == selfEntityID; + return entityID == selfEntityID; } // // END Self entity tracking. @@ -360,8 +355,6 @@ public void clear() { if (selfEntity != null) { selfEntity.clear(); } - selfEntity = null; - selfEntityID = NO_SELF_ENTITY_ID; currentWorldMinHeight = Integer.MIN_VALUE; currentWorldName = null; } diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerData.java b/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerData.java index 3485d674..b56a3148 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerData.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerData.java @@ -15,6 +15,7 @@ public class PlayerData { private final UUID playerUUID; private final int joinTick; private volatile boolean hasBypassPermission; + private volatile boolean connected = true; private final ThreadSafeLocatable ownLocation; private final BlockView blockView; @@ -22,20 +23,22 @@ public class PlayerData { private final EntityView playerView; private final NettyData nettyData; - public PlayerData(UUID player, boolean hasBypassPermission, int joinTick) { - this(player, joinTick); - this.hasBypassPermission = hasBypassPermission; - } - - public PlayerData(UUID player, int joinTick) { + PlayerData(UUID player, boolean hasBypassPermission, int joinTick, int selfEntityID, PlayerRegistry.SelfEntityCreator selfEntityCreator) { this.joinTick = joinTick; this.playerUUID = player; + this.hasBypassPermission = hasBypassPermission; blockView = ViewRegistry.createBlockView(); entityView = ViewRegistry.createEntityView(); playerView = ViewRegistry.createPlayerEntityView(); - nettyData = new NettyData(); ownLocation = new ThreadSafeLocatable(null, 0, 0, 0); + NettyEntityLocatable selfEntity = Logger.requireNonNull( + selfEntityCreator.createSelfEntity(this, selfEntityID, player), + "Self entity creator returned null", + 3, + PlayerData.class + ); + nettyData = new NettyData(selfEntity); } public EntityView entityView() { @@ -75,6 +78,14 @@ public int getJoinTick() { return joinTick; } + public boolean isConnected() { + return connected; + } + + public void markDisconnected() { + connected = false; + } + /** * @return Either the entity or player view for this player, depending on the entity ID */ diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerRegistry.java b/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerRegistry.java index 45208a4f..a2709536 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerRegistry.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/players/PlayerRegistry.java @@ -1,11 +1,19 @@ package games.cubi.raycastedantiesp.core.players; +import games.cubi.locatables.Locatable; +import games.cubi.raycastedantiesp.core.locatables.NettyEntityLocatable; + import java.util.Collection; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class PlayerRegistry { + @FunctionalInterface + public interface SelfEntityCreator { + NettyEntityLocatable createSelfEntity(PlayerData playerData, int selfEntityID, UUID playerUUID); + } + private static PlayerRegistry instance; private PlayerRegistry() {} @@ -19,32 +27,20 @@ public static PlayerRegistry getInstance() { private final ConcurrentHashMap playerDataMap = new ConcurrentHashMap<>(); - public void registerPlayerIfAbsent(UUID playerUUID, boolean hasBypassPermission, int joinTick) { - playerDataMap.putIfAbsent(playerUUID, new PlayerData(playerUUID, hasBypassPermission, joinTick)); - } - /** Forcefully registers a player and returns the new PlayerData, even if they were already registered.**/ - public PlayerData registerAndGetPlayer(UUID playerUUID, int joinTick) { - PlayerData newData = new PlayerData(playerUUID, joinTick); - playerDataMap.put(playerUUID, newData); + public PlayerData registerAndGetPlayer(UUID playerUUID, int joinTick, int selfEntityID, SelfEntityCreator selfEntityCreator) { + PlayerData newData = new PlayerData(playerUUID, false, joinTick, selfEntityID, selfEntityCreator); + PlayerData old = playerDataMap.put(playerUUID, newData); + if (old != null) old.markDisconnected(); return newData; } - public PlayerData registerAndGetPlayerIfAbsent(UUID playerUUID, boolean hasBypassPermission, int joinTick) { - PlayerData newData = new PlayerData(playerUUID, hasBypassPermission, joinTick); - PlayerData existingData = playerDataMap.putIfAbsent(playerUUID, newData); - return existingData != null ? existingData : newData; - } - public void unregisterPlayer(UUID playerUUID) { PlayerData unregisteredPlayer = playerDataMap.remove(playerUUID); if (unregisteredPlayer == null) { return; } - unregisteredPlayer.blockView().clear(); - unregisteredPlayer.entityView().clear(); - unregisteredPlayer.playerView().clear(); - unregisteredPlayer.nettyData().clear(); + unregisteredPlayer.markDisconnected(); } public PlayerData getPlayerData(UUID playerUUID) { diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/utils/SingleThreadedGuard.java b/core/src/main/java/games/cubi/raycastedantiesp/core/utils/SingleThreadedGuard.java new file mode 100644 index 00000000..0a6cbe9a --- /dev/null +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/utils/SingleThreadedGuard.java @@ -0,0 +1,19 @@ +package games.cubi.raycastedantiesp.core.utils; + +/** + * Classes where specific methods must be called from a single thread can extend this class and call {@link #guardThread()} + * at the start of those methods to ensure that they are only called from the permitted thread. + */ +public abstract class SingleThreadedGuard { + private final Thread permittedThread; + + protected SingleThreadedGuard(Thread thread) { + permittedThread = thread; + } + + protected void guardThread() { + if (Thread.currentThread() != permittedThread) { + throw new IllegalStateException("Method called from wrong thread. Expected: " + permittedThread + ", actual: " + Thread.currentThread()); + } + } +} diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/view/AbstractBlockView.java b/core/src/main/java/games/cubi/raycastedantiesp/core/view/AbstractBlockView.java index 1a3cda8c..58f7630d 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/view/AbstractBlockView.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/view/AbstractBlockView.java @@ -1,5 +1,6 @@ package games.cubi.raycastedantiesp.core.view; +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; import games.cubi.locatables.BlockLocatable; import games.cubi.locatables.ChunkSectionLocatable; import games.cubi.locatables.Locatable; @@ -11,7 +12,6 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; public abstract class AbstractBlockView> implements BlockView { @@ -20,7 +20,7 @@ public abstract class AbstractBlockView> implem private final Map chunks = new ConcurrentHashMap<>(); private final CanonicalSet knownTileEntities = new ConcurrentSelfMap<>(); - private final ConcurrentLinkedQueue transitions = new ConcurrentLinkedQueue<>(); + private final MultiThreadedQueue transitions = new MultiThreadedQueue<>(); @Deprecated protected abstract T createTrackedTileEntity(BlockLocatable location, int blockID, boolean visible); @@ -89,14 +89,18 @@ public void setVisibility(BlockLocatable location, boolean visible, int currentT if (existing == null) { return; } - if (existing.visible() != visible) { + setVisibility(existing, existing.visible(), visible, currentTick); + } + + public void setVisibility(T tileEntity, boolean currentVisibility, boolean shouldBeVisible, int currentTick) { + if (currentVisibility != shouldBeVisible) { transitions.add(new BlockViewTransition( - visible ? BlockViewTransition.Type.SHOW : BlockViewTransition.Type.HIDE, - location + shouldBeVisible ? BlockViewTransition.Type.SHOW : BlockViewTransition.Type.HIDE, + tileEntity )); } - existing.setVisible(visible); - existing.setLastChecked(currentTick); + tileEntity.setVisible(shouldBeVisible); + tileEntity.setLastChecked(currentTick); } @Override @@ -117,6 +121,23 @@ public int forEachNeedingRecheck(int recheckTicks, int currentTick, Consumer action); + @FunctionalInterface + interface VisibilityResolver { + byte SKIPPED = 78; + byte HIDE = -23; + byte SHOW = 42; + + byte setVisible(BlockLocatable location); + } + + /** + * Iterates currently tracked tile entities that should be visibility-checked. + * + * @return number of tile entities passed to {@code action}. + */ + int updateVisibilityForEachNeedingRecheck(int recheckTicks, int currentTick, VisibilityResolver action); + boolean hasPendingTransitions(); List drainTransitions(); diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/view/EntityView.java b/core/src/main/java/games/cubi/raycastedantiesp/core/view/EntityView.java index 662d74ca..c4311abd 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/view/EntityView.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/view/EntityView.java @@ -53,6 +53,13 @@ public interface EntityView> extends Clearable */ int forEachNeedingRecheck(int recheckTicks, int currentTick, Consumer action); + /** + * Iterates currently tracked entities that should be visibility-checked. + * + * @return number of entities passed to {@code action}, or 0 if {@code countingActuallyNeeded} is false. + */ + int forEachNeedingRecheckEntity(int recheckTicks, int currentTick, boolean countingActuallyNeeded, Consumer> action); + boolean hasPendingTransitions(); List drainTransitions(); diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PacketEntityViewController.java b/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PacketEntityViewController.java index 2020aa55..02348260 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PacketEntityViewController.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PacketEntityViewController.java @@ -80,17 +80,18 @@ protected void handleWorldStatePacket(UUID player, String world, int minWorldHei nettyData.setCurrentWorldName(world).setCurrentWorldMinHeight(minWorldHeight); } - protected void handlePlayPhaseLoginPacket(int entityID, UUID playerUUID, int currentTick) { - PlayerData playerData = PlayerRegistry.getInstance().getPlayerData(playerUUID); - playerData.nettyData().setSelfEntity(Logger.requireNonNull(createSelfEntity(playerData, entityID, playerUUID), "createSelfEntity returned null", 3, PacketEntityViewController.class)); - } - - protected PlayerData handleLoginPhaseLoginPacket(UUID playerUUID, int currentTick) { - return PlayerRegistry.getInstance().registerAndGetPlayer(playerUUID, currentTick); + protected PlayerData handlePlayPhaseLoginPacket(int entityID, UUID playerUUID, int currentTick) { + return PlayerRegistry.getInstance().registerAndGetPlayer(playerUUID, currentTick, entityID, this::createSelfEntity); } protected abstract NettyEntityLocatable createSelfEntity(PlayerData ownData, int entityID, UUID playerUUID); + protected void handlePlayerDisconnect(UUID player) { + if (player == null) { + return; + } + PlayerRegistry.getInstance().unregisterPlayer(player); + } /** * @return Whether or not to cancel the packet event. true to cancel, false to do nothing. */ diff --git a/leafpile b/leafpile new file mode 160000 index 00000000..aac6df93 --- /dev/null +++ b/leafpile @@ -0,0 +1 @@ +Subproject commit aac6df93ad1a4caad36ece099bf924a35b00c902 diff --git a/packetevents/build.gradle.kts b/packetevents/build.gradle.kts index 58cee0da..257bb517 100644 --- a/packetevents/build.gradle.kts +++ b/packetevents/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { implementation(project(":logging")) implementation(project(":core")) + compileOnly(project(":leafpile")) + compileOnly("com.github.retrooper:packetevents-api:2.12.0") compileOnly("org.spongepowered:configurate-core:4.2.0") compileOnly("it.unimi.dsi:fastutil:8.5.18") diff --git a/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/view/PacketEventsEntityView.java b/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/view/PacketEventsEntityView.java index 21bdc420..682b3d31 100644 --- a/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/view/PacketEventsEntityView.java +++ b/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/view/PacketEventsEntityView.java @@ -1,25 +1,28 @@ package games.cubi.raycastedantiesp.packetevents.view; +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; +import ca.spottedleaf.concurrentutil.map.SWMRHashTable; import games.cubi.locatables.Locatable; import games.cubi.logs.Logger; import games.cubi.raycastedantiesp.core.locatables.NettyEntityLocatable; +import games.cubi.raycastedantiesp.core.utils.SingleThreadedGuard; import games.cubi.raycastedantiesp.core.view.EntityView; import games.cubi.raycastedantiesp.core.view.EntityViewTransition; import games.cubi.raycastedantiesp.packetevents.locatables.PacketEventsEntity; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.jetbrains.annotations.NotNull; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; -public class PacketEventsEntityView implements EntityView { - private final Map entitiesByUUID = new ConcurrentHashMap<>(); - private final Map entityUUIDsByID = new ConcurrentHashMap<>(); - private final ConcurrentLinkedQueue transitions = new ConcurrentLinkedQueue<>(); +public class PacketEventsEntityView extends SingleThreadedGuard implements EntityView { + private final SWMRHashTable entitiesByUUID = new SWMRHashTable<>(); + private final Int2ObjectOpenHashMap entityUUIDsByID = new Int2ObjectOpenHashMap<>(); + private final MultiThreadedQueue transitions = new MultiThreadedQueue<>(); private final boolean isPlayerView; public PacketEventsEntityView(boolean isPlayerView) { + super(Thread.currentThread()); // Should be player's netty thread this.isPlayerView = isPlayerView; } @@ -40,6 +43,7 @@ public void insertEntity(PacketEventsEntity entity) { UUID entityUUID = entity.entityUUID(); int entityID = entity.entityID(); + guardThread(); UUID previousUUIDForID = entityUUIDsByID.put(entityID, entityUUID); if (previousUUIDForID != null && !previousUUIDForID.equals(entityUUID)) { PacketEventsEntity previousEntityForID = entitiesByUUID.get(previousUUIDForID); @@ -64,6 +68,7 @@ public void removeEntity(int entityID, int currentTick) { @Override public void removeEntity(int entityID) { + guardThread(); UUID entityUUID = entityUUIDsByID.remove(entityID); if (entityUUID == null) { return; @@ -99,6 +104,7 @@ public boolean exists(UUID entityUUID) { @Override public boolean exists(int entityID) { + guardThread(); return entityUUIDsByID.containsKey(entityID); } @@ -162,12 +168,25 @@ public void setVisibility(@NotNull NettyEntityLocatable entity, boolean vis @Override public Collection getKnownEntities() { - return List.copyOf(entitiesByUUID.keySet()); + List known = new ArrayList<>(entitiesByUUID.size()); + entitiesByUUID.forEachKey(known::add); + return known; } @Override public int[] getKnownEntityIDs() { - return entityUUIDsByID.keySet().stream().mapToInt(Integer::intValue).toArray(); + /* + guardThread(); + // This is only called while clearing on the Netty thread, so size() + // is stable and can be used as the exact output array length. + int[] entityIDs = new int[entityUUIDsByID.size()]; + int[] count = new int[1]; + entityUUIDsByID.forEachKey(entityID -> { + entityIDs[count[0]++] = entityID; + });*/ + guardThread(); + return entityUUIDsByID.keySet().toIntArray(); + //return entityIDs; } @Override @@ -183,6 +202,26 @@ public int forEachNeedingRecheck(int recheckTicks, int currentTick, Consumer> action) { + if (countingActuallyNeeded) { + return entitiesByUUID.forEachValueCounted( (entity) -> { + if (entity.visible() && (recheckTicks < 0 || currentTick - entity.lastChecked() < recheckTicks)) { + return false; + } + action.accept(entity); + return true; + }); + } + entitiesByUUID.forEachValue( (entity) -> { + if (entity.visible() && (recheckTicks < 0 || currentTick - entity.lastChecked() < recheckTicks)) { + return; + } + action.accept(entity); + }); + return 0; + } + @Override public boolean hasPendingTransitions() { return !transitions.isEmpty(); @@ -205,12 +244,14 @@ public boolean isPlayerView() { @Override public void clear() { + guardThread(); entitiesByUUID.clear(); entityUUIDsByID.clear(); transitions.clear(); } private PacketEventsEntity getTrackedEntity(int entityID) { + guardThread(); UUID entityUUID = entityUUIDsByID.get(entityID); return entityUUID == null ? null : entitiesByUUID.get(entityUUID); } @@ -218,14 +259,14 @@ private PacketEventsEntity getTrackedEntity(int entityID) { public String getStringDataForDebugging() { StringBuilder builder = new StringBuilder(); builder.append("EntityView isPlayerView=").append(isPlayerView).append("\n"); - Set> entries = new HashSet<>(entityUUIDsByID.entrySet()); - for (Map.Entry entry : entries) { - PacketEventsEntity entity = entitiesByUUID.get(entry.getValue()); - builder.append("EntityID=").append(entry.getKey()) - .append(" UUID=").append(entry.getValue()) + guardThread(); + entityUUIDsByID.forEach((entityID, entityUUID) -> { + PacketEventsEntity entity = entitiesByUUID.get(entityUUID); + builder.append("EntityID=").append(entityID) + .append(" UUID=").append(entityUUID) .append(" Entity=").append(entity) .append("\n"); - } + }); return builder.toString(); } } diff --git a/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsBlockViewController.java b/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsBlockViewController.java index 56aa117f..8b9ff514 100644 --- a/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsBlockViewController.java +++ b/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsBlockViewController.java @@ -154,16 +154,15 @@ private void processTileEntityTransitions(User viewer, BlockView blockView) { for (BlockViewTransition transition : blockView.drainTransitions()) { BlockLocatable location = transition.location(); TileEntityLocatable state = getTrackedTileEntity(blockView, location); - + if (state == null || state.blockID() == 0) { + continue; + } switch (transition.type()) { case HIDE -> viewer.writePacketSilently(new WrapperPlayServerBlockChange( new Vector3i(location.blockX(), location.blockY(), location.blockZ()), getHiddenBlockId(location.blockY()) )); case SHOW -> { - if (state == null || state.blockID() == 0) { - continue; - } viewer.writePacketSilently(new WrapperPlayServerBlockChange( new Vector3i(location.blockX(), location.blockY(), location.blockZ()), state.blockID() diff --git a/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsEntityViewController.java b/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsEntityViewController.java index e2a1adfe..319954b2 100644 --- a/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsEntityViewController.java +++ b/packetevents/src/main/java/games/cubi/raycastedantiesp/packetevents/viewcontrollers/PacketEventsEntityViewController.java @@ -11,6 +11,7 @@ import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.event.PacketListener; import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.event.UserDisconnectEvent; import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; @@ -62,7 +63,10 @@ protected PacketEventsEntityViewController(IntSupplier currentTickSupplier) { COMMON = PacketEventsCommonViewController.get(currentTickSupplier); } - public void removeViewer(UUID viewerUUID) { + @Override + public void onUserDisconnect(UserDisconnectEvent event) { + UUID viewerUUID = event.getUser().getUUID(); + handlePlayerDisconnect(viewerUUID); } @Override @@ -74,16 +78,11 @@ public void onPacketSend(PacketSendEvent event) { PlayerData playerData = PlayerRegistry.getInstance().getPlayerData(viewerUUID); - if (event.getPacketType() == PacketType.Login.Server.LOGIN_SUCCESS) { - handleLoginPhaseLoginPacket(viewerUUID, CURRENT_TICK_SUPPLIER.getAsInt()); - return; - } - if (event.getPacketType() == PacketType.Play.Server.JOIN_GAME) { WrapperPlayServerJoinGame packet = new WrapperPlayServerJoinGame(event); int currentTick = CURRENT_TICK_SUPPLIER.getAsInt(); + playerData = handlePlayPhaseLoginPacket(packet.getEntityId(), viewerUUID, currentTick); handleWorldStatePacket(viewerUUID, packet.getWorldName(), packet.getDimensionType().getMinY(), currentTick); - handlePlayPhaseLoginPacket(packet.getEntityId(), viewerUUID, currentTick); } if (playerData == null) { @@ -421,6 +420,9 @@ private void processEntityTransitions(PlayerData data, User viewer, EntityView

{ + int successfulRays = 0; long startTime = System.nanoTime(); for (Locatable locatable : locatables) { - RaycastUtil.raycast(playerData, playerLocatable, locatable, 3, 0, 100, false, playerData.blockView(), 1, null); + if (RaycastUtil.raycast(playerData, playerLocatable, locatable, 3, 0, 100, false, playerData.blockView(), 1, null)) successfulRays++; } long endTime = System.nanoTime(); long duration = endTime - startTime; double averageTime = duration / (double) locatables.length; + final int successfulRaysFinal = successfulRays; PaperScheduler.runForAudience(RaycastedAntiESP.get(), player, () -> { player.sendRichMessage("Average raycast time: " + averageTime + " nanoseconds"); player.sendRichMessage("Total raycast time: " + duration + " nanoseconds"); + player.sendRichMessage("Successful rays: " + successfulRaysFinal + "/" + locatables.length); }); }); } diff --git a/platform-paper/src/main/resources/plugin.yml b/platform-paper/src/main/resources/plugin.yml index e9bd1eea..2563a76a 100644 --- a/platform-paper/src/main/resources/plugin.yml +++ b/platform-paper/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: RaycastedAntiESP version: ${version} main: games.cubi.raycastedantiesp.paper.RaycastedAntiESP -api-version: '1.21.0' +api-version: '1.21.4' prefix: RaycastedAntiESP authors: [ _Cubicake ] load: STARTUP diff --git a/settings.gradle b/settings.gradle index 9e6dc69c..56471f6b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,15 @@ rootProject.name = 'RaycastedAntiESP' +dependencyResolutionManagement { + versionCatalogs { + libs { + from(files("leafpile/gradle/libs.versions.toml")) + } + } +} + include 'locatable-lib' include 'logging' include 'core' include 'platform-paper' -include 'packetevents' \ No newline at end of file +include 'packetevents' +include 'leafpile'