mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 21:04:14 +00:00
Implemented time-sliced catch-up on the main thread to keep input responsive.
src/client/ClientGameRunner.ts now drains pending game updates in small chunks (max 100 updates or ~8ms per slice) via requestAnimationFrame, merging and rendering per slice, and only clears the processing flag when the queue is empty.
This commit is contained in:
@@ -477,23 +477,25 @@ export class ClientGameRunner {
|
||||
}
|
||||
|
||||
private processPendingUpdates() {
|
||||
if (this.isProcessingUpdates) {
|
||||
return;
|
||||
}
|
||||
if (this.pendingUpdates.length === 0) {
|
||||
if (this.isProcessingUpdates || this.pendingUpdates.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProcessingUpdates = true;
|
||||
const batch = this.pendingUpdates.splice(0);
|
||||
const processSlice = () => {
|
||||
const SLICE_BUDGET_MS = 8; // keep UI responsive while catching up
|
||||
const MAX_PER_SLICE = 100;
|
||||
const sliceStart = performance.now();
|
||||
const batch: GameUpdateViewData[] = [];
|
||||
|
||||
let processedCount = 0;
|
||||
let lastTickDuration: number | undefined;
|
||||
let lastTick: number | undefined;
|
||||
let processedCount = 0;
|
||||
let lastTickDuration: number | undefined;
|
||||
let lastTick: number | undefined;
|
||||
|
||||
try {
|
||||
for (const gu of batch) {
|
||||
while (this.pendingUpdates.length > 0) {
|
||||
const gu = this.pendingUpdates.shift() as GameUpdateViewData;
|
||||
processedCount++;
|
||||
batch.push(gu);
|
||||
|
||||
this.transport.turnComplete();
|
||||
gu.updates[GameUpdateType.Hash].forEach((hu: HashUpdate) => {
|
||||
@@ -509,30 +511,41 @@ export class ClientGameRunner {
|
||||
lastTickDuration = gu.tickExecutionDuration;
|
||||
}
|
||||
lastTick = gu.tick;
|
||||
}
|
||||
} finally {
|
||||
this.isProcessingUpdates = false;
|
||||
}
|
||||
|
||||
if (processedCount > 0 && lastTick !== undefined) {
|
||||
const combinedGu = this.mergeGameUpdates(batch);
|
||||
if (combinedGu) {
|
||||
this.gameView.update(combinedGu);
|
||||
const elapsed = performance.now() - sliceStart;
|
||||
if (processedCount >= MAX_PER_SLICE || elapsed >= SLICE_BUDGET_MS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.renderer.tick();
|
||||
this.eventBus.emit(
|
||||
new TickMetricsEvent(
|
||||
lastTickDuration,
|
||||
this.currentTickDelay,
|
||||
this.backlogTurns,
|
||||
this.renderEveryN,
|
||||
),
|
||||
);
|
||||
if (processedCount > 0 && lastTick !== undefined) {
|
||||
const combinedGu = this.mergeGameUpdates(batch);
|
||||
if (combinedGu) {
|
||||
this.gameView.update(combinedGu);
|
||||
}
|
||||
|
||||
// Reset tick delay for next measurement
|
||||
this.currentTickDelay = undefined;
|
||||
}
|
||||
this.renderer.tick();
|
||||
this.eventBus.emit(
|
||||
new TickMetricsEvent(
|
||||
lastTickDuration,
|
||||
this.currentTickDelay,
|
||||
this.backlogTurns,
|
||||
this.renderEveryN,
|
||||
),
|
||||
);
|
||||
|
||||
// Reset tick delay for next measurement
|
||||
this.currentTickDelay = undefined;
|
||||
}
|
||||
|
||||
if (this.pendingUpdates.length > 0) {
|
||||
requestAnimationFrame(processSlice);
|
||||
} else {
|
||||
this.isProcessingUpdates = false;
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(processSlice);
|
||||
}
|
||||
|
||||
private adaptRenderFrequency(frameDuration: number) {
|
||||
|
||||
Reference in New Issue
Block a user