Files
suaveplan/packages/foundation/bitpacker/bitpacker-game/src/integration-manager/integration-manager.ts
2026-05-27 23:47:53 +01:00

393 lines
11 KiB
TypeScript

/**
* Integration manager for GameBitPacker features
*/
import {
type NetworkSimulationOptions,
type NetworkSimulationStats,
NetworkSimulator,
PacketStatus,
} from "@suaveplan/bitpacker-network";
import type { BitPackerSchema } from "@suaveplan/types";
import { GameInterpolator } from "../interpolation/interpolation.js";
import {
PositionPredictor,
RotationPredictor,
} from "../prediction/prediction.js";
import * as GameQuantization from "../quantization/quantization.js";
import { StateReconciliation } from "../reconciliation/reconciliation.js";
import type {
GameBitPackerOptions,
GameState,
IGameBitPacker,
InputData,
InterpolationMethod,
InterpolationMethodEnum,
NetworkStats,
PhysicsFunction,
PlayerInput,
PlayerState,
Rotation,
Vector3,
} from "../types.js";
import type {
GameIntegrationManagerInterface,
IntegrationManagerFeatures,
} from "./integration-manager.types.js";
export class GameIntegrationManager<T extends BitPackerSchema>
implements GameIntegrationManagerInterface<T>
{
private positionPredictor: PositionPredictor | null = null;
private rotationPredictor: RotationPredictor | null = null;
private interpolator: GameInterpolator | null = null;
private reconciliation: StateReconciliation<T> | null = null;
private networkSimulator: NetworkSimulator | null = null;
constructor(
private options: GameBitPackerOptions,
private schema: T
) {
this.initializeFeatures();
}
/**
* Initialize enhanced features based on options
*/
private initializeFeatures(): void {
if (this.options.enablePrediction) {
this.positionPredictor = new PositionPredictor(
this.options.predictionOptions
);
this.rotationPredictor = new RotationPredictor(
this.options.predictionOptions
);
}
if (this.options.enableInterpolation) {
this.interpolator = new GameInterpolator(
this.options.interpolationOptions
);
}
if (this.options.enableReconciliation) {
this.reconciliation = new StateReconciliation<T>(
this.schema,
this.options.reconciliationOptions
);
}
if (this.options.enableNetworkSimulation) {
this.networkSimulator = new NetworkSimulator(
this.options.networkSimulationOptions
);
}
}
/**
* Set BitPacker reference for reconciliation
*/
public setBitPacker(bitPacker: IGameBitPacker<T>): void {
if (this.reconciliation) {
this.reconciliation.setBitPacker(bitPacker);
}
}
/**
* Interpolate position
*/
public interpolatePosition(
from: Vector3,
to: Vector3,
t: number,
method?: InterpolationMethod | InterpolationMethodEnum
): Vector3 {
if (!this.interpolator) {
// Simple linear interpolation fallback
return {
x: from.x + (to.x - from.x) * t,
y: from.y + (to.y - from.y) * t,
z: from.z + (to.z - from.z) * t,
};
}
return this.interpolator.interpolatePosition(from, to, t, method);
}
/**
* Interpolate rotation
*/
public interpolateRotation(
from: Rotation,
to: Rotation,
t: number,
method?: InterpolationMethod | InterpolationMethodEnum
): Rotation {
if (!this.interpolator) {
// Simple linear interpolation fallback with angle wrapping
if (
"yaw" in from &&
"pitch" in from &&
"yaw" in to &&
"pitch" in to
) {
return {
yaw: GameQuantization.interpolateAngle(from.yaw, to.yaw, t),
pitch: GameQuantization.interpolateAngle(
from.pitch,
to.pitch,
t
),
};
}
return to;
}
return this.interpolator.interpolateRotation(from, to, t, method);
}
/**
* Predict position
*/
public predictPosition(deltaTime: number): Vector3 | null {
if (!this.positionPredictor) {
return null;
}
return this.positionPredictor.predictPosition(deltaTime);
}
/**
* Predict rotation
*/
public predictRotation(deltaTime: number): Rotation | null {
if (!this.rotationPredictor) {
return null;
}
return this.rotationPredictor.predictRotation(deltaTime);
}
/**
* Add position to history
*/
public addPositionToHistory(
position: Vector3,
timestamp = Date.now()
): void {
if (this.positionPredictor) {
this.positionPredictor.addPosition(position, timestamp);
}
}
/**
* Add rotation to history
*/
public addRotationToHistory(
rotation: Rotation,
timestamp = Date.now()
): void {
if (this.rotationPredictor) {
this.rotationPredictor.addRotation(rotation, timestamp);
}
}
/**
* Process input
*/
public processInput<I extends InputData>(
input: I,
timestamp = Date.now()
): number {
if (!this.reconciliation) {
return -1;
}
return this.reconciliation.processInput(
input as unknown as PlayerInput,
timestamp
);
}
/**
* Apply client-side prediction
*/
public predictClientState<S extends GameState, I extends InputData>(
currentState: S,
applyPhysics: PhysicsFunction<S, I>,
deltaTime: number
): S {
if (!this.reconciliation) {
return currentState;
}
return this.reconciliation.predictClientState(
currentState as unknown as PlayerState,
applyPhysics as unknown as PhysicsFunction<
PlayerState,
PlayerInput
>,
deltaTime
) as unknown as S;
}
/**
* Process server update
*/
public processServerUpdate<S extends GameState>(
serverState: S,
serverSequence: number,
serverTimestamp: number
): boolean {
if (!this.reconciliation) {
return false;
}
return this.reconciliation.processServerUpdate(
serverState as unknown as PlayerState,
serverSequence,
serverTimestamp
);
}
/**
* Reconcile state
*/
public reconcileState<S extends GameState, I extends InputData>(
currentState: S,
applyPhysics: PhysicsFunction<S, I>
): S {
if (!this.reconciliation) {
return currentState;
}
return this.reconciliation.reconcileState(
currentState as unknown as PlayerState,
applyPhysics as unknown as PhysicsFunction<PlayerState, PlayerInput>
) as unknown as S;
}
/**
* Send packet with network simulation
*/
public sendPacket(
data: Uint8Array,
callback: (data: Uint8Array) => void,
dropCallback?: () => void
): void {
if (!this.networkSimulator) {
callback(data);
return;
}
this.networkSimulator.sendPacket(data, (status, resultData) => {
if (status === PacketStatus.LOST && dropCallback) {
dropCallback();
} else if (resultData) {
callback(resultData);
}
});
}
/**
* Set network options
*/
public setNetworkOptions(options: NetworkSimulationOptions): void {
if (this.networkSimulator) {
this.networkSimulator.setOptions(options);
}
}
/**
* Get network statistics
*/
public getNetworkStats(): NetworkStats {
if (!this.networkSimulator) {
return {
packetsSent: 0,
packetsReceived: 0,
packetsLost: 0,
packetsDelayed: 0,
packetsReordered: 0,
averageLatency: 0,
averageJitter: 0,
totalBytes: 0,
};
}
const stats =
this.networkSimulator.getStats() as NetworkSimulationStats;
return {
packetsSent: stats.packetsSent,
packetsReceived: stats.packetsReceived,
packetsLost: stats.packetsLost,
packetsDelayed: 0, // Not in NetworkSimulationStats
packetsReordered: stats.packetsReordered,
averageLatency: stats.averageLatency,
averageJitter: 0, // Not in NetworkSimulationStats
totalBytes: stats.bytesTransferred,
};
}
/**
* Set features dynamically
*/
public setFeatures(features: IntegrationManagerFeatures): void {
// Update prediction
if (features.prediction !== undefined) {
this.options.enablePrediction = features.prediction;
if (features.prediction && !this.positionPredictor) {
this.positionPredictor = new PositionPredictor(
this.options.predictionOptions
);
this.rotationPredictor = new RotationPredictor(
this.options.predictionOptions
);
} else if (!features.prediction) {
this.positionPredictor = null;
this.rotationPredictor = null;
}
}
// Update interpolation
if (features.interpolation !== undefined) {
this.options.enableInterpolation = features.interpolation;
if (features.interpolation && !this.interpolator) {
this.interpolator = new GameInterpolator(
this.options.interpolationOptions
);
} else if (!features.interpolation) {
this.interpolator = null;
}
}
// Update reconciliation
if (features.reconciliation !== undefined) {
this.options.enableReconciliation = features.reconciliation;
if (features.reconciliation && !this.reconciliation) {
this.reconciliation = new StateReconciliation<T>(
this.schema,
this.options.reconciliationOptions
);
} else if (!features.reconciliation) {
this.reconciliation = null;
}
}
// Update network simulation
if (features.networkSimulation !== undefined) {
this.options.enableNetworkSimulation = features.networkSimulation;
if (features.networkSimulation && !this.networkSimulator) {
this.networkSimulator = new NetworkSimulator(
this.options.networkSimulationOptions
);
} else if (!features.networkSimulation) {
if (this.networkSimulator) {
this.networkSimulator.destroy();
}
this.networkSimulator = null;
}
}
}
/**
* Clean up resources
*/
public destroy(): void {
if (this.networkSimulator) {
this.networkSimulator.destroy();
this.networkSimulator = null;
}
}
}