From 04a0230248d042e34dbc216bc09ad518441de08f Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 8 Sep 2024 17:19:59 -0500 Subject: [PATCH] refactor: predictor next to client, smoothing --- app/client/local.js | 23 ++++++++++++----------- app/client/predictor.js | 24 ++++++++---------------- app/server/engine.js | 17 +++++++++++++++++ app/util/constants.js | 2 +- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/app/client/local.js b/app/client/local.js index af033fb..0f412b6 100644 --- a/app/client/local.js +++ b/app/client/local.js @@ -17,7 +17,13 @@ export default class LocalClient extends Client { {type: 'module'}, ); this.interpolator.addEventListener('message', (event) => { - this.accept(event.data); + const packet = event.data; + if (CLIENT_PREDICTION) { + this.predictor.postMessage([1, packet]); + } + else { + this.accept(packet); + } }); } if (CLIENT_PREDICTION) { @@ -35,12 +41,7 @@ export default class LocalClient extends Client { break; } case 1: { - if (CLIENT_INTERPOLATION) { - this.interpolator.postMessage(packet); - } - else { - this.accept(packet); - } + this.accept(packet); break; } } @@ -54,12 +55,12 @@ export default class LocalClient extends Client { } this.throughput.$$down += event.data.byteLength; const packet = decode(event.data); - if (CLIENT_PREDICTION) { - this.predictor.postMessage([1, packet]); - } - else if (CLIENT_INTERPOLATION) { + if (CLIENT_INTERPOLATION) { this.interpolator.postMessage(packet); } + else if (CLIENT_PREDICTION) { + this.predictor.postMessage([1, packet]); + } else { this.accept(packet); } diff --git a/app/client/predictor.js b/app/client/predictor.js index 79c4d01..bf76267 100644 --- a/app/client/predictor.js +++ b/app/client/predictor.js @@ -27,17 +27,8 @@ const Flow = { DOWN: 1, }; -const Stage = { - UNACK: 0, - ACK: 1, - FINISHING: 2, - FINISHED: 3, -}; - const actions = new Map(); - let ecs = new PredictionEcs({Components, Systems}); - let mainEntityId = 0; function applyClientActions(elapsed) { @@ -46,7 +37,7 @@ function applyClientActions(elapsed) { const {Controlled} = main; const finished = []; for (const [id, action] of actions) { - if (Stage.UNACK === action.stage) { + if (0 === action.finished && !action.ack) { if (!Controlled.locked) { switch (action.action.type) { case 'moveUp': @@ -60,7 +51,7 @@ function applyClientActions(elapsed) { } action.steps.push(elapsed); } - if (Stage.FINISHING === action.stage) { + if (1 === action.finished) { if (!Controlled.locked) { switch (action.action.type) { case 'moveUp': @@ -72,9 +63,9 @@ function applyClientActions(elapsed) { } } } - action.stage = Stage.FINISHED; + action.finished = 2; } - if (Stage.FINISHED === action.stage) { + if (action.ack && 2 === action.finished) { action.steps.shift(); if (0 === action.steps.length) { finished.push(id); @@ -113,13 +104,13 @@ onmessage = async (event) => { if (0 === packet.payload.value) { const ack = pending.get(packet.payload.type); const action = actions.get(ack); - action.stage = Stage.FINISHING; + action.finished = 1; pending.delete(packet.payload.type); } else { const tx = { action: packet.payload, - stage: Stage.UNACK, + ack: false,finished: 0, steps: [], }; packet.payload.ack = Math.random(); @@ -139,11 +130,12 @@ onmessage = async (event) => { switch (packet.type) { case 'ActionAck': { const action = actions.get(packet.payload.ack); - action.stage = Stage.ACK; + action.ack = true; return; } case 'EcsChange': { ecs = new PredictionEcs({Components, Systems}); + mainEntityId = 0; break; } case 'Tick': { diff --git a/app/server/engine.js b/app/server/engine.js index 20d37a6..1d78259 100644 --- a/app/server/engine.js +++ b/app/server/engine.js @@ -27,6 +27,7 @@ const textEncoder = new TextEncoder(); export default class Engine { + ackingActions = new Map(); connectedPlayers = new Map(); ecses = {}; frame = 0; @@ -250,6 +251,15 @@ export default class Engine { } } if (payload.ack) { + if (!this.ackingActions.has(connection)) { + this.ackingActions.set(connection, []); + } + this.ackingActions.get(connection).push({ + type: 'ActionAck', + payload: { + ack: payload.ack, + }, + }) this.server.send( connection, { @@ -303,6 +313,7 @@ export default class Engine { if (!connectedPlayer) { return; } + this.ackingActions.delete(connection); this.connectedPlayers.delete(connection); this.incomingActions.delete(connection); const {entity, heartbeat, id} = connectedPlayer; @@ -445,6 +456,12 @@ export default class Engine { update(elapsed) { for (const [connection, {entity}] of this.connectedPlayers) { + if (this.ackingActions.has(connection)) { + for (const ack of this.ackingActions.get(connection)) { + this.server.send(connection, ack); + } + this.ackingActions.delete(connection); + } if (!entity) { continue; } diff --git a/app/util/constants.js b/app/util/constants.js index 94d1280..3faa0a1 100644 --- a/app/util/constants.js +++ b/app/util/constants.js @@ -17,4 +17,4 @@ export const SERVER_LATENCY = 0; export const TPS = 60; -export const UPS = 15; +export const UPS = 30;