Improve network stack (less traffic)
This commit is contained in:
parent
43874d0f54
commit
ddf68f316e
130
src/game.js
130
src/game.js
@ -55,7 +55,8 @@ const POWERUPS = ["bomb", "flame", "speed"];
|
||||
const MAX_PLAYERS = 8;
|
||||
const NETWORK_PATH = "/ws";
|
||||
const PLAYER_NAME_STORAGE_KEY = "gptBomberPlayerName";
|
||||
const SNAPSHOT_RATE = 24;
|
||||
const SNAPSHOT_RATE = 18;
|
||||
const MAP_RESYNC_SECONDS = 1.2;
|
||||
|
||||
const canvas = document.getElementById("game");
|
||||
const ctx = canvas.getContext("2d");
|
||||
@ -142,6 +143,8 @@ const network = {
|
||||
lastPingAt: 0,
|
||||
lobbyPhase: "lobby",
|
||||
hasReceivedSnapshot: false,
|
||||
lastFullMapSnapshotAt: 0,
|
||||
lastSentMapState: null,
|
||||
};
|
||||
|
||||
let audio = null;
|
||||
@ -351,6 +354,7 @@ function resetRoundSingle() {
|
||||
state.multiplayerRoundKills = {};
|
||||
state.multiplayerRoundDeathTimes = {};
|
||||
state.multiplayerRoundTime = 0;
|
||||
resetHostSnapshotMapState();
|
||||
closeMenu();
|
||||
}
|
||||
|
||||
@ -384,6 +388,7 @@ function resetRoundMultiplayer(roster) {
|
||||
state.multiplayerRoundKills = {};
|
||||
state.multiplayerRoundDeathTimes = {};
|
||||
state.multiplayerRoundTime = 0;
|
||||
resetHostSnapshotMapState();
|
||||
closeMenu();
|
||||
inputState.bombQueued = false;
|
||||
network.remoteBombPrev.clear();
|
||||
@ -858,6 +863,7 @@ function leaveLobbyInternals() {
|
||||
network.inputStateAtClient = { dir: null, bomb: false, bombCell: null };
|
||||
network.lastInputSentAt = 0;
|
||||
network.hasReceivedSnapshot = false;
|
||||
resetHostSnapshotMapState();
|
||||
}
|
||||
|
||||
function leaveMultiplayerToMainMenu() {
|
||||
@ -878,12 +884,68 @@ function hostStartMatchFromLobby() {
|
||||
sendSocketMessage("lobby_start");
|
||||
}
|
||||
|
||||
function serializeGameState() {
|
||||
function resetHostSnapshotMapState() {
|
||||
network.lastSentMapState = null;
|
||||
network.lastFullMapSnapshotAt = 0;
|
||||
}
|
||||
|
||||
function serializeMapTile(tile) {
|
||||
return { type: tile.type, powerup: tile.powerup || null };
|
||||
}
|
||||
|
||||
function mapTileSignature(tile) {
|
||||
return tile.type + "|" + (tile.powerup || "");
|
||||
}
|
||||
|
||||
function buildMapSignatureState() {
|
||||
return state.map.map((row) => row.map((tile) => mapTileSignature(tile)));
|
||||
}
|
||||
|
||||
function serializeMapPatch() {
|
||||
if (!Array.isArray(network.lastSentMapState) || network.lastSentMapState.length !== state.map.length) {
|
||||
network.lastSentMapState = buildMapSignatureState();
|
||||
return [];
|
||||
}
|
||||
|
||||
const patch = [];
|
||||
for (let y = 0; y < state.map.length; y += 1) {
|
||||
const row = state.map[y];
|
||||
const previousRow = network.lastSentMapState[y];
|
||||
for (let x = 0; x < row.length; x += 1) {
|
||||
const tile = row[x];
|
||||
const signature = mapTileSignature(tile);
|
||||
if (!previousRow || previousRow[x] !== signature) {
|
||||
patch.push({ x, y, type: tile.type, powerup: tile.powerup || null });
|
||||
if (previousRow) {
|
||||
previousRow[x] = signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
function serializeGameState(includeFullMap = false) {
|
||||
let map;
|
||||
let mapPatch;
|
||||
|
||||
if (includeFullMap || !Array.isArray(network.lastSentMapState)) {
|
||||
map = state.map.map((row) => row.map((tile) => serializeMapTile(tile)));
|
||||
network.lastSentMapState = buildMapSignatureState();
|
||||
} else {
|
||||
const patch = serializeMapPatch();
|
||||
if (patch.length > 0) {
|
||||
mapPatch = patch;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: state.status,
|
||||
message: state.message,
|
||||
time: state.time,
|
||||
map: state.map.map((row) => row.map((tile) => ({ type: tile.type, powerup: tile.powerup || null }))),
|
||||
map,
|
||||
mapPatch,
|
||||
players: state.players.map((player) => ({
|
||||
id: player.id,
|
||||
name: player.name,
|
||||
@ -957,20 +1019,33 @@ function playSnapshotSfx(snapshot) {
|
||||
}
|
||||
|
||||
let powerupCollected = false;
|
||||
for (let y = 0; y < state.map.length; y += 1) {
|
||||
for (let x = 0; x < state.map[y].length; x += 1) {
|
||||
const prevTile = state.map[y][x];
|
||||
const nextTile = snapshot.map[y]?.[x];
|
||||
if (!prevTile || !nextTile) {
|
||||
continue;
|
||||
if (Array.isArray(snapshot.map)) {
|
||||
for (let y = 0; y < state.map.length; y += 1) {
|
||||
for (let x = 0; x < state.map[y].length; x += 1) {
|
||||
const prevTile = state.map[y][x];
|
||||
const nextTile = snapshot.map[y]?.[x];
|
||||
if (!prevTile || !nextTile) {
|
||||
continue;
|
||||
}
|
||||
if (prevTile.type === "floor" && prevTile.powerup && nextTile.type === "floor" && !nextTile.powerup) {
|
||||
powerupCollected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prevTile.type === "floor" && prevTile.powerup && nextTile.type === "floor" && !nextTile.powerup) {
|
||||
powerupCollected = true;
|
||||
if (powerupCollected) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (powerupCollected) {
|
||||
break;
|
||||
} else if (Array.isArray(snapshot.mapPatch)) {
|
||||
for (const change of snapshot.mapPatch) {
|
||||
const prevTile = state.map[change.y]?.[change.x];
|
||||
if (!prevTile) {
|
||||
continue;
|
||||
}
|
||||
if (prevTile.type === "floor" && prevTile.powerup && change.type === "floor" && !change.powerup) {
|
||||
powerupCollected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -989,7 +1064,21 @@ function applySnapshot(snapshot) {
|
||||
state.status = snapshot.status;
|
||||
state.message = snapshot.message;
|
||||
state.time = snapshot.time;
|
||||
state.map = snapshot.map.map((row) => row.map((tile) => ({ type: tile.type, powerup: tile.powerup })));
|
||||
|
||||
if (Array.isArray(snapshot.map)) {
|
||||
state.map = snapshot.map.map((row) => row.map((tile) => ({ type: tile.type, powerup: tile.powerup })));
|
||||
} else if (Array.isArray(snapshot.mapPatch) && state.map.length > 0) {
|
||||
for (const change of snapshot.mapPatch) {
|
||||
if (!inBounds(change.x, change.y)) {
|
||||
continue;
|
||||
}
|
||||
const row = state.map[change.y];
|
||||
if (!row || !row[change.x]) {
|
||||
continue;
|
||||
}
|
||||
row[change.x] = { type: change.type, powerup: change.powerup || null };
|
||||
}
|
||||
}
|
||||
state.players = snapshot.players.map((player) => {
|
||||
const key = player.ownerId || `id:${player.id}`;
|
||||
const prev = previousPlayers.get(key);
|
||||
@ -1031,12 +1120,23 @@ function broadcastSnapshot(force = false) {
|
||||
if (!network.isHost || state.mode !== "multiplayer" || state.screen !== "game") {
|
||||
return;
|
||||
}
|
||||
|
||||
const t = now();
|
||||
if (!force && t - network.lastSnapshotSentAt < 1 / SNAPSHOT_RATE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const includeFullMap =
|
||||
force ||
|
||||
!Array.isArray(network.lastSentMapState) ||
|
||||
t - network.lastFullMapSnapshotAt >= MAP_RESYNC_SECONDS;
|
||||
|
||||
if (includeFullMap) {
|
||||
network.lastFullMapSnapshotAt = t;
|
||||
}
|
||||
|
||||
network.lastSnapshotSentAt = t;
|
||||
sendSocketMessage("game_snapshot", { snapshot: serializeGameState(), ts: t });
|
||||
sendSocketMessage("game_snapshot", { snapshot: serializeGameState(includeFullMap), ts: t });
|
||||
}
|
||||
|
||||
function updateLobbyNetwork(dt) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user