From 29ba1c29d7ada13a9e6d475ca880d121a85894ea Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sat, 2 Sep 2023 14:40:46 -0600 Subject: [PATCH] make next update interval a property on network update component instead of inheritable attribute on entities --- engine/components/BoundingBox.ts | 1 + engine/components/NetworkUpdateable.ts | 18 +++++++++++++++++- engine/entities/Entity.ts | 5 +---- engine/entities/Floor.ts | 6 +----- engine/entities/Player.ts | 6 +----- engine/systems/NetworkUpdate.ts | 23 +++++++++++++---------- server/src/server.ts | 16 ++++++++-------- 7 files changed, 42 insertions(+), 33 deletions(-) diff --git a/engine/components/BoundingBox.ts b/engine/components/BoundingBox.ts index 921feb9..f01ae9b 100644 --- a/engine/components/BoundingBox.ts +++ b/engine/components/BoundingBox.ts @@ -16,6 +16,7 @@ export class BoundingBox extends Component { } public isCollidingWith(box: BoundingBox): boolean { + // optimization; when neither rotates just check if they overlap if (this.rotation == 0 && box.rotation == 0) { const thisTopLeft = this.getTopLeft(); const thisBottomRight = this.getBottomRight(); diff --git a/engine/components/NetworkUpdateable.ts b/engine/components/NetworkUpdateable.ts index 014270c..78d7324 100644 --- a/engine/components/NetworkUpdateable.ts +++ b/engine/components/NetworkUpdateable.ts @@ -1,7 +1,23 @@ import { Component, ComponentNames } from '.'; export class NetworkUpdateable extends Component { - constructor() { + static DEFAULT_UPDATE_JITTER_MS = 30; + static DEFAULT_THRESHOLD_TIME_MS = 20; + + public updateThreshold: number; + public jitter: number; + + constructor( + updateThreshold = NetworkUpdateable.DEFAULT_THRESHOLD_TIME_MS, + jitter = NetworkUpdateable.DEFAULT_UPDATE_JITTER_MS + ) { super(ComponentNames.NetworkUpdateable); + + this.updateThreshold = updateThreshold; + this.jitter = jitter; + } + + public getNextUpdateTime() { + return Math.random() * this.jitter + this.updateThreshold; } } diff --git a/engine/entities/Entity.ts b/engine/entities/Entity.ts index 7078f79..a7d561c 100644 --- a/engine/entities/Entity.ts +++ b/engine/entities/Entity.ts @@ -1,8 +1,7 @@ import { EntityNames, Floor, Player } from '.'; import { type Component } from '../components'; -const randomId = () => - (performance.now() + Math.random() * 10_000_000).toString(); +const randomId = () => (Math.random() * 1_000_000_000).toString(); export abstract class Entity { public id: string; @@ -59,6 +58,4 @@ export abstract class Entity { public abstract setFrom(args: Record): void; public abstract serialize(): Record; - - public abstract getNextUpdateInterval(): number; } diff --git a/engine/entities/Floor.ts b/engine/entities/Floor.ts index f56c0d3..fb4988d 100644 --- a/engine/entities/Floor.ts +++ b/engine/entities/Floor.ts @@ -17,7 +17,7 @@ export class Floor extends Entity { this.addComponent( new Sprite( - IMAGES.get((Floor.spriteSpec?.states?.get(width) as SpriteSpec).sheet), + IMAGES.get(Floor.spriteSpec!.states!.get(width)!.sheet!)!, { x: 0, y: 0 }, { width, height: Floor.spriteSpec.height }, Floor.spriteSpec.msPerFrame, @@ -45,8 +45,4 @@ export class Floor extends Entity { ) ); } - - public getNextUpdateInterval() { - return Math.random() * 500; - } } diff --git a/engine/entities/Player.ts b/engine/entities/Player.ts index b42fca4..02dd14e 100644 --- a/engine/entities/Player.ts +++ b/engine/entities/Player.ts @@ -60,7 +60,7 @@ export class Player extends Entity { const [leftSprite, rightSprite] = [Direction.LEFT, Direction.RIGHT].map( (direction) => new Sprite( - IMAGES.get(Player.spriteSpec.states?.get(direction)?.sheet as string), + IMAGES.get(Player.spriteSpec.states!.get(direction)!.sheet!)!, { x: 0, y: 0 }, { width: Player.spriteSpec.width, height: Player.spriteSpec.height }, Player.spriteSpec.msPerFrame, @@ -102,8 +102,4 @@ export class Player extends Entity { new BoundingBox(center, boundingBox.dimension, boundingBox.rotation) ].forEach((component) => this.addComponent(component)); } - - public getNextUpdateInterval() { - return Math.random() * 30 + 50; - } } diff --git a/engine/systems/NetworkUpdate.ts b/engine/systems/NetworkUpdate.ts index f4c8f9c..524ebf4 100644 --- a/engine/systems/NetworkUpdate.ts +++ b/engine/systems/NetworkUpdate.ts @@ -1,6 +1,6 @@ import { System, SystemNames } from '.'; import { Game } from '../Game'; -import { ComponentNames } from '../components'; +import { ComponentNames, NetworkUpdateable } from '../components'; import { type MessageQueueProvider, type MessagePublisher, @@ -55,15 +55,20 @@ export class NetworkUpdate extends System { // 2. send entity updates const updateMessages: EntityUpdateBody[] = []; - // todo: figure out if we can use the controllable component to determine if we should publish an update game.forEachEntityWithComponent( ComponentNames.NetworkUpdateable, (entity) => { + const networkUpdateableComponent = + entity.getComponent( + ComponentNames.NetworkUpdateable + ); + const nextUpdateTime = networkUpdateableComponent.getNextUpdateTime(); + const newHash = stringify(entity.serialize()); let updateInfo: EntityUpdateInfo = this.entityUpdateInfo.get( entity.id ) ?? { - timer: this.getNextUpdateTimeMs(), + timer: nextUpdateTime, hash: newHash }; @@ -71,13 +76,11 @@ export class NetworkUpdate extends System { updateInfo.timer -= dt; this.entityUpdateInfo.set(entity.id, updateInfo); if (updateInfo.timer > 0) return; - updateInfo.timer = entity.getNextUpdateInterval(); + updateInfo.timer = nextUpdateTime; this.entityUpdateInfo.set(entity.id, updateInfo); - // maybe update if hash is not consitent - if (updateInfo.hash == newHash) { - return; - } + // maybe update, if hash is not consistent + if (updateInfo.hash == newHash) return; updateInfo.hash = newHash; this.entityUpdateInfo.set(entity.id, updateInfo); @@ -102,7 +105,7 @@ export class NetworkUpdate extends System { } } - private getNextUpdateInterval(): number { - return Math.random() * 30; + private getNextUpdateInterval() { + return Math.random() * 30 + 20; } } diff --git a/server/src/server.ts b/server/src/server.ts index 575e916..3beebd5 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -42,7 +42,9 @@ export class GameServer { fetch: (req, srv) => this.fetchHandler(req, srv), websocket: { open: (ws) => this.openWebsocket(ws), - message: (ws, msg) => this.websocketMessage(ws, msg), + message: (ws, msg) => { + if (typeof msg === 'string') this.websocketMessage(ws, msg); + }, close: (ws) => this.closeWebsocket(ws) } }); @@ -54,21 +56,19 @@ export class GameServer { private websocketMessage( websocket: ServerWebSocket, - message: string | Uint8Array + message: string ) { - if (typeof message == 'string') { - const receivedMessage = parse(message); - receivedMessage.sessionData = websocket.data; + const receivedMessage = parse(message); + receivedMessage.sessionData = websocket.data; - this.messageReceiver.addMessage(receivedMessage); - } + this.messageReceiver.addMessage(receivedMessage); } private closeWebsocket(websocket: ServerWebSocket) { const { sessionId } = websocket.data; const sessionEntities = - this.sessionManager.getSession(sessionId)!.controllableEntities; + this.sessionManager.getSession(sessionId)?.controllableEntities; this.sessionManager.removeSession(sessionId); if (!sessionEntities) return;