From 367aac0274b4012f44533ea4f54ebab6735f5ae8 Mon Sep 17 00:00:00 2001 From: MachineBreaker Date: Sun, 17 Nov 2024 10:06:22 -0400 Subject: [PATCH] Use FastUtil collections where possible --- .../grim/grimac/checks/impl/combat/Reach.java | 8 ++++--- .../grimac/manager/PunishmentManager.java | 5 +++-- .../grimac/manager/init/start/SuperDebug.java | 6 +++-- .../predictionengine/UncertaintyHandler.java | 14 ++++++------ .../utils/data/packetentity/PacketEntity.java | 13 ++++++----- .../utils/latency/CompensatedWorld.java | 9 +++++--- .../CorrectingPlayerInventoryStorage.java | 9 +++++--- .../grim/grimac/utils/lists/RunningMode.java | 22 ++++++++++--------- 8 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java b/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java index 5b6e987580..a53f96ce95 100644 --- a/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java +++ b/src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java @@ -34,6 +34,8 @@ import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.bukkit.util.Vector; import java.util.*; @@ -43,7 +45,7 @@ public class Reach extends Check implements PacketCheck { // Only one flag per reach attack, per entity, per tick. // We store position because lastX isn't reliable on teleports. - private final Map playerAttackQueue = new HashMap<>(); + private final Int2ObjectMap playerAttackQueue = new Int2ObjectOpenHashMap<>(); private static final List blacklisted = Arrays.asList( EntityTypes.BOAT, EntityTypes.CHEST_BOAT, @@ -142,8 +144,8 @@ private boolean isKnownInvalid(PacketEntity reachEntity) { } private void tickBetterReachCheckWithAngle() { - for (Map.Entry attack : playerAttackQueue.entrySet()) { - PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getKey().intValue()); + for (Int2ObjectMap.Entry attack : playerAttackQueue.int2ObjectEntrySet()) { + PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey()); if (reachEntity != null) { String result = checkReach(reachEntity, attack.getValue(), false); if (result != null) { diff --git a/src/main/java/ac/grim/grimac/manager/PunishmentManager.java b/src/main/java/ac/grim/grimac/manager/PunishmentManager.java index 2faf7dbb5a..873d2f3cd1 100644 --- a/src/main/java/ac/grim/grimac/manager/PunishmentManager.java +++ b/src/main/java/ac/grim/grimac/manager/PunishmentManager.java @@ -11,6 +11,7 @@ import ac.grim.grimac.utils.anticheat.LogUtil; import ac.grim.grimac.utils.anticheat.MessageUtil; import io.github.retrooper.packetevents.util.folia.FoliaScheduler; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import lombok.Getter; import lombok.Setter; import org.bukkit.Bukkit; @@ -183,7 +184,7 @@ public void handleViolation(Check check) { group.violations.put(currentTime, check); // Remove violations older than the defined time in the config - group.violations.entrySet().removeIf(time -> currentTime - time.getKey() > group.removeViolationsAfter); + group.violations.long2ObjectEntrySet().removeIf(time -> currentTime - time.getLongKey() > group.removeViolationsAfter); } } } @@ -195,7 +196,7 @@ class PunishGroup { @Getter List commands; @Getter - HashMap violations = new HashMap<>(); + Long2ObjectOpenHashMap violations = new Long2ObjectOpenHashMap<>(); @Getter int removeViolationsAfter; diff --git a/src/main/java/ac/grim/grimac/manager/init/start/SuperDebug.java b/src/main/java/ac/grim/grimac/manager/init/start/SuperDebug.java index 54fac60e75..68727e7c4c 100644 --- a/src/main/java/ac/grim/grimac/manager/init/start/SuperDebug.java +++ b/src/main/java/ac/grim/grimac/manager/init/start/SuperDebug.java @@ -12,6 +12,8 @@ import ac.grim.grimac.utils.math.GrimMath; import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.AllArgsConstructor; import org.bukkit.util.Vector; @@ -20,7 +22,7 @@ public final class SuperDebug extends Check implements PostPredictionCheck { private static final StringBuilder[] flags = new StringBuilder[256]; // 17 MB of logs in memory - Map continuedDebug = new HashMap<>(); + Object2IntMap continuedDebug = new Object2IntOpenHashMap<>(); List predicted = new EvictingQueue<>(60); List actually = new EvictingQueue<>(60); @@ -45,7 +47,7 @@ public void onPredictionComplete(final PredictionComplete predictionComplete) { Location location = new Location(player.x, player.y, player.z, player.xRot, player.yRot, player.bukkitPlayer == null ? "null" : player.bukkitPlayer.getWorld().getName()); - for (Iterator> it = continuedDebug.entrySet().iterator(); it.hasNext(); ) { + for (Iterator> it = continuedDebug.object2IntEntrySet().iterator(); it.hasNext(); ) { Map.Entry debug = it.next(); appendDebug(debug.getKey(), player.predictedVelocity, player.actualMovement, location, player.startTickClientVel, player.baseTickAddition, player.baseTickWaterPushing); debug.setValue(debug.getValue() - 1); diff --git a/src/main/java/ac/grim/grimac/predictionengine/UncertaintyHandler.java b/src/main/java/ac/grim/grimac/predictionengine/UncertaintyHandler.java index dbf2a8fb86..c94220b549 100644 --- a/src/main/java/ac/grim/grimac/predictionengine/UncertaintyHandler.java +++ b/src/main/java/ac/grim/grimac/predictionengine/UncertaintyHandler.java @@ -13,6 +13,7 @@ import com.github.retrooper.packetevents.protocol.attribute.Attributes; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.protocol.world.BlockFace; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.bukkit.util.Vector; import java.util.*; @@ -338,10 +339,9 @@ private boolean regularHardCollision(SimpleCollisionBox expandedBB) { private boolean striderCollision(SimpleCollisionBox expandedBB) { // Stiders can walk on top of other striders if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityStrider) { - for (Map.Entry entityPair : player.compensatedEntities.entityMap.int2ObjectEntrySet()) { - PacketEntity entity = entityPair.getValue(); - if (entity.getType() == EntityTypes.STRIDER && entity != player.compensatedEntities.getSelf().getRiding() && !entity.hasPassenger(entityPair.getValue()) - && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) { + for (PacketEntity entity : player.compensatedEntities.entityMap.values()) { + if (entity.getType() == EntityTypes.STRIDER && entity != player.compensatedEntities.getSelf().getRiding() + && !entity.hasPassenger(entity) && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) { return true; } } @@ -355,9 +355,9 @@ private boolean boatCollision(SimpleCollisionBox expandedBB) { final PacketEntity riding = player.compensatedEntities.getSelf().getRiding(); if (riding == null || !riding.isBoat()) return false; - for (Map.Entry entityPair : player.compensatedEntities.entityMap.int2ObjectEntrySet()) { - PacketEntity entity = entityPair.getValue(); - if (entity != riding && entity.isPushable() && !riding.hasPassenger(entityPair.getValue()) && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) { + for (PacketEntity entity : player.compensatedEntities.entityMap.values()) { + if (entity != riding && entity.isPushable() && !riding.hasPassenger(entity) + && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) { return true; } } diff --git a/src/main/java/ac/grim/grimac/utils/data/packetentity/PacketEntity.java b/src/main/java/ac/grim/grimac/utils/data/packetentity/PacketEntity.java index c967b8f85f..bd65dd30a4 100644 --- a/src/main/java/ac/grim/grimac/utils/data/packetentity/PacketEntity.java +++ b/src/main/java/ac/grim/grimac/utils/data/packetentity/PacketEntity.java @@ -27,6 +27,8 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion; import com.github.retrooper.packetevents.protocol.potion.PotionType; import com.github.retrooper.packetevents.util.Vector3d; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; import java.util.ArrayList; @@ -54,7 +56,7 @@ public class PacketEntity extends TypedPacketEntity { private ReachInterpolationData oldPacketLocation; private ReachInterpolationData newPacketLocation; - private Map potionsMap = null; + private Object2IntMap potionsMap = null; protected final Map attributeMap = new IdentityHashMap<>(); public PacketEntity(GrimPlayer player, EntityType type) { @@ -202,8 +204,8 @@ public PacketEntity getRiding() { } public OptionalInt getPotionEffectLevel(PotionType effect) { - final Integer amplifier = potionsMap == null ? null : potionsMap.get(effect); - return amplifier == null ? OptionalInt.empty() : OptionalInt.of(amplifier); + final int amplifier = potionsMap == null ? -1 : potionsMap.getInt(effect); + return amplifier == -1 ? OptionalInt.empty() : OptionalInt.of(amplifier); } public boolean hasPotionEffect(PotionType effect) { @@ -212,13 +214,14 @@ public boolean hasPotionEffect(PotionType effect) { public void addPotionEffect(PotionType effect, int amplifier) { if (potionsMap == null) { - potionsMap = new HashMap<>(); + potionsMap = new Object2IntOpenHashMap<>(); + potionsMap.defaultReturnValue(-1); } potionsMap.put(effect, amplifier); } public void removePotionEffect(PotionType effect) { if (potionsMap == null) return; - potionsMap.remove(effect); + potionsMap.removeInt(effect); } } diff --git a/src/main/java/ac/grim/grimac/utils/latency/CompensatedWorld.java b/src/main/java/ac/grim/grimac/utils/latency/CompensatedWorld.java index a355f25fe6..672a63a2be 100644 --- a/src/main/java/ac/grim/grimac/utils/latency/CompensatedWorld.java +++ b/src/main/java/ac/grim/grimac/utils/latency/CompensatedWorld.java @@ -43,6 +43,9 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging; import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientUseItem; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import org.bukkit.Bukkit; @@ -55,7 +58,7 @@ public class CompensatedWorld { public static final ClientVersion blockVersion = PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(); private static final WrappedBlockState airData = WrappedBlockState.getByGlobalId(blockVersion, 0); public final GrimPlayer player; - public final Map chunks; + public final Long2ObjectMap chunks; // Packet locations for blocks public Set activePistons = new HashSet<>(); public Set openShulkerBoxes = new HashSet<>(); @@ -70,7 +73,7 @@ public class CompensatedWorld { private final Long2ObjectOpenHashMap originalServerBlocks = new Long2ObjectOpenHashMap<>(); // Blocks the client changed while placing or breaking blocks private List currentlyChangedBlocks = new LinkedList<>(); - private final Map> serverIsCurrentlyProcessingThesePredictions = new HashMap<>(); + private final Int2ObjectMap> serverIsCurrentlyProcessingThesePredictions = new Int2ObjectOpenHashMap<>(); private final Object2ObjectLinkedOpenHashMap, Vector3d> unackedActions = new Object2ObjectLinkedOpenHashMap<>(); private boolean isCurrentlyPredicting = false; public boolean isRaining = false; @@ -89,7 +92,7 @@ public void startPredicting() { } public void handlePredictionConfirmation(int prediction) { - for (Iterator>> it = serverIsCurrentlyProcessingThesePredictions.entrySet().iterator(); it.hasNext(); ) { + for (Iterator>> it = serverIsCurrentlyProcessingThesePredictions.int2ObjectEntrySet().iterator(); it.hasNext(); ) { Map.Entry> iter = it.next(); if (iter.getKey() <= prediction) { applyBlockChanges(iter.getValue()); diff --git a/src/main/java/ac/grim/grimac/utils/lists/CorrectingPlayerInventoryStorage.java b/src/main/java/ac/grim/grimac/utils/lists/CorrectingPlayerInventoryStorage.java index d22c830afc..c1ddedc1d1 100644 --- a/src/main/java/ac/grim/grimac/utils/lists/CorrectingPlayerInventoryStorage.java +++ b/src/main/java/ac/grim/grimac/utils/lists/CorrectingPlayerInventoryStorage.java @@ -7,6 +7,7 @@ import com.github.retrooper.packetevents.protocol.item.ItemStack; import io.github.retrooper.packetevents.util.SpigotConversionUtil; import io.github.retrooper.packetevents.util.folia.FoliaScheduler; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.bukkit.inventory.InventoryView; import java.util.*; @@ -42,7 +43,8 @@ public class CorrectingPlayerInventoryStorage extends InventoryStorage { GrimPlayer player; // The key for this map is the inventory slot ID // The value for this map is the transaction that we care about - Map serverIsCurrentlyProcessingThesePredictions = new HashMap<>(); + // Returns -1 if the entry is null + Int2IntOpenHashMap serverIsCurrentlyProcessingThesePredictions = new Int2IntOpenHashMap(); // A list of predictions the client has made for inventory changes // Remove if the server rejects these changes Map pendingFinalizedSlot = new ConcurrentHashMap<>(); @@ -54,6 +56,7 @@ public class CorrectingPlayerInventoryStorage extends InventoryStorage { public CorrectingPlayerInventoryStorage(GrimPlayer player, int size) { super(size); this.player = player; + this.serverIsCurrentlyProcessingThesePredictions.defaultReturnValue(-1); } // 1.17+ clients send what slots they have changed. This makes our jobs much easier. @@ -75,11 +78,11 @@ public void handleServerCorrectSlot(int slotID) { @Override public void setItem(int item, ItemStack stack) { // If there is a more recent change to this one, don't override it - Integer finalTransaction = serverIsCurrentlyProcessingThesePredictions.get(item); + int finalTransaction = serverIsCurrentlyProcessingThesePredictions.get(item); // If the server is currently sending a packet to the player AND it is the final change to the slot // OR, the client was in control of setting this slot - if (finalTransaction == null || player.lastTransactionReceived.get() >= finalTransaction) { + if (finalTransaction == -1 || player.lastTransactionReceived.get() >= finalTransaction) { // This is the last change for this slot, try to resync this slot if possible pendingFinalizedSlot.put(item, GrimAPI.INSTANCE.getTickManager().currentTick + 5); serverIsCurrentlyProcessingThesePredictions.remove(item); diff --git a/src/main/java/ac/grim/grimac/utils/lists/RunningMode.java b/src/main/java/ac/grim/grimac/utils/lists/RunningMode.java index 2dbff3c5bb..6726c7d6e7 100644 --- a/src/main/java/ac/grim/grimac/utils/lists/RunningMode.java +++ b/src/main/java/ac/grim/grimac/utils/lists/RunningMode.java @@ -1,6 +1,8 @@ package ac.grim.grimac.utils.lists; import ac.grim.grimac.utils.data.Pair; +import it.unimi.dsi.fastutil.doubles.Double2IntMap; +import it.unimi.dsi.fastutil.doubles.Double2IntOpenHashMap; import java.util.HashMap; import java.util.Map; @@ -12,7 +14,7 @@ // This class calculates the running mode of a list in best case o(1) worst case o(n) time. public class RunningMode { Queue addList; - Map popularityMap = new HashMap<>(); + Double2IntMap popularityMap = new Double2IntOpenHashMap(); int maxSize; private static final double threshold = 1e-3; @@ -34,10 +36,10 @@ public int getMaxSize() { public void add(double value) { pop(); - for (Map.Entry entry : popularityMap.entrySet()) { - if (Math.abs(entry.getKey() - value) < threshold) { - entry.setValue(entry.getValue() + 1); - addList.add(entry.getKey()); + for (Double2IntMap.Entry entry : popularityMap.double2IntEntrySet()) { + if (Math.abs(entry.getDoubleKey() - value) < threshold) { + entry.setValue(entry.getIntValue() + 1); + addList.add(entry.getDoubleKey()); return; } } @@ -49,7 +51,7 @@ public void add(double value) { private void pop() { if (addList.size() >= maxSize) { - Double type = addList.poll(); + double type = addList.poll(); int popularity = popularityMap.get(type); // Being null isn't possible if (popularity == 1) { popularityMap.remove(type); // Make sure not to leak memory @@ -63,10 +65,10 @@ public Pair getMode() { int max = 0; Double mostPopular = null; - for (Map.Entry entry : popularityMap.entrySet()) { - if (entry.getValue() > max) { - max = entry.getValue(); - mostPopular = entry.getKey(); + for (Double2IntMap.Entry entry : popularityMap.double2IntEntrySet()) { + if (entry.getIntValue() > max) { + max = entry.getIntValue(); + mostPopular = entry.getDoubleKey(); } }