@rpgjs/physic
Version:
A deterministic 2D top-down physics library for RPG, sandbox and MMO games
89 lines (88 loc) • 2.64 kB
JavaScript
class PredictionController {
constructor(config) {
this.config = config;
this.frameCounter = 0;
this.history = [];
this.pendingSnapshot = null;
this.lastAckFrame = 0;
this.lastAckTick = 0;
this.correctionThreshold = config.correctionThreshold ?? 5;
this.historyTtlMs = config.historyTtlMs ?? 2e3;
this.maxHistoryEntries = config.maxHistoryEntries ?? 200;
}
recordInput(direction, timestamp) {
const frame = ++this.frameCounter;
const tick = this.config.getPhysicsTick();
this.history.push({ frame, tick, timestamp, direction });
this.trimHistory(timestamp);
return { frame, tick };
}
attachPredictedState(frame, state) {
const entry = this.history.find((item) => item.frame === frame);
if (entry) {
entry.state = state;
}
}
hasPendingInputs() {
return this.history.length > 0;
}
queueServerSnapshot(snapshot) {
if (this.hasPendingInputs()) {
this.pendingSnapshot = snapshot;
return;
}
this.applySnapshot(snapshot);
}
tryApplyPendingSnapshot() {
if (!this.pendingSnapshot || this.hasPendingInputs()) {
return;
}
this.applySnapshot(this.pendingSnapshot);
this.pendingSnapshot = null;
}
applyServerAck(ack) {
if (typeof ack.frame !== "number") {
return;
}
this.lastAckFrame = Math.max(this.lastAckFrame, ack.frame);
if (typeof ack.serverTick === "number") {
this.lastAckTick = Math.max(this.lastAckTick, ack.serverTick);
}
this.history = this.history.filter((entry) => entry.frame > this.lastAckFrame);
if (ack.state && !this.hasPendingInputs()) {
this.applySnapshot(ack.state);
this.pendingSnapshot = null;
return;
}
if (this.pendingSnapshot && !this.hasPendingInputs()) {
this.applySnapshot(this.pendingSnapshot);
this.pendingSnapshot = null;
}
}
cleanup(now) {
this.trimHistory(now);
}
trimHistory(now) {
const cutoff = now - this.historyTtlMs;
this.history = this.history.filter((entry) => entry.timestamp >= cutoff);
if (this.history.length > this.maxHistoryEntries) {
this.history = this.history.slice(-this.maxHistoryEntries);
}
}
applySnapshot(snapshot) {
const current = this.config.getCurrentState();
const dx = current.x - snapshot.x;
const dy = current.y - snapshot.y;
const distance = Math.hypot(dx, dy);
if (distance > this.correctionThreshold) {
this.config.setAuthoritativeState(snapshot);
this.history = [];
return;
}
this.history = [];
}
}
export {
PredictionController
};
//# sourceMappingURL=index44.js.map