airframe.io / src /game /multiplayer /SimulationSystem.ts
LATTICEWORKS's picture
feat: integrate Google Earth 3D Tiles and optimize engine hooks
565f6e7
Raw
History Blame Contribute Delete
3.63 kB
import { Pilot, GroundTarget, SkyZone } from "../../types";
import { FlightPhysicsEngine } from "../flightModel";
import { destructible, control } from "../../types/components";
import { BotAISystem } from "../botAISystem";
import { FlightCommand } from "../../types";
import { MAP_REGISTRY } from "../content/maps/registry";
const FIXED_DT = 1 / 60;
export class SimulationSystem {
public updateSimulation(
accumulator: number,
deltaMs: number,
matchEnded: boolean,
tickCallback: () => void
): number {
if (matchEnded) return accumulator;
let newAccumulator = accumulator + (deltaMs / 1000);
if (newAccumulator > 0.1) newAccumulator = 0.1; // clamp death spiral
while (newAccumulator >= FIXED_DT) {
tickCallback();
newAccumulator -= FIXED_DT;
}
return newAccumulator;
}
public tickPlayers(
pilots: Map<string, Pilot>,
statePlayers: any,
getNextInput: (id: string, neutral: FlightCommand) => { command: FlightCommand, seq?: number },
neutralCommand: () => FlightCommand,
tickCooldowns: (p: Pilot, dt: number) => void,
updateDeadPilot: (p: Pilot, dt: number) => void,
enforceMapBoundary: (p: Pilot, dt: number) => void,
handleWeaponFiring: (p: Pilot, pF: boolean, sF: boolean, dt: number, seq?: number) => void,
syncRapierBody: (id: string, p: Pilot) => void,
recordHistory: (id: string, p: Pilot) => void,
mapId: string
) {
for (const [sessionId, pilot] of pilots.entries()) {
if (pilot.isBot) continue;
const { command, seq } = getNextInput(sessionId, neutralCommand());
const schemaPlayer = statePlayers.get(sessionId);
if (schemaPlayer && seq !== undefined) {
schemaPlayer.lastProcessedSeq = seq;
}
if (destructible(pilot.entity).damageModel!.fuselage <= 0) {
updateDeadPilot(pilot, FIXED_DT);
} else {
tickCooldowns(pilot, FIXED_DT);
FlightPhysicsEngine.update(pilot, command, FIXED_DT, mapId);
enforceMapBoundary(pilot, FIXED_DT);
handleWeaponFiring(pilot, command.primaryFire, command.secondaryFire, FIXED_DT, seq);
}
syncRapierBody(sessionId, pilot);
recordHistory(sessionId, pilot);
}
}
public tickBots(
pilots: Map<string, Pilot>,
groundTargets: GroundTarget[],
skyZones: SkyZone[],
tickCooldowns: (p: Pilot, dt: number) => void,
updateDeadPilot: (p: Pilot, dt: number) => void,
enforceMapBoundary: (p: Pilot, dt: number) => void,
handleWeaponFiring: (p: Pilot, pF: boolean, sF: boolean, dt: number, seq?: number) => void,
syncRapierBody: (id: string, p: Pilot) => void,
recordHistory: (id: string, p: Pilot) => void,
mapId: string,
neutralCommand: () => FlightCommand
) {
for (const [botId, pilot] of pilots.entries()) {
if (!pilot.isBot) continue;
if (destructible(pilot.entity).damageModel!.fuselage <= 0) {
updateDeadPilot(pilot, FIXED_DT);
} else {
tickCooldowns(pilot, FIXED_DT);
BotAISystem.runAIConsensus(
pilot,
FIXED_DT,
Array.from(pilots.values()),
groundTargets,
skyZones,
(p, prim, sec, d) => handleWeaponFiring(p, prim, sec, d),
MAP_REGISTRY[mapId]?.world.radius ?? 6000
);
const botCtrl = control(pilot.entity);
const cmd = botCtrl.lastCommand || neutralCommand();
FlightPhysicsEngine.update(pilot, cmd, FIXED_DT, mapId);
enforceMapBoundary(pilot, FIXED_DT);
}
syncRapierBody(botId, pilot);
recordHistory(botId, pilot);
}
}
}