diff --git a/src/main/java/ac/grim/grimac/events/packets/PacketEntityReplication.java b/src/main/java/ac/grim/grimac/events/packets/PacketEntityReplication.java index d15bcfb97f..112af71bc0 100644 --- a/src/main/java/ac/grim/grimac/events/packets/PacketEntityReplication.java +++ b/src/main/java/ac/grim/grimac/events/packets/PacketEntityReplication.java @@ -441,10 +441,12 @@ private void handleMoveEntity(PacketSendEvent event, int entityId, double deltaX return; } + player.compensatedEntities.entityMap.updateEntityPosition(player.compensatedEntities.entityMap.get(entityId), new Vector3d(data.getX() + deltaX, data.getY() + deltaY, data.getZ() + deltaZ)); data.setX(data.getX() + deltaX); data.setY(data.getY() + deltaY); data.setZ(data.getZ() + deltaZ); } else { + player.compensatedEntities.entityMap.updateEntityPosition(player.compensatedEntities.entityMap.get(entityId), new Vector3d(deltaX, deltaY, deltaZ)); data.setX(deltaX); data.setY(deltaY); data.setZ(deltaZ); diff --git a/src/main/java/ac/grim/grimac/utils/data/packetentity/dragon/PacketEntityEnderDragon.java b/src/main/java/ac/grim/grimac/utils/data/packetentity/dragon/PacketEntityEnderDragon.java index 81d10eee2a..8461db2552 100644 --- a/src/main/java/ac/grim/grimac/utils/data/packetentity/dragon/PacketEntityEnderDragon.java +++ b/src/main/java/ac/grim/grimac/utils/data/packetentity/dragon/PacketEntityEnderDragon.java @@ -2,8 +2,8 @@ import ac.grim.grimac.player.GrimPlayer; import ac.grim.grimac.utils.data.packetentity.PacketEntity; +import ac.grim.grimac.utils.latency.SectionedEntityMap; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.util.ArrayList; import java.util.List; @@ -15,7 +15,7 @@ public final class PacketEntityEnderDragon extends PacketEntity { public PacketEntityEnderDragon(GrimPlayer player, UUID uuid, int entityID, double x, double y, double z) { super(player, uuid, EntityTypes.ENDER_DRAGON, x, y, z); - final Int2ObjectOpenHashMap entityMap = player.compensatedEntities.entityMap; + final SectionedEntityMap entityMap = player.compensatedEntities.entityMap; parts.add(new PacketEntityEnderDragonPart(player, DragonPart.HEAD, x, y, z, 1.0F, 1.0F)); parts.add(new PacketEntityEnderDragonPart(player, DragonPart.NECK, x, y, z, 3.0F, 3.0F)); parts.add(new PacketEntityEnderDragonPart(player, DragonPart.BODY, x, y, z, 5.0F, 3.0F)); diff --git a/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java b/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java index 93fb9122ee..838fc53e90 100644 --- a/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java +++ b/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java @@ -34,8 +34,9 @@ public class CompensatedEntities { public static final UUID SPRINTING_MODIFIER_UUID = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); public static final UUID SNOW_MODIFIER_UUID = UUID.fromString("1eaf83ff-7207-4596-b37a-d7a07b3ec4ce"); - public final Int2ObjectOpenHashMap entityMap = new Int2ObjectOpenHashMap<>(40, 0.7f); - public final Int2ObjectOpenHashMap serverPositionsMap = new Int2ObjectOpenHashMap<>(40, 0.7f); + public final SectionedEntityMap entityMap = new SectionedEntityMap(); +// public final Int2ObjectLinkedOpenHashMap entityMap = new Int2ObjectLinkedOpenHashMap<>(40, 0.7f); // needs to be linked to replicate vanilla iteration order! + public final Int2ObjectOpenHashMap serverPositionsMap = new Int2ObjectOpenHashMap<>(40, 0.7f); // never iterate over, so iteration order does not matter public final Object2ObjectOpenHashMap profiles = new Object2ObjectOpenHashMap<>(); public Integer serverPlayerVehicle = null; public boolean hasSprintingAttributeEnabled = false; @@ -70,13 +71,15 @@ public void tick() { } public void removeEntity(int entityID) { - PacketEntity entity = entityMap.remove(entityID); + PacketEntity entity = entityMap.get(entityID); if (entity == null) return; + entityMap.removeEntity(entityID); + if (entity instanceof PacketEntityEnderDragon) { PacketEntityEnderDragon dragon = (PacketEntityEnderDragon) entity; for (int i = 1; i < dragon.getParts().size() + 1; i++) { - entityMap.remove(entityID + i); + entityMap.removeEntity(entityID + i); } } diff --git a/src/main/java/ac/grim/grimac/utils/latency/SectionedEntityMap.java b/src/main/java/ac/grim/grimac/utils/latency/SectionedEntityMap.java index b5ef507106..10f810602f 100644 --- a/src/main/java/ac/grim/grimac/utils/latency/SectionedEntityMap.java +++ b/src/main/java/ac/grim/grimac/utils/latency/SectionedEntityMap.java @@ -1,23 +1,33 @@ package ac.grim.grimac.utils.latency; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.function.Consumer; import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox; import ac.grim.grimac.utils.data.packetentity.PacketEntity; import ac.grim.grimac.utils.math.GrimMath; import com.github.retrooper.packetevents.util.Vector3d; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.LongAVLTreeSet; import it.unimi.dsi.fastutil.longs.LongSortedSet; public class SectionedEntityMap { + // exists to make porting over the legacy entity handling easier, will remove later + private final Int2ObjectOpenHashMap idToEntity = new Int2ObjectOpenHashMap<>(); + private final Long2ObjectOpenHashMap sections = new Long2ObjectOpenHashMap<>(); private final LongSortedSet trackedSections = new LongAVLTreeSet(); - public void addEntity(PacketEntity entity) { + private final EntityCollection entityCollection = new EntityCollection(); + + public PacketEntity get(int entityId) { + return idToEntity.get(entityId); + } + + public void addEntity(int entityID, PacketEntity entity) { + idToEntity.put(entityID, entity); Vector3d entityLocation = entity.trackedServerPosition.getPos(); long sectionPos = GrimMath.asLong( GrimMath.getSectionCoord(entityLocation.getX()), @@ -30,6 +40,13 @@ public void addEntity(PacketEntity entity) { trackedSections.add(sectionPos); } + public void removeEntity(int entityId) { + PacketEntity entity = idToEntity.remove(entityId); + if (entity != null) { + removeEntity(entity); + } + } + public void removeEntity(PacketEntity entity) { Vector3d entityLocation = entity.trackedServerPosition.getPos(); long sectionPos = GrimMath.asLong( @@ -48,6 +65,26 @@ public void removeEntity(PacketEntity entity) { } } + public Collection values() { + return entityCollection; + } + + public boolean containsKey(int entityId) { + return idToEntity.containsKey(entityId); + } + + public void forEachEntity(Consumer action) { + long minPacked = Long.MIN_VALUE; + long maxPacked = Long.MAX_VALUE; + + for (long sectionPos : trackedSections.subSet(minPacked, maxPacked)) { + EntitySection section = sections.get(sectionPos); + if (section != null) { + section.forEachEntity(action); + } + } + } + public void forEachInBox(SimpleCollisionBox box, Consumer action) { int minX = GrimMath.getSectionCoord(box.minX - 2.0); int minY = GrimMath.getSectionCoord(box.minY - 4.0); @@ -75,10 +112,63 @@ public void forEachInBox(SimpleCollisionBox box, Consumer action) } } + public void updateEntityPosition(PacketEntity entity, Vector3d newPosition) { + if (entity == null) return; // is null on startup at first + // Get old and new section positions + Vector3d oldPosition = entity.trackedServerPosition.getPos(); + long oldSectionPos = GrimMath.asLong( + GrimMath.getSectionCoord(oldPosition.getX()), + GrimMath.getSectionCoord(oldPosition.getY()), + GrimMath.getSectionCoord(oldPosition.getZ()) + ); + + long newSectionPos = GrimMath.asLong( + GrimMath.getSectionCoord(newPosition.getX()), + GrimMath.getSectionCoord(newPosition.getY()), + GrimMath.getSectionCoord(newPosition.getZ()) + ); + + // If section changed + if (oldSectionPos != newSectionPos) { + // Remove from old section + EntitySection oldSection = sections.get(oldSectionPos); + if (oldSection != null) { + oldSection.removeEntity(entity); + if (oldSection.isEmpty()) { + sections.remove(oldSectionPos); + trackedSections.remove(oldSectionPos); + } + } + + // Add to new section + EntitySection newSection = sections.computeIfAbsent(newSectionPos, this::createSection); + newSection.addEntity(entity); + trackedSections.add(newSectionPos); + } + } + private EntitySection createSection(long pos) { return new EntitySection(); } + public Iterable> int2ObjectEntrySet() { + return idToEntity.entrySet(); + } + + public void put(int entityID, PacketEntity packetEntity) { + addEntity(entityID, packetEntity); + } + + public boolean containsValue(PacketEntity entity) { + return idToEntity.containsValue(entity); + } + + public void clear() { + idToEntity.clear(); + sections.clear(); + trackedSections.clear(); + } + private static class EntitySection { private final List entities = new ArrayList<>(); @@ -104,4 +194,46 @@ public List getEntities() { return entities; } } -} \ No newline at end of file + + private class EntityCollection extends AbstractCollection { + @Override + public Iterator iterator() { + return new EntityIterator(); + } + + @Override + public int size() { + return idToEntity.size(); + } + + @Override + public boolean contains(Object o) { + return o instanceof PacketEntity && containsValue((PacketEntity) o); + } + } + + private class EntityIterator implements Iterator { + private final Iterator sectionIterator = trackedSections.iterator(); + private Iterator currentSectionIterator = Collections.emptyIterator(); + + @Override + public boolean hasNext() { + while (!currentSectionIterator.hasNext() && sectionIterator.hasNext()) { + long sectionPos = sectionIterator.next(); + EntitySection section = sections.get(sectionPos); + if (section != null) { + currentSectionIterator = section.getEntities().iterator(); + } + } + return currentSectionIterator.hasNext(); + } + + @Override + public PacketEntity next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return currentSectionIterator.next(); + } + } +}