Front-end 'push' updates for SupplyRoutes

This commit is contained in:
Raffson 2022-06-15 02:21:35 +02:00
parent 74142536e9
commit e9917ba00e
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
8 changed files with 62 additions and 11 deletions

View File

@ -22,6 +22,7 @@ import {
FrontLine, FrontLine,
IadsConnection, IadsConnection,
NavMesh, NavMesh,
SupplyRoute,
Tgo, Tgo,
ThreatZones, ThreatZones,
UnculledZone, UnculledZone,
@ -32,6 +33,8 @@ import { threatZonesUpdated } from "./threatZonesSlice";
import { unculledZonesUpdated } from "./unculledZonesSlice"; import { unculledZonesUpdated } from "./unculledZonesSlice";
import { LatLng } from "leaflet"; import { LatLng } from "leaflet";
import { updateIadsConnection, removeIadsConnection } from "./iadsNetworkSlice"; import { updateIadsConnection, removeIadsConnection } from "./iadsNetworkSlice";
import { IadsConnection } from "./_liberationApi";
import { supplyRoutesUpdated } from "./supplyRoutesSlice";
interface GameUpdateEvents { interface GameUpdateEvents {
updated_flight_positions: { [id: string]: LatLng }; updated_flight_positions: { [id: string]: LatLng };
@ -52,6 +55,7 @@ interface GameUpdateEvents {
updated_control_points: ControlPoint[]; updated_control_points: ControlPoint[];
updated_iads: IadsConnection[]; updated_iads: IadsConnection[];
deleted_iads: string[]; deleted_iads: string[];
updated_supply_routes: SupplyRoute[];
reset_on_map_center: LatLng | null; reset_on_map_center: LatLng | null;
game_unloaded: boolean; game_unloaded: boolean;
new_turn: boolean; new_turn: boolean;
@ -135,6 +139,10 @@ export const handleStreamedEvents = (
dispatch(updateIadsConnection(events.updated_iads)); 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) { if (events.reset_on_map_center != null) {
reloadGameState(dispatch); reloadGameState(dispatch);
} }

View File

@ -1,26 +1,46 @@
import { RootState } from "../app/store"; import { RootState } from "../app/store";
import { gameLoaded, gameUnloaded } from "./actions"; import { gameLoaded, gameUnloaded } from "./actions";
import { SupplyRoute } from "./liberationApi"; import { SupplyRoute } from "./liberationApi";
import { createSlice } from "@reduxjs/toolkit"; import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface SupplyRoutesState { interface SupplyRoutesState {
routes: SupplyRoute[]; routes: { [id: string]: SupplyRoute };
} }
const initialState: SupplyRoutesState = { const initialState: SupplyRoutesState = {
routes: [], routes: {},
}; };
export const supplyRoutesSlice = createSlice({ export const supplyRoutesSlice = createSlice({
name: "supplyRoutes", name: "supplyRoutes",
initialState, initialState,
reducers: {}, reducers: {
updated: (state, action: PayloadAction<SupplyRoute[]>) => {
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) => { extraReducers: (builder) => {
builder.addCase(gameLoaded, (state, action) => { 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) => { builder.addCase(gameUnloaded, (state) => {
state.routes = []; state.routes = {};
}); });
}, },
}); });
@ -28,3 +48,5 @@ export const supplyRoutesSlice = createSlice({
export const selectSupplyRoutes = (state: RootState) => state.supplyRoutes; export const selectSupplyRoutes = (state: RootState) => state.supplyRoutes;
export default supplyRoutesSlice.reducer; export default supplyRoutesSlice.reducer;
export const { updated: supplyRoutesUpdated } = supplyRoutesSlice.actions;

View File

@ -56,8 +56,7 @@ export default function SupplyRoute(props: SupplyRouteProps) {
return ( return (
<Polyline <Polyline
positions={props.route.points} positions={props.route.points}
color={color} pathOptions={{ color: color, weight: weight}}
weight={weight}
ref={(ref) => (path.current = ref)} ref={(ref) => (path.current = ref)}
> >
<SupplyRouteTooltip {...props} /> <SupplyRouteTooltip {...props} />

View File

@ -7,7 +7,7 @@ export default function SupplyRoutesLayer() {
const routes = useAppSelector(selectSupplyRoutes).routes; const routes = useAppSelector(selectSupplyRoutes).routes;
return ( return (
<LayerGroup> <LayerGroup>
{routes.map((route) => { {Object.values(routes).map( route => {
return <SupplyRoute key={route.id} route={route} />; return <SupplyRoute key={route.id} route={route} />;
})} })}
</LayerGroup> </LayerGroup>

View File

@ -15,6 +15,7 @@ from game.server.mapzones.models import ThreatZonesJs
from game.server.navmesh.models import NavMeshJs from game.server.navmesh.models import NavMeshJs
from game.server.tgos.models import TgoJs from game.server.tgos.models import TgoJs
from game.server.mapzones.models import UnculledZoneJs from game.server.mapzones.models import UnculledZoneJs
from game.server.supplyroutes.models import SupplyRouteJs
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
@ -40,6 +41,7 @@ class GameUpdateEventsJs(BaseModel):
updated_control_points: list[ControlPointJs] updated_control_points: list[ControlPointJs]
updated_iads: list[IadsConnectionJs] updated_iads: list[IadsConnectionJs]
deleted_iads: set[UUID] deleted_iads: set[UUID]
updated_supply_routes: list[SupplyRouteJs]
reset_on_map_center: LeafletPoint | None reset_on_map_center: LeafletPoint | None
game_unloaded: bool game_unloaded: bool
new_turn: bool new_turn: bool
@ -57,6 +59,7 @@ class GameUpdateEventsJs(BaseModel):
updated_threat_zones = {} updated_threat_zones = {}
updated_unculled_zones = [] updated_unculled_zones = []
updated_iads = [] updated_iads = []
updated_supply_routes = []
if game is not None: if game is not None:
new_combats = [ new_combats = [
FrozenCombatJs.for_combat(c, game.theater) for c in events.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) updated_unculled_zones = UnculledZoneJs.from_game(game)
for node in events.updated_iads: for node in events.updated_iads:
updated_iads.extend(IadsConnectionJs.connections_for_node(node)) 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( return GameUpdateEventsJs(
updated_flight_positions={ updated_flight_positions={
@ -108,6 +115,7 @@ class GameUpdateEventsJs(BaseModel):
], ],
updated_iads=updated_iads, updated_iads=updated_iads,
deleted_iads=events.deleted_iads_connections, deleted_iads=events.deleted_iads_connections,
updated_supply_routes=updated_supply_routes,
reset_on_map_center=events.reset_on_map_center, reset_on_map_center=events.reset_on_map_center,
game_unloaded=events.game_unloaded, game_unloaded=events.game_unloaded,
new_turn=events.new_turn, new_turn=events.new_turn,

View File

@ -64,7 +64,7 @@ class TransportFinder:
class SupplyRouteJs(BaseModel): class SupplyRouteJs(BaseModel):
id: UUID id: str
points: list[LeafletPoint] points: list[LeafletPoint]
front_active: bool front_active: bool
is_sea: bool is_sea: bool
@ -91,7 +91,7 @@ class SupplyRouteJs(BaseModel):
# #
# https://reactjs.org/docs/lists-and-keys.html#keys # https://reactjs.org/docs/lists-and-keys.html#keys
# https://github.com/dcs-liberation/dcs_liberation/issues/2167 # https://github.com/dcs-liberation/dcs_liberation/issues/2167
id=uuid.uuid4(), id=f"{a}:{b}",
points=[p.latlng() for p in points], points=[p.latlng() for p in points],
front_active=not sea and a.front_is_active(b), front_active=not sea and a.front_is_active(b),
is_sea=sea, is_sea=sea,

View File

@ -38,6 +38,7 @@ class GameUpdateEvents:
updated_control_points: set[ControlPoint] = field(default_factory=set) updated_control_points: set[ControlPoint] = field(default_factory=set)
updated_iads: set[IadsNetworkNode] = field(default_factory=set) updated_iads: set[IadsNetworkNode] = field(default_factory=set)
deleted_iads_connections: set[UUID] = 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 reset_on_map_center: LatLng | None = None
game_unloaded: bool = False game_unloaded: bool = False
new_turn: bool = False new_turn: bool = False
@ -133,6 +134,10 @@ class GameUpdateEvents:
def delete_iads_connection(self, connection_id: UUID) -> GameUpdateEvents: def delete_iads_connection(self, connection_id: UUID) -> GameUpdateEvents:
self.deleted_iads_connections.add(connection_id) 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 return self
def game_loaded(self, game: Game | None) -> GameUpdateEvents: def game_loaded(self, game: Game | None) -> GameUpdateEvents:

View File

@ -571,6 +571,12 @@ class PendingTransfers:
def __iter__(self) -> Iterator[TransferOrder]: def __iter__(self) -> Iterator[TransferOrder]:
yield from self.pending_transfers 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 @property
def pending_transfer_count(self) -> int: def pending_transfer_count(self) -> int:
return len(self.pending_transfers) return len(self.pending_transfers)
@ -605,6 +611,7 @@ class PendingTransfers:
transfer.origin.base.commit_losses(transfer.units) transfer.origin.base.commit_losses(transfer.units)
self.pending_transfers.append(transfer) self.pending_transfers.append(transfer)
self.arrange_transport(transfer) self.arrange_transport(transfer)
self._send_supply_route_event_stream_update()
def split_transfer(self, transfer: TransferOrder, size: int) -> TransferOrder: def split_transfer(self, transfer: TransferOrder, size: int) -> TransferOrder:
"""Creates a smaller transfer that is a subset of the original.""" """Creates a smaller transfer that is a subset of the original."""
@ -659,6 +666,7 @@ class PendingTransfers:
self.cancel_transport(transfer.transport, transfer) self.cancel_transport(transfer.transport, transfer)
self.pending_transfers.remove(transfer) self.pending_transfers.remove(transfer)
transfer.origin.base.commission_units(transfer.units) transfer.origin.base.commission_units(transfer.units)
self._send_supply_route_event_stream_update()
def perform_transfers(self) -> None: def perform_transfers(self) -> None:
""" """
@ -675,6 +683,7 @@ class PendingTransfers:
self.pending_transfers = incomplete self.pending_transfers = incomplete
self.convoys.disband_all() self.convoys.disband_all()
self.cargo_ships.disband_all() self.cargo_ships.disband_all()
self._send_supply_route_event_stream_update()
def plan_transports(self) -> None: def plan_transports(self) -> None:
""" """