diff --git a/client/src/api/eventstream.tsx b/client/src/api/eventstream.tsx index b67e78e7..ab6e713a 100644 --- a/client/src/api/eventstream.tsx +++ b/client/src/api/eventstream.tsx @@ -22,6 +22,7 @@ import { FrontLine, IadsConnection, NavMesh, + SupplyRoute, Tgo, ThreatZones, UnculledZone, @@ -32,6 +33,8 @@ import { threatZonesUpdated } from "./threatZonesSlice"; import { unculledZonesUpdated } from "./unculledZonesSlice"; import { LatLng } from "leaflet"; import { updateIadsConnection, removeIadsConnection } from "./iadsNetworkSlice"; +import { IadsConnection } from "./_liberationApi"; +import { supplyRoutesUpdated } from "./supplyRoutesSlice"; interface GameUpdateEvents { updated_flight_positions: { [id: string]: LatLng }; @@ -52,6 +55,7 @@ interface GameUpdateEvents { updated_control_points: ControlPoint[]; updated_iads: IadsConnection[]; deleted_iads: string[]; + updated_supply_routes: SupplyRoute[]; reset_on_map_center: LatLng | null; game_unloaded: boolean; new_turn: boolean; @@ -135,6 +139,10 @@ export const handleStreamedEvents = ( dispatch(updateIadsConnection(events.updated_iads)); } + if (events.updated_supply_routes.length > 0) { + dispatch(supplyRoutesUpdated(events.updated_supply_routes)); + } + if (events.reset_on_map_center != null) { reloadGameState(dispatch); } diff --git a/client/src/api/supplyRoutesSlice.ts b/client/src/api/supplyRoutesSlice.ts index d5683b89..c3dd0864 100644 --- a/client/src/api/supplyRoutesSlice.ts +++ b/client/src/api/supplyRoutesSlice.ts @@ -1,26 +1,46 @@ import { RootState } from "../app/store"; import { gameLoaded, gameUnloaded } from "./actions"; import { SupplyRoute } from "./liberationApi"; -import { createSlice } from "@reduxjs/toolkit"; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; interface SupplyRoutesState { - routes: SupplyRoute[]; + routes: { [id: string]: SupplyRoute }; } const initialState: SupplyRoutesState = { - routes: [], + routes: {}, }; export const supplyRoutesSlice = createSlice({ name: "supplyRoutes", initialState, - reducers: {}, + reducers: { + updated: (state, action: PayloadAction) => { + for (const route of action.payload) { + const id = route.id; + let points = route.points; + if (points.length > 0) { + state.routes[id] = route; + } else if (id in state.routes) { + points = state.routes[id].points; + state.routes[id] = route; + state.routes[id].points = points; + } else { + console.log("Trying to update a route that doesn't exist without points..."); + } + } + }, + }, extraReducers: (builder) => { builder.addCase(gameLoaded, (state, action) => { - state.routes = action.payload.supply_routes; + state.routes = {} + for (const route of action.payload.supply_routes) + { + state.routes[route.id] = route; + } }); builder.addCase(gameUnloaded, (state) => { - state.routes = []; + state.routes = {}; }); }, }); @@ -28,3 +48,5 @@ export const supplyRoutesSlice = createSlice({ export const selectSupplyRoutes = (state: RootState) => state.supplyRoutes; export default supplyRoutesSlice.reducer; + +export const { updated: supplyRoutesUpdated } = supplyRoutesSlice.actions; diff --git a/client/src/components/supplyroute/SupplyRoute.tsx b/client/src/components/supplyroute/SupplyRoute.tsx index c2605928..270b2087 100644 --- a/client/src/components/supplyroute/SupplyRoute.tsx +++ b/client/src/components/supplyroute/SupplyRoute.tsx @@ -56,8 +56,7 @@ export default function SupplyRoute(props: SupplyRouteProps) { return ( (path.current = ref)} > diff --git a/client/src/components/supplyrouteslayer/SupplyRoutesLayer.tsx b/client/src/components/supplyrouteslayer/SupplyRoutesLayer.tsx index 07f642e0..d4d4f4a6 100644 --- a/client/src/components/supplyrouteslayer/SupplyRoutesLayer.tsx +++ b/client/src/components/supplyrouteslayer/SupplyRoutesLayer.tsx @@ -7,7 +7,7 @@ export default function SupplyRoutesLayer() { const routes = useAppSelector(selectSupplyRoutes).routes; return ( - {routes.map((route) => { + {Object.values(routes).map( route => { return ; })} diff --git a/game/server/eventstream/models.py b/game/server/eventstream/models.py index cfe4a063..fcedb6e7 100644 --- a/game/server/eventstream/models.py +++ b/game/server/eventstream/models.py @@ -15,6 +15,7 @@ from game.server.mapzones.models import ThreatZonesJs from game.server.navmesh.models import NavMeshJs from game.server.tgos.models import TgoJs from game.server.mapzones.models import UnculledZoneJs +from game.server.supplyroutes.models import SupplyRouteJs if TYPE_CHECKING: from game import Game @@ -40,6 +41,7 @@ class GameUpdateEventsJs(BaseModel): updated_control_points: list[ControlPointJs] updated_iads: list[IadsConnectionJs] deleted_iads: set[UUID] + updated_supply_routes: list[SupplyRouteJs] reset_on_map_center: LeafletPoint | None game_unloaded: bool new_turn: bool @@ -57,6 +59,7 @@ class GameUpdateEventsJs(BaseModel): updated_threat_zones = {} updated_unculled_zones = [] updated_iads = [] + updated_supply_routes = [] if game is not None: new_combats = [ FrozenCombatJs.for_combat(c, game.theater) for c in events.new_combats @@ -76,6 +79,10 @@ class GameUpdateEventsJs(BaseModel): updated_unculled_zones = UnculledZoneJs.from_game(game) for node in events.updated_iads: updated_iads.extend(IadsConnectionJs.connections_for_node(node)) + if events.updated_supply_routes: + updated_supply_routes = SupplyRouteJs.all_in_game(game) + for route in updated_supply_routes: + route.points = [] return GameUpdateEventsJs( updated_flight_positions={ @@ -108,6 +115,7 @@ class GameUpdateEventsJs(BaseModel): ], updated_iads=updated_iads, deleted_iads=events.deleted_iads_connections, + updated_supply_routes=updated_supply_routes, reset_on_map_center=events.reset_on_map_center, game_unloaded=events.game_unloaded, new_turn=events.new_turn, diff --git a/game/server/supplyroutes/models.py b/game/server/supplyroutes/models.py index 8db6485e..68a8da5e 100644 --- a/game/server/supplyroutes/models.py +++ b/game/server/supplyroutes/models.py @@ -64,7 +64,7 @@ class TransportFinder: class SupplyRouteJs(BaseModel): - id: UUID + id: str points: list[LeafletPoint] front_active: bool is_sea: bool @@ -91,7 +91,7 @@ class SupplyRouteJs(BaseModel): # # https://reactjs.org/docs/lists-and-keys.html#keys # https://github.com/dcs-liberation/dcs_liberation/issues/2167 - id=uuid.uuid4(), + id=f"{a}:{b}", points=[p.latlng() for p in points], front_active=not sea and a.front_is_active(b), is_sea=sea, diff --git a/game/sim/gameupdateevents.py b/game/sim/gameupdateevents.py index cf7371ef..913b5963 100644 --- a/game/sim/gameupdateevents.py +++ b/game/sim/gameupdateevents.py @@ -38,6 +38,7 @@ class GameUpdateEvents: updated_control_points: set[ControlPoint] = field(default_factory=set) updated_iads: set[IadsNetworkNode] = field(default_factory=set) deleted_iads_connections: set[UUID] = field(default_factory=set) + updated_supply_routes: bool = False reset_on_map_center: LatLng | None = None game_unloaded: bool = False new_turn: bool = False @@ -133,6 +134,10 @@ class GameUpdateEvents: def delete_iads_connection(self, connection_id: UUID) -> GameUpdateEvents: self.deleted_iads_connections.add(connection_id) + return self.update_supply_routes() + + def update_supply_routes(self) -> GameUpdateEvents: + self.updated_supply_routes = True return self def game_loaded(self, game: Game | None) -> GameUpdateEvents: diff --git a/game/transfers.py b/game/transfers.py index 09524a4e..07623e93 100644 --- a/game/transfers.py +++ b/game/transfers.py @@ -571,6 +571,12 @@ class PendingTransfers: def __iter__(self) -> Iterator[TransferOrder]: yield from self.pending_transfers + def _send_supply_route_event_stream_update(self) -> None: + from game.server import EventStream + + with EventStream.event_context() as events: + events.update_supply_routes() + @property def pending_transfer_count(self) -> int: return len(self.pending_transfers) @@ -605,6 +611,7 @@ class PendingTransfers: transfer.origin.base.commit_losses(transfer.units) self.pending_transfers.append(transfer) self.arrange_transport(transfer) + self._send_supply_route_event_stream_update() def split_transfer(self, transfer: TransferOrder, size: int) -> TransferOrder: """Creates a smaller transfer that is a subset of the original.""" @@ -659,6 +666,7 @@ class PendingTransfers: self.cancel_transport(transfer.transport, transfer) self.pending_transfers.remove(transfer) transfer.origin.base.commission_units(transfer.units) + self._send_supply_route_event_stream_update() def perform_transfers(self) -> None: """ @@ -675,6 +683,7 @@ class PendingTransfers: self.pending_transfers = incomplete self.convoys.disband_all() self.cargo_ships.disband_all() + self._send_supply_route_event_stream_update() def plan_transports(self) -> None: """