package zombie.vehicles; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import zombie.characters.IsoGameCharacter; import zombie.characters.IsoPlayer; import zombie.core.physics.CarController; /** * Resolves the effective driver for constraint auth in chained towing. * For middle vehicles in a chain, scan through links so long trains still resolve authority. */ public final class LandtrainConstraintAuthHelper { private LandtrainConstraintAuthHelper() { } public static IsoGameCharacter resolveConstraintDriver(BaseVehicle vehicle) { if (vehicle == null) { return null; } IsoGameCharacter driver = findDriverAlongChain(vehicle, true); if (driver != null) return driver; return findDriverAlongChain(vehicle, false); } private static IsoGameCharacter findDriverAlongChain(BaseVehicle start, boolean towardFront) { BaseVehicle cursor = start; Set visited = new HashSet<>(); while (cursor != null) { int id = cursor.getId(); if (!visited.add(id)) { // Safety: malformed link graph; stop scanning. return null; } IsoGameCharacter driver = cursor.getDriver(); if (driver != null) { return driver; } cursor = towardFront ? cursor.getVehicleTowedBy() : cursor.getVehicleTowing(); } return null; } public static boolean isEnterBlockedLandtrain( BaseVehicle vehicle, IsoGameCharacter chr, int seat) { if (isVehicleBeingTowedInLandtrain(vehicle)) { return true; } return vehicle != null && vehicle.isExitBlocked(chr, seat); } public static boolean isEnterBlocked2Landtrain(BaseVehicle vehicle, int seat) { if (isVehicleBeingTowedInLandtrain(vehicle)) { return true; } return vehicle != null && vehicle.isExitBlocked2(seat); } private static boolean isVehicleBeingTowedInLandtrain(BaseVehicle vehicle) { return vehicle != null && vehicle.getVehicleTowedBy() != null; } public static void authorizationChangedLandtrain(BaseVehicle vehicle, IsoGameCharacter character) { if (vehicle == null) { return; } if (character != null) { applyAuthorizationAcrossChain( vehicle, BaseVehicle.Authorization.Local, character.getOnlineID(), false); } else { applyAuthorizationAcrossChain(vehicle, BaseVehicle.Authorization.Server, -1, false); } } public static void authorizationClientCollideLandtrain(BaseVehicle vehicle, IsoPlayer driver) { if (vehicle == null || driver == null || vehicle.getDriver() != null) { return; } applyAuthorizationAcrossChain( vehicle, BaseVehicle.Authorization.LocalCollide, driver.getOnlineID(), true); } public static void authorizationServerCollideLandtrain( BaseVehicle vehicle, short playerID, boolean isCollide) { if (vehicle == null) { return; } if (vehicle.isNetPlayerAuthorization(BaseVehicle.Authorization.Local)) { return; } if (isCollide) { applyAuthorizationAcrossChain( vehicle, BaseVehicle.Authorization.LocalCollide, playerID, false); return; } BaseVehicle.Authorization auth = playerID == -1 ? BaseVehicle.Authorization.Server : BaseVehicle.Authorization.Local; applyAuthorizationAcrossChain(vehicle, auth, playerID, false); } public static void authorizationServerOnSeatLandtrain( BaseVehicle vehicle, IsoPlayer player, boolean enter) { if (vehicle == null || player == null) { return; } BaseVehicle vehicleA = vehicle.getVehicleTowing(); BaseVehicle vehicleB = vehicle.getVehicleTowedBy(); if (vehicle.isNetPlayerId((short) -1) && enter) { if (vehicleA != null && vehicleA.getDriver() == null) { vehicle.addPointConstraint( null, vehicleA, vehicle.getTowAttachmentSelf(), vehicleA.getTowAttachmentSelf()); } else if (vehicleB != null && vehicleB.getDriver() == null) { vehicle.addPointConstraint( null, vehicleB, vehicle.getTowAttachmentSelf(), vehicleB.getTowAttachmentSelf()); } else { applyAuthorizationAcrossChain( vehicle, BaseVehicle.Authorization.Local, player.getOnlineID(), false); } } else if (vehicle.isNetPlayerId(player.getOnlineID()) && !enter) { if (vehicleA != null && vehicleA.getDriver() != null) { vehicleA.addPointConstraint( null, vehicle, vehicleA.getTowAttachmentSelf(), vehicle.getTowAttachmentSelf()); } else if (vehicleB != null && vehicleB.getDriver() != null) { vehicleB.addPointConstraint( null, vehicle, vehicleB.getTowAttachmentSelf(), vehicle.getTowAttachmentSelf()); } else { applyAuthorizationAcrossChain(vehicle, BaseVehicle.Authorization.Server, -1, false); } } } private static List collectConnectedChain(BaseVehicle start) { ArrayList chain = new ArrayList<>(); if (start == null) { return chain; } ArrayDeque pending = new ArrayDeque<>(); Set visitedIds = new HashSet<>(); pending.add(start); while (!pending.isEmpty()) { BaseVehicle cursor = pending.removeFirst(); if (cursor == null) { continue; } int id = cursor.getId(); if (!visitedIds.add(id)) { continue; } chain.add(cursor); BaseVehicle front = cursor.getVehicleTowedBy(); BaseVehicle rear = cursor.getVehicleTowing(); if (front != null && front != cursor) { pending.add(front); } if (rear != null && rear != cursor) { pending.add(rear); } } return chain; } private static void applyAuthorizationAcrossChain( BaseVehicle start, BaseVehicle.Authorization authorization, int playerId, boolean refreshSimulation) { long now = System.currentTimeMillis(); for (BaseVehicle vehicle : collectConnectedChain(start)) { if (vehicle == null) { continue; } vehicle.setNetPlayerAuthorization(authorization, playerId); if (refreshSimulation) { vehicle.authSimulationTime = now; if (vehicle.interpolation != null) { vehicle.interpolation.clear(); } } } } public static void safeParkControllerLandtrain(CarController controller) { if (controller != null) { controller.park(); } } }