mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Handle map reset when the game is loaded/unloaded.
https://github.com/dcs-liberation/dcs_liberation/issues/2039 Partial fix for https://github.com/dcs-liberation/dcs_liberation/issues/2045 (now works in the new map, old one not fixed yet).
This commit is contained in:
@@ -1,17 +1,14 @@
|
||||
import LiberationMap from "./components/liberationmap";
|
||||
import useEventStream from "./hooks/useEventSteam";
|
||||
import useInitialGameState from "./hooks/useInitialGameState";
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
function App() {
|
||||
const mapCenter: LatLng = new LatLng(25.58, 54.9);
|
||||
|
||||
useInitialGameState();
|
||||
useEventStream();
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<LiberationMap mapCenter={mapCenter} />
|
||||
<LiberationMap />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
5
client/src/api/actions.ts
Normal file
5
client/src/api/actions.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import Game from "./game";
|
||||
import { createAction } from "@reduxjs/toolkit";
|
||||
|
||||
export const gameLoaded = createAction<Game>("game/loaded");
|
||||
export const gameUnloaded = createAction("game/unloaded");
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { ControlPoint } from "./controlpoint";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
@@ -14,21 +15,28 @@ export const controlPointsSlice = createSlice({
|
||||
name: "controlPoints",
|
||||
initialState,
|
||||
reducers: {
|
||||
setControlPoints: (state, action: PayloadAction<ControlPoint[]>) => {
|
||||
state.controlPoints = {};
|
||||
for (const cp of action.payload) {
|
||||
state.controlPoints[cp.id] = cp;
|
||||
}
|
||||
},
|
||||
updateControlPoint: (state, action: PayloadAction<ControlPoint>) => {
|
||||
const cp = action.payload;
|
||||
state.controlPoints[cp.id] = cp;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.controlPoints = action.payload.control_points.reduce(
|
||||
(acc: { [key: number]: ControlPoint }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.controlPoints = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { setControlPoints, updateControlPoint } =
|
||||
controlPointsSlice.actions;
|
||||
export const { updateControlPoint } = controlPointsSlice.actions;
|
||||
|
||||
export const selectControlPoints = (state: RootState) => state.controlPoints;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AppDispatch } from "../app/store";
|
||||
import { gameUnloaded } from "./actions";
|
||||
import backend from "./backend";
|
||||
import Combat from "./combat";
|
||||
import { endCombat, newCombat, updateCombat } from "./combatSlice";
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
updateFrontLine,
|
||||
} from "./frontLinesSlice";
|
||||
import FrontLine from "./frontline";
|
||||
import reloadGameState from "./gamestate";
|
||||
import Tgo from "./tgo";
|
||||
import { updateTgo } from "./tgosSlice";
|
||||
import { LatLng } from "leaflet";
|
||||
@@ -41,6 +43,8 @@ interface GameUpdateEvents {
|
||||
deleted_front_lines: string[];
|
||||
updated_tgos: string[];
|
||||
updated_control_points: number[];
|
||||
reset_on_map_center: LatLng | null;
|
||||
game_unloaded: boolean;
|
||||
}
|
||||
|
||||
export const handleStreamedEvents = (
|
||||
@@ -114,4 +118,12 @@ export const handleStreamedEvents = (
|
||||
dispatch(updateControlPoint(cp));
|
||||
});
|
||||
}
|
||||
|
||||
if (events.reset_on_map_center != null) {
|
||||
reloadGameState(dispatch);
|
||||
}
|
||||
|
||||
if (events.game_unloaded) {
|
||||
dispatch(gameUnloaded());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { Flight } from "./flight";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { LatLng } from "leaflet";
|
||||
@@ -17,9 +18,6 @@ export const flightsSlice = createSlice({
|
||||
name: "flights",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearFlights: (state) => {
|
||||
state.flights = {};
|
||||
},
|
||||
registerFlight: (state, action: PayloadAction<Flight>) => {
|
||||
const flight = action.payload;
|
||||
if (flight.id in state.flights) {
|
||||
@@ -51,10 +49,25 @@ export const flightsSlice = createSlice({
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.selected = null;
|
||||
state.flights = action.payload.flights.reduce(
|
||||
(acc: { [key: string]: Flight }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.selected = null;
|
||||
state.flights = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
clearFlights,
|
||||
registerFlight,
|
||||
unregisterFlight,
|
||||
updateFlight,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import FrontLine from "./frontline";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
@@ -14,12 +15,6 @@ export const frontLinesSlice = createSlice({
|
||||
name: "frontLines",
|
||||
initialState,
|
||||
reducers: {
|
||||
setFrontLines: (state, action: PayloadAction<FrontLine[]>) => {
|
||||
state.fronts = {};
|
||||
for (const front of action.payload) {
|
||||
state.fronts[front.id] = front;
|
||||
}
|
||||
},
|
||||
addFrontLine: (state, action: PayloadAction<FrontLine>) => {
|
||||
const front = action.payload;
|
||||
state.fronts[front.id] = front;
|
||||
@@ -32,9 +27,23 @@ export const frontLinesSlice = createSlice({
|
||||
delete state.fronts[action.payload];
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.fronts = action.payload.front_lines.reduce(
|
||||
(acc: { [key: string]: FrontLine }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.fronts = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { setFrontLines, addFrontLine, updateFrontLine, deleteFrontLine } =
|
||||
export const { addFrontLine, updateFrontLine, deleteFrontLine } =
|
||||
frontLinesSlice.actions;
|
||||
|
||||
export const selectFrontLines = (state: RootState) => state.frontLines;
|
||||
|
||||
15
client/src/api/game.ts
Normal file
15
client/src/api/game.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ControlPoint } from "./controlpoint";
|
||||
import { Flight } from "./flight";
|
||||
import FrontLine from "./frontline";
|
||||
import SupplyRoute from "./supplyroute";
|
||||
import Tgo from "./tgo";
|
||||
import { LatLngLiteral } from "leaflet";
|
||||
|
||||
export default interface Game {
|
||||
control_points: ControlPoint[];
|
||||
tgos: Tgo[];
|
||||
supply_routes: SupplyRoute[];
|
||||
front_lines: FrontLine[];
|
||||
flights: Flight[];
|
||||
map_center: LatLngLiteral;
|
||||
}
|
||||
17
client/src/api/gamestate.ts
Normal file
17
client/src/api/gamestate.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { AppDispatch } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import backend from "./backend";
|
||||
import Game from "./game";
|
||||
|
||||
export default function reloadGameState(dispatch: AppDispatch) {
|
||||
backend
|
||||
.get("/game")
|
||||
.catch((error) => console.log(`Error fetching game state: ${error}`))
|
||||
.then((response) => {
|
||||
if (response == null || response.data == null) {
|
||||
dispatch(gameUnloaded());
|
||||
return;
|
||||
}
|
||||
dispatch(gameLoaded(response.data as Game));
|
||||
});
|
||||
}
|
||||
30
client/src/api/mapSlice.ts
Normal file
30
client/src/api/mapSlice.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { LatLngLiteral } from "leaflet";
|
||||
|
||||
interface MapState {
|
||||
center: LatLngLiteral;
|
||||
}
|
||||
|
||||
const initialState: MapState = {
|
||||
center: { lat: 0, lng: 0 },
|
||||
};
|
||||
|
||||
const mapSlice = createSlice({
|
||||
name: "map",
|
||||
initialState: initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.center = action.payload.map_center;
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.center = { lat: 0, lng: 0 };
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const selectMapCenter = (state: RootState) => state.map.center;
|
||||
|
||||
export default mapSlice.reducer;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import SupplyRoute from "./supplyroute";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
interface SupplyRoutesState {
|
||||
routes: SupplyRoute[];
|
||||
@@ -13,15 +14,17 @@ const initialState: SupplyRoutesState = {
|
||||
export const supplyRoutesSlice = createSlice({
|
||||
name: "supplyRoutes",
|
||||
initialState,
|
||||
reducers: {
|
||||
setSupplyRoutes: (state, action: PayloadAction<SupplyRoute[]>) => {
|
||||
state.routes = action.payload;
|
||||
},
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.routes = action.payload.supply_routes;
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.routes = [];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { setSupplyRoutes } = supplyRoutesSlice.actions;
|
||||
|
||||
export const selectSupplyRoutes = (state: RootState) => state.supplyRoutes;
|
||||
|
||||
export default supplyRoutesSlice.reducer;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RootState } from "../app/store";
|
||||
import { gameLoaded, gameUnloaded } from "./actions";
|
||||
import { Tgo } from "./tgo";
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
@@ -14,20 +15,28 @@ export const tgosSlice = createSlice({
|
||||
name: "tgos",
|
||||
initialState,
|
||||
reducers: {
|
||||
setTgos: (state, action: PayloadAction<Tgo[]>) => {
|
||||
state.tgos = {};
|
||||
for (const tgo of action.payload) {
|
||||
state.tgos[tgo.id] = tgo;
|
||||
}
|
||||
},
|
||||
updateTgo: (state, action: PayloadAction<Tgo>) => {
|
||||
const tgo = action.payload;
|
||||
state.tgos[tgo.id] = tgo;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(gameLoaded, (state, action) => {
|
||||
state.tgos = action.payload.tgos.reduce(
|
||||
(acc: { [key: string]: Tgo }, curr) => {
|
||||
acc[curr.id] = curr;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
builder.addCase(gameUnloaded, (state) => {
|
||||
state.tgos = {};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { setTgos, updateTgo } = tgosSlice.actions;
|
||||
export const { updateTgo } = tgosSlice.actions;
|
||||
|
||||
export const selectTgos = (state: RootState) => state.tgos;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import combatReducer from "../api/combatSlice";
|
||||
import controlPointsReducer from "../api/controlPointsSlice";
|
||||
import flightsReducer from "../api/flightsSlice";
|
||||
import frontLinesReducer from "../api/frontLinesSlice";
|
||||
import mapReducer from "../api/mapSlice";
|
||||
import supplyRoutesReducer from "../api/supplyRoutesSlice";
|
||||
import tgosReducer from "../api/tgosSlice";
|
||||
import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
|
||||
@@ -13,6 +14,7 @@ export const store = configureStore({
|
||||
controlPoints: controlPointsReducer,
|
||||
flights: flightsReducer,
|
||||
frontLines: frontLinesReducer,
|
||||
map: mapReducer,
|
||||
supplyRoutes: supplyRoutesReducer,
|
||||
tgos: tgosReducer,
|
||||
[apiSlice.reducerPath]: apiSlice.reducer,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { selectMapCenter } from "../../api/mapSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
import AircraftLayer from "../aircraftlayer";
|
||||
import AirDefenseRangeLayer from "../airdefenserangelayer";
|
||||
import CombatLayer from "../combatlayer";
|
||||
@@ -7,17 +9,23 @@ import FrontLinesLayer from "../frontlineslayer";
|
||||
import SupplyRoutesLayer from "../supplyrouteslayer";
|
||||
import TgosLayer from "../tgoslayer/TgosLayer";
|
||||
import "./LiberationMap.css";
|
||||
import { LatLng } from "leaflet";
|
||||
import { Map } from "leaflet";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { BasemapLayer } from "react-esri-leaflet";
|
||||
import { LayersControl, MapContainer, ScaleControl } from "react-leaflet";
|
||||
|
||||
interface GameProps {
|
||||
mapCenter: LatLng;
|
||||
}
|
||||
|
||||
export default function LiberationMap(props: GameProps) {
|
||||
export default function LiberationMap() {
|
||||
const map = useRef<Map>();
|
||||
const mapCenter = useAppSelector(selectMapCenter);
|
||||
useEffect(() => {
|
||||
map.current?.setView(mapCenter, 8, { animate: true, duration: 1 });
|
||||
});
|
||||
return (
|
||||
<MapContainer zoom={8} center={props.mapCenter} zoomControl={false}>
|
||||
<MapContainer
|
||||
zoom={8}
|
||||
zoomControl={false}
|
||||
whenCreated={(mapInstance) => (map.current = mapInstance)}
|
||||
>
|
||||
<ScaleControl />
|
||||
<LayersControl collapsed={false}>
|
||||
<LayersControl.BaseLayer name="Imagery Clarity" checked>
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
import backend from "../api/backend";
|
||||
import { setControlPoints } from "../api/controlPointsSlice";
|
||||
import { ControlPoint } from "../api/controlpoint";
|
||||
import { Flight } from "../api/flight";
|
||||
import { registerFlight } from "../api/flightsSlice";
|
||||
import { setFrontLines } from "../api/frontLinesSlice";
|
||||
import FrontLine from "../api/frontline";
|
||||
import { setSupplyRoutes } from "../api/supplyRoutesSlice";
|
||||
import SupplyRoute from "../api/supplyroute";
|
||||
import Tgo from "../api/tgo";
|
||||
import { setTgos } from "../api/tgosSlice";
|
||||
import reloadGameState from "../api/gamestate";
|
||||
import { useAppDispatch } from "../app/hooks";
|
||||
import { useEffect } from "react";
|
||||
|
||||
@@ -19,48 +9,7 @@ import { useEffect } from "react";
|
||||
export const useInitialGameState = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
useEffect(() => {
|
||||
backend
|
||||
.get("/control-points")
|
||||
.catch((error) => console.log(`Error fetching control points: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
dispatch(setControlPoints(response.data as ControlPoint[]));
|
||||
}
|
||||
});
|
||||
backend
|
||||
.get("/tgos")
|
||||
.catch((error) => console.log(`Error fetching TGOs: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
dispatch(setTgos(response.data as Tgo[]));
|
||||
}
|
||||
});
|
||||
backend
|
||||
.get("/supply-routes")
|
||||
.catch((error) => console.log(`Error fetching supply routes: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
dispatch(setSupplyRoutes(response.data as SupplyRoute[]));
|
||||
}
|
||||
});
|
||||
backend
|
||||
.get("/front-lines")
|
||||
.catch((error) => console.log(`Error fetching front-lines: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
dispatch(setFrontLines(response.data as FrontLine[]));
|
||||
}
|
||||
});
|
||||
backend
|
||||
.get("/flights?with_waypoints=true")
|
||||
.catch((error) => console.log(`Error fetching flights: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
for (const flight of response.data) {
|
||||
dispatch(registerFlight(flight as Flight));
|
||||
}
|
||||
}
|
||||
});
|
||||
reloadGameState(dispatch);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user