feat: Implemented server mode

This commit is contained in:
Davide Passoni
2024-12-16 17:24:02 +01:00
parent 032b74b57b
commit c2d5d4ea17
20 changed files with 389 additions and 222 deletions

View File

@@ -0,0 +1,73 @@
import React, { useEffect, useState } from "react";
import { ServerStatusUpdatedEvent } from "../events";
import { ServerStatus } from "../interfaces";
import { FaCheck, FaXmark } from "react-icons/fa6";
import { zeroAppend } from "../other/utils";
export function ServerOverlay() {
const [serverStatus, setServerStatus] = useState({} as ServerStatus);
useEffect(() => {
ServerStatusUpdatedEvent.on((status) => setServerStatus(status));
}, []);
let loadColor = "#8BFF63";
if (serverStatus.load > 1000) loadColor = "#F05252";
else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = "#FF9900";
let frameRateColor = "#8BFF63";
if (serverStatus.frameRate < 30) frameRateColor = "#F05252";
else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = "#FF9900";
const MThours = serverStatus.missionTime? serverStatus.missionTime.h: 0;
const MTminutes = serverStatus.missionTime? serverStatus.missionTime.m: 0;
const MTseconds = serverStatus.missionTime? serverStatus.missionTime.s: 0;
const EThours = Math.floor((serverStatus.elapsedTime ?? 0) / 3600);
const ETminutes = Math.floor((serverStatus.elapsedTime ?? 0) / 60) % 60;
const ETseconds = Math.round(serverStatus.elapsedTime ?? 0) % 60;
let MTtimeString = `${zeroAppend(MThours, 2)}:${zeroAppend(MTminutes, 2)}:${zeroAppend(MTseconds, 2)}`;
let ETtimeString = `${zeroAppend(EThours, 2)}:${zeroAppend(ETminutes, 2)}:${zeroAppend(ETseconds, 2)}`;
return (
<div
className={`
absolute left-0 top-0 z-50 h-full w-full flex-col bg-olympus-900 p-5
`}
>
<div className="flex-col content-center">
<h2 className="mb-10 text-3xl font-bold text-white">DCS Olympus server</h2>
<div className="flex flex-col">
<div className="flex gap-5 text-white">
<div className="w-64">Connected to DCS:</div>
<div>{serverStatus.connected? <FaCheck className={`
text-xl text-green-500
`}/> : <FaXmark className={`text-xl text-red-500`}/>}</div>
</div>
<div className="flex gap-5 text-white">
<div className="w-64">Server load:</div>
<div style={{color: loadColor}}>{serverStatus.load}</div>
</div>
<div className="flex gap-5 text-white">
<div className="w-64">Server framerate:</div>
<div style={{color: frameRateColor}}>{serverStatus.frameRate} fps</div>
</div>
<div className="flex gap-5 text-white">
<div className="w-64">Elapsed time:</div>
<div>{ETtimeString}</div>
</div>
<div className="flex gap-5 text-white">
<div className="w-64">Mission local time:</div>
<div>{MTtimeString}</div>
</div>
</div>
</div>
<img src="./images/olympus-500x500.png" className={`
absolute right-4 top-4 ml-auto flex h-24
`}></img>
</div>
);
}

View File

@@ -32,6 +32,7 @@ import { SpawnContextMenu } from "./contextmenus/spawncontextmenu";
import { CoordinatesPanel } from "./panels/coordinatespanel";
import { RadiosSummaryPanel } from "./panels/radiossummarypanel";
import { AWACSMenu } from "./panels/awacsmenu";
import { ServerOverlay } from "./serveroverlay";
export type OlympusUIState = {
mainMenuVisible: boolean;
@@ -58,7 +59,7 @@ export function UI() {
useEffect(() => {
setupApp();
})
});
return (
<div
@@ -67,50 +68,64 @@ export function UI() {
font-sans
`}
>
<Header />
{appState !== OlympusState.SERVER && (
<>
<Header />
</>
)}
<div className="flex h-full w-full flex-row-reverse">
<LoginModal open={appState === OlympusState.LOGIN} />
<ProtectionPromptModal open={appState === OlympusState.UNIT_CONTROL && appSubState == UnitControlSubState.PROTECTION} />
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
{appState === OlympusState.SERVER && <ServerOverlay />}
{appState !== OlympusState.SERVER && (
<>
<LoginModal open={appState === OlympusState.LOGIN} />
<ProtectionPromptModal open={appState === OlympusState.UNIT_CONTROL && appSubState == UnitControlSubState.PROTECTION} />
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
</>
)}
<div id="map-container" className="z-0 h-full w-screen" />
<MainMenu open={appState === OlympusState.MAIN_MENU} onClose={() => getApp().setState(OlympusState.IDLE)} />
<SpawnMenu open={appState === OlympusState.SPAWN} onClose={() => getApp().setState(OlympusState.IDLE)} />
<OptionsMenu open={appState === OlympusState.OPTIONS} onClose={() => getApp().setState(OlympusState.IDLE)} />
<UnitControlMenu
open={
appState === OlympusState.UNIT_CONTROL &&
![UnitControlSubState.FORMATION, UnitControlSubState.UNIT_EXPLOSION_MENU].includes(appSubState as UnitControlSubState)
}
onClose={() => getApp().setState(OlympusState.IDLE)}
/>
<FormationMenu
open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.FORMATION}
onClose={() => getApp().setState(OlympusState.IDLE)}
/>
<DrawingMenu open={appState === OlympusState.DRAW} onClose={() => getApp().setState(OlympusState.IDLE)} />
<AirbaseMenu open={appState === OlympusState.AIRBASE} onClose={() => getApp().setState(OlympusState.IDLE)} />
<AudioMenu open={appState === OlympusState.AUDIO} onClose={() => getApp().setState(OlympusState.IDLE)} />
<GameMasterMenu open={appState === OlympusState.GAME_MASTER} onClose={() => getApp().setState(OlympusState.IDLE)} />
<UnitExplosionMenu
open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.UNIT_EXPLOSION_MENU}
onClose={() => getApp().setState(OlympusState.IDLE)}
/>
{/*}<JTACMenu open={appState === OlympusState.JTAC} onClose={() => getApp().setState(OlympusState.IDLE)} />
{appState !== OlympusState.SERVER && (
<>
<MainMenu open={appState === OlympusState.MAIN_MENU} onClose={() => getApp().setState(OlympusState.IDLE)} />
<SpawnMenu open={appState === OlympusState.SPAWN} onClose={() => getApp().setState(OlympusState.IDLE)} />
<OptionsMenu open={appState === OlympusState.OPTIONS} onClose={() => getApp().setState(OlympusState.IDLE)} />
<UnitControlMenu
open={
appState === OlympusState.UNIT_CONTROL &&
![UnitControlSubState.FORMATION, UnitControlSubState.UNIT_EXPLOSION_MENU].includes(appSubState as UnitControlSubState)
}
onClose={() => getApp().setState(OlympusState.IDLE)}
/>
<FormationMenu
open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.FORMATION}
onClose={() => getApp().setState(OlympusState.IDLE)}
/>
<DrawingMenu open={appState === OlympusState.DRAW} onClose={() => getApp().setState(OlympusState.IDLE)} />
<AirbaseMenu open={appState === OlympusState.AIRBASE} onClose={() => getApp().setState(OlympusState.IDLE)} />
<AudioMenu open={appState === OlympusState.AUDIO} onClose={() => getApp().setState(OlympusState.IDLE)} />
<GameMasterMenu open={appState === OlympusState.GAME_MASTER} onClose={() => getApp().setState(OlympusState.IDLE)} />
<UnitExplosionMenu
open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.UNIT_EXPLOSION_MENU}
onClose={() => getApp().setState(OlympusState.IDLE)}
/>
{/*}<JTACMenu open={appState === OlympusState.JTAC} onClose={() => getApp().setState(OlympusState.IDLE)} />
<AWACSMenu open={appState === OlympusState.AWACS} onClose={() => getApp().setState(OlympusState.IDLE)} />{*/}
<MiniMapPanel />
<ControlsPanel />
<CoordinatesPanel />
<RadiosSummaryPanel />
<MiniMapPanel />
<ControlsPanel />
<CoordinatesPanel />
<RadiosSummaryPanel />
<SideBar />
<InfoBar />
<HotGroupBar />
<SideBar />
<InfoBar />
<HotGroupBar />
<MapContextMenu />
<SpawnContextMenu />
<MapContextMenu />
<SpawnContextMenu />
</>
)}
</div>
</div>
);