feat: added laser code change, target move, and delete

Note: deleted lasers are not removed from table and keep being drawn. Also added a cooler looking server page
This commit is contained in:
Davide Passoni
2025-01-30 16:20:31 +01:00
parent cc902aec04
commit 9525982161
19 changed files with 844 additions and 218 deletions

View File

@@ -4,12 +4,26 @@ import { ServerStatus } from "../interfaces";
import { FaCheck, FaXmark } from "react-icons/fa6";
import { zeroAppend } from "../other/utils";
import { colors } from "../constants/constants";
import { CircularProgressbar, buildStyles } from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
import { Line } from "react-chartjs-2";
import { Chart, LineElement, LinearScale, Title, CategoryScale, Legend, PointElement } from "chart.js";
Chart.register(LineElement, LinearScale, Title, CategoryScale, Legend, PointElement);
export function ServerOverlay() {
const [serverStatus, setServerStatus] = useState({} as ServerStatus);
const [loadData, setLoadData] = useState<number[]>([]);
const [frameRateData, setFrameRateData] = useState<number[]>([]);
const [timeLabels, setTimeLabels] = useState<string[]>([]);
useEffect(() => {
ServerStatusUpdatedEvent.on((status) => setServerStatus(status));
ServerStatusUpdatedEvent.on((status) => {
setServerStatus(status);
setLoadData((prevData) => [...prevData, status.load].slice(-300));
setFrameRateData((prevData) => [...prevData, status.frameRate].slice(-300));
setTimeLabels((prevLabels) => [...prevLabels, new Date().toLocaleTimeString()].slice(-300));
});
}, []);
let loadColor = colors.OLYMPUS_GREEN;
@@ -20,9 +34,9 @@ export function ServerOverlay() {
if (serverStatus.frameRate < 30) frameRateColor = colors.OLYMPUS_RED;
else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = colors.OLYMPUS_ORANGE;
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 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;
@@ -31,43 +45,136 @@ export function ServerOverlay() {
let MTtimeString = `${zeroAppend(MThours, 2)}:${zeroAppend(MTminutes, 2)}:${zeroAppend(MTseconds, 2)}`;
let ETtimeString = `${zeroAppend(EThours, 2)}:${zeroAppend(ETminutes, 2)}:${zeroAppend(ETseconds, 2)}`;
const missionTime = new Date();
missionTime.setHours(MThours);
missionTime.setMinutes(MTminutes);
missionTime.setSeconds(MTseconds);
const data = {
labels: timeLabels,
datasets: [
{
label: "Server Load",
data: loadData,
borderColor: colors.LIGHT_BLUE,
borderWidth: 2,
fill: false,
pointRadius: 0,
yAxisID: "y-load", // Specify the y-axis for load
},
{
label: "Server Framerate",
data: frameRateData,
borderColor: colors.WHITE,
borderWidth: 2,
fill: false,
pointRadius: 0,
yAxisID: "y-framerate", // Specify the y-axis for framerate
},
],
};
const options = {
animation: false,
responsive: true,
scales: {
x: {
type: "category",
labels: timeLabels,
},
"y-framerate": {
type: "linear",
position: "left",
beginAtZero: true,
max: 120, // Max value for framerate
},
"y-load": {
type: "linear",
position: "right",
beginAtZero: true,
max: 2000, // Max value for load
grid: {
drawOnChartArea: false, // Only want the grid lines for one axis to show up
},
},
},
plugins: {
legend: {
display: true,
position: "top",
},
},
};
return (
<div
className={`
absolute left-0 top-0 z-50 h-full w-full flex-col bg-olympus-900 p-5
absolute left-0 top-0 z-50 flex h-full w-full flex-col bg-black
bg-opacity-80 p-5 backdrop-blur-sm animate-fadeIn
`}
>
<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
className={`
m-auto flex w-3/4 max-w-4xl flex-col items-center rounded-lg bg-white
bg-opacity-10 p-5 shadow-lg animate-slideIn
`}
>
<h2 className="mb-5 text-4xl font-bold text-white drop-shadow-lg">DCS Olympus Server</h2>
<div className="flex w-full gap-12">
<div className="flex w-72 flex-col gap-4 text-lg text-white">
<div className="flex justify-between">
<div className="font-semibold">Connected to DCS:</div>
<div>{serverStatus.connected ? <FaCheck className={`
text-2xl text-green-500
`} /> : <FaXmark className={`text-2xl text-red-500`} />}</div>
</div>
<div className="flex justify-between">
<div className="font-semibold">Elapsed time:</div>
<div>{ETtimeString}</div>
</div>
<div className="flex justify-between">
<div className="font-semibold">Mission local time:</div>
<div>{MTtimeString}</div>
</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 className="flex items-center justify-between gap-5 text-white">
<div className="flex flex-col items-center">
<div className="mb-2 font-semibold">Load</div>
<div className="h-24 w-24">
<CircularProgressbar
value={serverStatus.load}
maxValue={2000}
text={`${serverStatus.load}`}
styles={buildStyles({
textColor: loadColor,
pathColor: loadColor,
trailColor: "rgba(255, 255, 255, 0.2)",
})}
/>
</div>
</div>
<div className="flex flex-col items-center">
<div className="mb-2 font-semibold">Framerate</div>
<div className="h-24 w-24">
<CircularProgressbar
value={serverStatus.frameRate}
maxValue={120}
text={`${serverStatus.frameRate} fps`}
styles={buildStyles({
textColor: frameRateColor,
pathColor: frameRateColor,
trailColor: "rgba(255, 255, 255, 0.2)",
})}
/>
</div>
</div>
</div>
</div>
<div className="mt-10 w-full flex-1">
{/* @ts-ignore */}
<Line data={data} options={options} />
</div>
</div>
<img src="images/olympus-500x500.png" className={`
absolute right-4 top-4 ml-auto flex h-24
`}></img>
</div>
);
}

View File

@@ -121,4 +121,32 @@ input[type="range"]:focus::-moz-range-thumb {
100% {
width: 200px;
}
}
}
.bouncing-ball {
position: relative;
width: 100px;
height: 100px;
background-color: transparent;
border-radius: 50%;
overflow: hidden;
animation: bounce 2s infinite;
}
.ball-logo {
width: 100%;
height: 100%;
object-fit: cover;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-150px);
}
60% {
transform: translateY(-75px);
}
}

View File

@@ -8,13 +8,7 @@ import { MainMenu } from "./panels/mainmenu";
import { SideBar } from "./panels/sidebar";
import { OptionsMenu } from "./panels/optionsmenu";
import { MapHiddenTypes, MapOptions } from "../types/types";
import {
NO_SUBSTATE,
OlympusState,
OlympusSubState,
OptionsSubstate,
UnitControlSubState
} from "../constants/constants";
import { NO_SUBSTATE, OlympusState, OlympusSubState, OptionsSubstate, UnitControlSubState } from "../constants/constants";
import { getApp, setupApp } from "../olympusapp";
import { LoginModal } from "./modals/loginmodal";
@@ -30,7 +24,7 @@ import { ProtectionPromptModal } from "./modals/protectionpromptmodal";
import { KeybindModal } from "./modals/keybindmodal";
import { UnitExplosionMenu } from "./panels/unitexplosionmenu";
import { JTACMenu } from "./panels/jtacmenu";
import { AppStateChangedEvent } from "../events";
import { AppStateChangedEvent, ServerStatusUpdatedEvent } from "../events";
import { GameMasterMenu } from "./panels/gamemastermenu";
import { InfoBar } from "./panels/infobar";
import { HotGroupBar } from "./panels/hotgroupsbar";
@@ -41,6 +35,7 @@ import { AWACSMenu } from "./panels/awacsmenu";
import { ServerOverlay } from "./serveroverlay";
import { ImportExportModal } from "./modals/importexportmodal";
import { WarningModal } from "./modals/warningmodal";
import { ServerStatus } from "../interfaces";
export type OlympusUIState = {
mainMenuVisible: boolean;
@@ -57,12 +52,19 @@ export type OlympusUIState = {
export function UI() {
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
const [appSubState, setAppSubState] = useState(NO_SUBSTATE as OlympusSubState);
const [serverStatus, setServerStatus] = useState({} as ServerStatus);
const [connectedOnce, setConnectedOnce] = useState(false);
useEffect(() => {
AppStateChangedEvent.on((state, subState) => {
setAppState(state);
setAppSubState(subState);
});
ServerStatusUpdatedEvent.on((status) => {
// If we connected at least once, record it
if (status.connected) setConnectedOnce(true);
setServerStatus(status);
});
}, []);
useEffect(() => {
@@ -76,11 +78,7 @@ export function UI() {
font-sans
`}
>
{appState !== OlympusState.SERVER && (
<>
<Header />
</>
)}
{appState !== OlympusState.SERVER && <Header />}
<div className="flex h-full w-full flex-row-reverse">
{appState === OlympusState.SERVER && <ServerOverlay />}
@@ -138,6 +136,45 @@ export function UI() {
</>
)}
</div>
{!serverStatus.connected && appState !== OlympusState.LOGIN && (
<div
className={`
absolute left-0 top-0 z-50 flex h-screen w-screen items-center
justify-center bg-gray-900 bg-opacity-80 text-white backdrop-blur-sm
`}
>
<div className="flex flex-col items-center justify-center gap-4">
<div className="bouncing-ball">
<img
src="images/olympus-500x500.png"
alt="Olympus Logo"
className={`ball-logo`}
/>
</div>
{!connectedOnce && <div>Establishing connection</div>}
{connectedOnce && <div>Connection lost</div>}
{!connectedOnce && <div className="text-gray-400">Trying to connect with the server, please wait...</div>}
{connectedOnce && (
<div className="text-gray-400">
Try reloading this page. However, this usually means that you internet connection is down, or that the server is offline, paused, or loading a
new mission.
</div>
)}
</div>
</div>
)}
{appState === OlympusState.NOT_INITIALIZED && (
<div
className={`
absolute left-0 top-0 z-50 flex h-screen w-screen items-center
justify-center bg-gray-900 text-white
`}
>
Loading...
</div>
)}
</div>
);
}