From fb83bbbf4a9b26ff7b9773b80825a21eef707545 Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:22:12 +0930 Subject: [PATCH 01/10] Remove old PassengerReconciliationTask --- .../PassengerReconciliationTask.java | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PassengerReconciliationTask.java diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PassengerReconciliationTask.java b/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PassengerReconciliationTask.java deleted file mode 100644 index 67fcc591..00000000 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/view/controller/PassengerReconciliationTask.java +++ /dev/null @@ -1,44 +0,0 @@ -package games.cubi.raycastedantiesp.core.view.controller; - -import games.cubi.logs.Logger; -import games.cubi.raycastedantiesp.core.locatables.NettyEntityLocatable; -import games.cubi.raycastedantiesp.core.players.PlayerData; -import games.cubi.raycastedantiesp.core.utils.BaseEntitySpawnTask; - -import java.util.Arrays; - -final class PassengerReconciliationTask extends BaseEntitySpawnTask { - private final PlayerData playerData; - private final int queuedEntityId; - private final int vehicleEntityId; - private final int[] passengerIds; - - PassengerReconciliationTask(PlayerData playerData, int queuedEntityId, int vehicleEntityId, int[] passengerIds, int submittedTick) { - super(submittedTick); - this.playerData = playerData; - this.queuedEntityId = queuedEntityId; - this.vehicleEntityId = vehicleEntityId; - this.passengerIds = passengerIds.clone(); - } - - @Override - public void run() { - NettyEntityLocatable vehicle = playerData.entityFromID(vehicleEntityId); - if (vehicle == null) { - Logger.error("Reconciliation fail: Attempted to reconcile passengers for unknown vehicle, queuedEntityId=" + queuedEntityId + " vehicleEntityId=" + vehicleEntityId, 3, this.getClass()); - return; - } - PacketEntityViewController.get().handleEntityPassengersNow(vehicle, passengerIds, playerData, submittedTick); - } - - @Override - public String toString() { - return "PassengerReconciliationTask{" + - "submittedTick=" + submittedTick + - ", queuedEntityId=" + queuedEntityId + - ", vehicleEntityId=" + vehicleEntityId + - ", passengerIds=" + Arrays.toString(passengerIds) + - ", playerUUID=" + playerData.getPlayerUUID() + - '}'; - } -} From cdca05ca32f12095abffd47565f1137539d78beb Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:22:41 +0930 Subject: [PATCH 02/10] Fix gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b722f888..efdd86ac 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,7 @@ $RECYCLE.BIN/ *.lnk .gradle +.gradle-user/ build/ # Ignore Gradle GUI config From 3c28104ba454b38b7ef1447614ca05d5c3b0ae21 Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:25:47 +0930 Subject: [PATCH 03/10] Switch to an unresolvedLeashedEntityIDsByHolderID-style system for vehicle-passenger relationships --- .../core/players/NettyData.java | 105 +++++++++++++++++- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/games/cubi/raycastedantiesp/core/players/NettyData.java b/core/src/main/java/games/cubi/raycastedantiesp/core/players/NettyData.java index 0e6048bc..d9913984 100644 --- a/core/src/main/java/games/cubi/raycastedantiesp/core/players/NettyData.java +++ b/core/src/main/java/games/cubi/raycastedantiesp/core/players/NettyData.java @@ -3,6 +3,7 @@ import games.cubi.logs.Logger; import games.cubi.raycastedantiesp.core.utils.*; import games.cubi.raycastedantiesp.core.utils.Packet.Packets; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; @@ -10,16 +11,18 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import static games.cubi.raycastedantiesp.core.locatables.NettyEntityLocatable.NO_VEHICLE; /** * Per-player mutable state intended for Netty-side packet tracking and deferred reconciliation. */ public class NettyData implements Clearable { + private static final int DEFAULT_MAP_SIZE = 16; // // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // START Leash tracking: // - private final Int2ObjectArrayMap unresolvedLeashedEntityIDsByHolderID = new Int2ObjectArrayMap<>(16); // shot in the dark guess at capacity here. Can't be the more generic Int2ObjectMap because that doesn't expose a fast iterator. + private final Int2ObjectArrayMap unresolvedLeashedEntityIDsByHolderID = new Int2ObjectArrayMap<>(DEFAULT_MAP_SIZE); public void addUnresolvedLeash(int holderEntityID, int leashedEntityID) { unresolvedLeashedEntityIDsByHolderID.compute(holderEntityID, (ignored, existing) -> { @@ -52,11 +55,11 @@ public void removeUnresolvedLeashedEntityFromAll(int leashedEntityID) { while (iterator.hasNext()) { Int2ObjectMap.Entry entry = iterator.next(); int[] existing = entry.getValue(); - if (!IntArrayList.contains(existing, leashedEntityID)) { + if (!PrimitiveIntArrayList.contains(existing, leashedEntityID)) { continue; } - int[] updated = IntArrayList.remove(existing, leashedEntityID); - if (IntArrayList.isEmpty(updated)) { + int[] updated = PrimitiveIntArrayList.remove(existing, leashedEntityID); + if (PrimitiveIntArrayList.isEmpty(updated)) { iterator.remove(); continue; } @@ -68,6 +71,98 @@ public void removeUnresolvedLeashedEntityFromAll(int leashedEntityID) { // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // + // + // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // START Passenger tracking: + // + /** + * Latest unresolved full passenger list for a vehicle. + * This is only needed while at least one referenced passenger or the vehicle-side reconciliation has still not been spawned in for the client. The actual client deals with this somehow but we need the entity to have been spawned in before we can register the passenger relationship. + */ + private final Int2ObjectArrayMap unresolvedPassengerIDsByVehicleID = new Int2ObjectArrayMap<>(DEFAULT_MAP_SIZE); + /** + * Reverse lookup for unresolved passenger relationships. + * Lets a later passenger spawn discover which vehicle most recently claimed it as a passenger. + * This may grow larger than {@link #unresolvedPassengerIDsByVehicleID} if vehicles have several passengers, so it's an open hash map. + */ + private final Int2IntOpenHashMap unresolvedVehicleIDsByPassengerID = new Int2IntOpenHashMap(DEFAULT_MAP_SIZE); + + { + unresolvedVehicleIDsByPassengerID.defaultReturnValue(NO_VEHICLE); + } + + public int[] getUnresolvedPassengers(int vehicleEntityID) { + return PrimitiveIntArrayList.getCopyOrNull(unresolvedPassengerIDsByVehicleID.get(vehicleEntityID)); + } + + /** + * Replaces any previous unresolved passenger state for this vehicle. + * The passenger packet is authoritative, so the latest packet wins. + */ + public void setUnresolvedPassengers(int vehicleEntityID, int[] passengerIDs) { + consumeUnresolvedPassengers(vehicleEntityID); + if (PrimitiveIntArrayList.isEmpty(passengerIDs)) { + return; + } + int[] copiedPassengerIDs = passengerIDs.clone(); + unresolvedPassengerIDsByVehicleID.put(vehicleEntityID, copiedPassengerIDs); + for (int passengerID : copiedPassengerIDs) { + int previousVehicleID = unresolvedVehicleIDsByPassengerID.put(passengerID, vehicleEntityID); + if (previousVehicleID != NO_VEHICLE && previousVehicleID != vehicleEntityID) { + removePassengerFromUnresolvedVehicle(previousVehicleID, passengerID); + } + } + } + + public int[] consumeUnresolvedPassengers(int vehicleEntityID) { + int[] existing = unresolvedPassengerIDsByVehicleID.remove(vehicleEntityID); + if (PrimitiveIntArrayList.isEmpty(existing)) { + return existing; + } + for (int passengerID : existing) { + if (unresolvedVehicleIDsByPassengerID.get(passengerID) == vehicleEntityID) { + unresolvedVehicleIDsByPassengerID.remove(passengerID); + } + } + return existing; + } + + public int getUnresolvedVehicleForPassenger(int passengerEntityID) { + return unresolvedVehicleIDsByPassengerID.get(passengerEntityID); + } + + public boolean removeUnresolvedPassengerLink(int passengerEntityID, int vehicleEntityID) { + if (unresolvedVehicleIDsByPassengerID.get(passengerEntityID) != vehicleEntityID) { + return false; + } + unresolvedVehicleIDsByPassengerID.remove(passengerEntityID); + removePassengerFromUnresolvedVehicle(vehicleEntityID, passengerEntityID); + return true; + } + + public int consumeUnresolvedVehicleForPassenger(int passengerEntityID) { + int vehicleEntityID = unresolvedVehicleIDsByPassengerID.remove(passengerEntityID); + if (vehicleEntityID == NO_VEHICLE) { + return NO_VEHICLE; + } + removePassengerFromUnresolvedVehicle(vehicleEntityID, passengerEntityID); + return vehicleEntityID; + } + + private void removePassengerFromUnresolvedVehicle(int vehicleEntityID, int passengerEntityID) { + unresolvedPassengerIDsByVehicleID.computeIfPresent(vehicleEntityID, (ignored, existing) -> { + if (!PrimitiveIntArrayList.contains(existing, passengerEntityID)) { + return existing; + } + int[] updated = PrimitiveIntArrayList.remove(existing, passengerEntityID); + return PrimitiveIntArrayList.isEmpty(updated) ? null : updated; + }); + } + // + // END Passenger tracking. + // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // + // // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // START Netty entity spawn task queue: @@ -143,6 +238,8 @@ public void evictOldPendingPostSpawnTasks(int currentTick) { @Override public void clear() { unresolvedLeashedEntityIDsByHolderID.clear(); + unresolvedPassengerIDsByVehicleID.clear(); + unresolvedVehicleIDsByPassengerID.clear(); pendingPostEntitySpawnTasksByEntityID.clear(); } } From c498cfa634a7b6b48ade9e72e47611d13632bb41 Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:37:33 +0930 Subject: [PATCH 04/10] Switch to an unresolvedLeashedEntityIDsByHolderID-style system for vehicle-passenger relationships --- .../PacketEntityViewController.java | 184 ++++++++++++++---- 1 file changed, 150 insertions(+), 34 deletions(-) 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 36e0ae1a..845d6a00 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 @@ -10,6 +10,7 @@ import games.cubi.raycastedantiesp.core.utils.PrimitiveIntArrayList; import games.cubi.raycastedantiesp.core.utils.Packet; import games.cubi.raycastedantiesp.core.view.EntityView; +import it.unimi.dsi.fastutil.ints.IntArrayList; import java.util.ArrayList; import java.util.UUID; @@ -194,38 +195,43 @@ protected boolean handleEntityEffect(P packet, int entityID, PlayerData playerDa * @return Whether or not to cancel the packet event. true to cancel, false to do nothing. */ protected boolean handleEntityPassengers(int entityID, int[] passengers, PlayerData playerData, int currentTick) { - NettyEntityLocatable entity = playerData.entityFromID(entityID); - if (entity == null) { - queuePassengerRetry(playerData, entityID, entityID, passengers, currentTick); + NettyEntityLocatable vehicle = playerData.entityFromID(entityID); + int[] previousPassengers = getPreviousPassengerState(entityID, vehicle, playerData); + playerData.nettyData().consumeUnresolvedPassengers(entityID); // throw away any unresolved passengers as this new packet will have the correct passenger list + // "Stale" passengers are passengers that the previous authoritative state said were mounted, + // but the new full replacement passenger list no longer contains. + clearStalePassengerReferences(entityID, previousPassengers, passengers, playerData); + if (vehicle == null) { + playerData.nettyData().setUnresolvedPassengers(entityID, passengers); return false; } - return handleEntityPassengersNow(entity, passengers, playerData, currentTick); + return handleEntityPassengersNow(vehicle, passengers, playerData); } //This (and leash handling) leaks some info to the client, as it will receive the passenger packet even if the passengers are auto-hidden once parsed, but as the packet doesn't include any location or type info, this shouldn't be too incriminating. - boolean handleEntityPassengersNow(NettyEntityLocatable entity, int[] passengers, PlayerData playerData, int currentTick) { + boolean handleEntityPassengersNow(NettyEntityLocatable entity, int[] passengers, PlayerData playerData) { int entityID = entity.entityID(); entity.setPassengerIDs(passengers); - int blockingEntityID = NO_VEHICLE; + int[] unresolvedPassengers = null; for (int passengerID : passengers) { NettyEntityLocatable passenger = playerData.entityFromID(passengerID); if (passenger == null) { - if (blockingEntityID == NO_VEHICLE) { - blockingEntityID = passengerID; - } + unresolvedPassengers = PrimitiveIntArrayList.add(unresolvedPassengers, passengerID); continue; } passenger.setVehicleID(entityID); } - if (blockingEntityID != NO_VEHICLE) { - queuePassengerRetry(playerData, blockingEntityID, entityID, passengers, currentTick); - } + playerData.nettyData().setUnresolvedPassengers(entityID, unresolvedPassengers); checkVehicle(entity, playerData); if (cancelIfEnabledAndHidden(entityID, playerData)) return true; boolean passengersNotVisible = false; - ArrayList visiblePassengers = new ArrayList<>(passengers.length); + IntArrayList visiblePassengers = new IntArrayList(passengers.length); for (int passengerID : passengers) { - if (cancelIfEnabledAndHidden(passengerID, playerData)) { + NettyEntityLocatable passenger = playerData.entityFromID(passengerID); + if (passenger == null) { + visiblePassengers.add(passengerID); + } + else if (cancelIfEnabledAndHidden(passenger, playerData)) { passengersNotVisible = true; } else { @@ -239,34 +245,50 @@ boolean handleEntityPassengersNow(NettyEntityLocatable entity, int[] passen return passengersNotVisible; } - private void queuePassengerRetry(PlayerData playerData, int queueEntityID, int vehicleEntityID, int[] passengers, int submittedTick) { - playerData.nettyData().addPostEntitySpawnTask(queueEntityID, new PassengerReconciliationTask(playerData, queueEntityID, vehicleEntityID, passengers, submittedTick)); + private int[] getPreviousPassengerState(int vehicleID, NettyEntityLocatable vehicle, PlayerData playerData) { + if (vehicle != null) { + int[] previousPassengerIDs = vehicle.passengerIDs(); + if (!PrimitiveIntArrayList.isEmpty(previousPassengerIDs)) { + return previousPassengerIDs; + } + } + return playerData.nettyData().getUnresolvedPassengers(vehicleID); } - private void checkVehicle(NettyEntityLocatable entity, PlayerData playerData) { - int vehicleID = entity.vehicleID(); - if (vehicleID >= 0) { - NettyEntityLocatable vehicle = playerData.entityFromID(vehicleID); - if (vehicle == null) { - Logger.error(new RuntimeException("Found null vehicle when handling entity passengers packet, vehicleID=" + vehicleID + " for player: " + playerData.getPlayerUUID()), 2, PacketEntityViewController.class); - return; - } - if (cancelIfEnabledAndHidden(vehicleID, playerData)) { - //Vehicle is hidden, so this entity should be hidden as well. No need to check passengers. - return; + /** + * Clears reverse vehicle links for passengers that were part of the previous vehicle state, + * but are absent from the new authoritative replacement list. + */ + private void clearStalePassengerReferences(int vehicleID, int[] previousPassengers, int[] newPassengers, PlayerData playerData) { + if (PrimitiveIntArrayList.isEmpty(previousPassengers)) { + return; + } + for (int previousPassengerID : previousPassengers) { + if (PrimitiveIntArrayList.contains(newPassengers, previousPassengerID)) { + continue; } - //Vehicle is visible, but this entity may not be. - if (cancelIfEnabledAndHidden(entity.entityID(), playerData)) { - return; + NettyEntityLocatable previousPassenger = playerData.entityFromID(previousPassengerID); + if (previousPassenger != null && previousPassenger.vehicleID() == vehicleID) { + previousPassenger.setVehicleID(NO_VEHICLE); } - ArrayList passengers = new ArrayList<>(); - passengers.add(entity.entityID()); - sendEntityPassengerPacket(vehicleID, passengers, playerData); } } + private void checkVehicle(NettyEntityLocatable entity, PlayerData playerData) { + int vehicleID = entity.vehicleID(); + if (vehicleID < 0) { + return; + } + NettyEntityLocatable vehicle = playerData.entityFromID(vehicleID); + if (vehicle == null) { + return; + } + resendPassengerStateIfClientVisible(vehicle, playerData); + } + protected void handleDestroyEntities(int[] entityIDs, PlayerData playerData, int currentTick) { for (int entityID : entityIDs) { + clearPassengerReferencesForDestroyedEntity(entityID, playerData); clearPendingHolderReference(entityID, playerData); playerData.nettyData().removeUnresolvedLeashedEntityFromAll(entityID); playerData.nettyData().clearPendingPostSpawnTasksForEntity(entityID); @@ -280,6 +302,52 @@ protected void handleDestroyEntities(int[] entityIDs, PlayerData playerData, int } } + private void clearPassengerReferencesForDestroyedEntity(int entityID, PlayerData playerData) { + int[] unresolvedPassengers = playerData.nettyData().consumeUnresolvedPassengers(entityID); + if (!PrimitiveIntArrayList.isEmpty(unresolvedPassengers)) { + for (int passengerID : unresolvedPassengers) { + NettyEntityLocatable passenger = playerData.entityFromID(passengerID); + if (passenger != null && passenger.vehicleID() == entityID) { + passenger.setVehicleID(NO_VEHICLE); + } + } + } + + int unresolvedVehicleID = playerData.nettyData().consumeUnresolvedVehicleForPassenger(entityID); + if (unresolvedVehicleID != NO_VEHICLE) { + NettyEntityLocatable unresolvedVehicle = playerData.entityFromID(unresolvedVehicleID); + if (unresolvedVehicle != null) { + // this can occur if the vehicle existed at the time of the passenger packet but not the passenger, and somehow the passenger never got resolved to the vehicle (missing spawn packets etc). In reality this should never happen. + unresolvedVehicle.setPassengerIDs(PrimitiveIntArrayList.remove(unresolvedVehicle.passengerIDs(), entityID)); + } + } + + NettyEntityLocatable entity = playerData.entityFromID(entityID); + if (entity == null) { + return; + } + + int[] currentPassengerIDs = entity.passengerIDs(); + if (!PrimitiveIntArrayList.isEmpty(currentPassengerIDs)) { + for (int passengerID : currentPassengerIDs) { + NettyEntityLocatable passenger = playerData.entityFromID(passengerID); + if (passenger != null && passenger.vehicleID() == entityID) { + passenger.setVehicleID(NO_VEHICLE); + } + } + } + + int vehicleID = entity.vehicleID(); + if (vehicleID == NO_VEHICLE) { + return; + } + NettyEntityLocatable vehicle = playerData.entityFromID(vehicleID); + if (vehicle != null) { + vehicle.setPassengerIDs(PrimitiveIntArrayList.remove(vehicle.passengerIDs(), entityID)); + } + entity.setVehicleID(NO_VEHICLE); + } + private void clearPendingHolderReference(int holderEntityID, PlayerData playerData) { int[] pendingLeashedEntityIDs = playerData.nettyData().consumeUnresolvedLeashes(holderEntityID); if (PrimitiveIntArrayList.isEmpty(pendingLeashedEntityIDs)) { @@ -392,6 +460,54 @@ protected boolean cancelIfEnabledAndHidden(NettyEntityLocatable entity, Pla return getCorrectConfig(playerData.viewFromEntityID(entity.entityID())).enabled(); // If this statement is reached, the entity should be hidden, so if the config is enabled it is hidden. } + /** + * Replays any passenger relationship that was blocked earlier because either: + * 1. this entity is the vehicle and one or more passengers were missing, or + * 2. this entity is the passenger and the vehicle was already known. + */ + protected void reconcileUnresolvedPassengers(NettyEntityLocatable insertedEntity, PlayerData playerData) { + int[] pendingPassengers = playerData.nettyData().getUnresolvedPassengers(insertedEntity.entityID()); + if (!PrimitiveIntArrayList.isEmpty(pendingPassengers)) { + playerData.nettyData().consumeUnresolvedPassengers(insertedEntity.entityID()); + handleEntityPassengersNow(insertedEntity, pendingPassengers, playerData); + resendPassengerStateIfClientVisible(insertedEntity, playerData); + } + + int unresolvedVehicleID = playerData.nettyData().getUnresolvedVehicleForPassenger(insertedEntity.entityID()); + if (unresolvedVehicleID == NO_VEHICLE) { + return; + } + NettyEntityLocatable vehicle = playerData.entityFromID(unresolvedVehicleID); + if (vehicle == null) { + return; + } + insertedEntity.setVehicleID(unresolvedVehicleID); + playerData.nettyData().removeUnresolvedPassengerLink(insertedEntity.entityID(), unresolvedVehicleID); + resendPassengerStateIfClientVisible(vehicle, playerData); + } + + protected void resendPassengerStateIfClientVisible(NettyEntityLocatable vehicle, PlayerData playerData) { + if (!vehicle.clientVisible()) { + return; + } + sendEntityPassengerPacket(vehicle.entityID(), collectVisiblePassengers(vehicle.passengerIDs(), playerData), playerData); + } + + private IntArrayList collectVisiblePassengers(int[] passengerIDs, PlayerData playerData) { + int size = passengerIDs == null ? 0 : passengerIDs.length; + IntArrayList visiblePassengers = new IntArrayList(size); + if (passengerIDs == null) { + return visiblePassengers; + } + for (int passengerID : passengerIDs) { + NettyEntityLocatable passenger = playerData.entityFromID(passengerID); + if (passenger != null && passenger.visible()) { + visiblePassengers.add(passengerID); + } + } + return visiblePassengers; + } + /** * @return The created entity, with a default visibility of true. Does not insert the entity into any views, that is the responsibility of the caller. */ @@ -420,7 +536,7 @@ protected boolean cancelIfEnabledAndHidden(NettyEntityLocatable entity, Pla protected abstract int processEntityVelocityPacket(P packet, PlayerData playerData, int currentTick); /**Silently sends the provided array of entities as passengers for the required vehicle.*/ - protected abstract void sendEntityPassengerPacket(int vehicle, ArrayList passengers, PlayerData playerData); + protected abstract void sendEntityPassengerPacket(int vehicle, IntArrayList passengers, PlayerData playerData); protected abstract void insertEntityToPlayerView(NettyEntityLocatable entity, PlayerData playerData); From 1f3ac0e2a554cb3f350116c25784c8e91372ee4e Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:37:53 +0930 Subject: [PATCH 05/10] Avoid (un)boxing --- .../PacketEventsEntityViewController.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 39bb989c..aaca5295 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 @@ -25,6 +25,7 @@ import games.cubi.raycastedantiesp.core.view.controller.PacketEntityViewController; import games.cubi.raycastedantiesp.packetevents.locatables.PacketEventsEntity; import games.cubi.raycastedantiesp.packetevents.replaydata.PacketEventsEntityReplayData; +import it.unimi.dsi.fastutil.ints.IntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -371,13 +372,13 @@ protected int processEntityVelocityPacket(PacketWrapper packet, PlayerData pl } @Override - protected void sendEntityPassengerPacket(int vehicle, ArrayList passengers, PlayerData playerData) { + protected void sendEntityPassengerPacket(int vehicle, IntArrayList passengers, PlayerData playerData) { NettyEntityLocatable entity = playerData.entityFromID(vehicle); if (entity == null) { Logger.error("Attempted to send passenger packet for unknown entity, id=" + vehicle, 2, PacketEventsEntityViewController.class); return; } - WrapperPlayServerSetPassengers packet = new WrapperPlayServerSetPassengers(vehicle, passengers.stream().mapToInt(Integer::intValue).toArray()); + WrapperPlayServerSetPassengers packet = new WrapperPlayServerSetPassengers(vehicle, passengers.elements()); Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(playerData.getPlayerUUID()); PacketEvents.getAPI().getProtocolManager().getUser(channel).writePacketSilently(packet); } @@ -624,11 +625,15 @@ private void sendEntityAbsoluteCorrection(User viewer, PacketEventsEntity entity protected void insertEntityToPlayerView(NettyEntityLocatable entity, PlayerData playerData) { playerData.playerView().insertEntity(entity.cast()); + // Passenger relationships can arrive before spawn/pairing completes, so resolve them as soon as the entity becomes known. + reconcileUnresolvedPassengers(entity, playerData); reconcileUnresolvedLeashes(entity, playerData); } protected void insertEntityToEntityView(NettyEntityLocatable entity, PlayerData playerData) { playerData.entityView().insertEntity(entity.cast()); //todo: no need to put here, move to abstract packet view controller + // Passenger relationships can arrive before spawn/pairing completes, so resolve them as soon as the entity becomes known. + reconcileUnresolvedPassengers(entity, playerData); reconcileUnresolvedLeashes(entity, playerData); } From da07fa063b53c21c16ae562d7d636c1c505a4a7a Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:38:05 +0930 Subject: [PATCH 06/10] Bump version --- core/build.gradle.kts | 2 +- platform-paper/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index d303cbca..dc4a38cc 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { compileOnly(project(":logging")) } -val coreVersion = "0.4.1-SNAPSHOT" +val coreVersion = "0.4.2-SNAPSHOT" val isRelease = gradle.startParameter.taskNames.any { it.contains("buildRelease") diff --git a/platform-paper/build.gradle.kts b/platform-paper/build.gradle.kts index abd6e055..c793da32 100644 --- a/platform-paper/build.gradle.kts +++ b/platform-paper/build.gradle.kts @@ -41,7 +41,7 @@ val javaToolchainService = project.extensions.getByType(JavaToolchainService::cl group = "games.cubi.raycastedantiesp.paper" -val platformPaperVersion: String = "0.4.6-SNAPSHOT" +val platformPaperVersion: String = "0.4.9-SNAPSHOT" val coreVersion = project(":core").version.toString() val commitShort = providers.exec { From 0bdd1fe61cbb3fb585fc454ff5331419f68eb1c4 Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:38:11 +0930 Subject: [PATCH 07/10] Include fastutils --- packetevents/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/packetevents/build.gradle.kts b/packetevents/build.gradle.kts index ce554edb..58cee0da 100644 --- a/packetevents/build.gradle.kts +++ b/packetevents/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { 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") } java { From ccb4672da603840a1e59802b68cb58ad93ae395b Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 17:38:17 +0930 Subject: [PATCH 08/10] No logspam plz --- .../games/cubi/raycastedantiesp/paper/PaperLoggerAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-paper/src/main/java/games/cubi/raycastedantiesp/paper/PaperLoggerAdapter.java b/platform-paper/src/main/java/games/cubi/raycastedantiesp/paper/PaperLoggerAdapter.java index 97c2351f..9c1c6fc5 100644 --- a/platform-paper/src/main/java/games/cubi/raycastedantiesp/paper/PaperLoggerAdapter.java +++ b/platform-paper/src/main/java/games/cubi/raycastedantiesp/paper/PaperLoggerAdapter.java @@ -115,7 +115,7 @@ public void warning(Throwable throwable, @Range(from = 1, to = 10) int level, Cl @Deprecated @Override public void debug(String message) { - forwardLog(message, Level.INFO, 1); + forwardLog(message, Level.INFO, 10); } @Override From d8fc2bfc9382af22aca62f5b8df41fcabd00ae21 Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 19:23:42 +0930 Subject: [PATCH 09/10] Use toIntArray --- .../viewcontrollers/PacketEventsEntityViewController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 aaca5295..0b00cb17 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 @@ -378,7 +378,7 @@ protected void sendEntityPassengerPacket(int vehicle, IntArrayList passengers, P Logger.error("Attempted to send passenger packet for unknown entity, id=" + vehicle, 2, PacketEventsEntityViewController.class); return; } - WrapperPlayServerSetPassengers packet = new WrapperPlayServerSetPassengers(vehicle, passengers.elements()); + WrapperPlayServerSetPassengers packet = new WrapperPlayServerSetPassengers(vehicle, passengers.toIntArray()); Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(playerData.getPlayerUUID()); PacketEvents.getAPI().getProtocolManager().getUser(channel).writePacketSilently(packet); } From 7f836b85bf080363b2dfba992fa99082d0aed9e3 Mon Sep 17 00:00:00 2001 From: Cubicake <74905@stpeters.sa.edu.au> Date: Sun, 7 Jun 2026 19:24:05 +0930 Subject: [PATCH 10/10] Bump version --- platform-paper/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-paper/build.gradle.kts b/platform-paper/build.gradle.kts index c793da32..0143bf33 100644 --- a/platform-paper/build.gradle.kts +++ b/platform-paper/build.gradle.kts @@ -41,7 +41,7 @@ val javaToolchainService = project.extensions.getByType(JavaToolchainService::cl group = "games.cubi.raycastedantiesp.paper" -val platformPaperVersion: String = "0.4.9-SNAPSHOT" +val platformPaperVersion: String = "0.4.10-SNAPSHOT" val coreVersion = project(":core").version.toString() val commitShort = providers.exec {