diff --git a/client/src/JumpStorm.ts b/client/src/JumpStorm.ts index 1beeb0d..ca95ed8 100644 --- a/client/src/JumpStorm.ts +++ b/client/src/JumpStorm.ts @@ -1,5 +1,4 @@ import { Game } from '@engine/Game'; -import { Entity } from '@engine/entities'; import { Grid } from '@engine/structures'; import { WallBounds, @@ -8,128 +7,13 @@ import { Physics, Input, Collision, - NetworkUpdate, - SystemNames + NetworkUpdate } from '@engine/systems'; import { - type MessageQueueProvider, - type MessagePublisher, - type MessageProcessor, - type Message, - type EntityAddBody, - MessageType, - type EntityUpdateBody -} from '@engine/network'; -import { stringify, parse } from '@engine/utils'; -import { ComponentNames, Control, NetworkUpdateable } from '@engine/components'; - -class ClientMessageProcessor implements MessageProcessor { - private game: Game; - - constructor(game: Game) { - this.game = game; - } - - public process(message: Message) { - switch (message.type) { - case MessageType.NEW_ENTITIES: - const entityAdditions = message.body as unknown as EntityAddBody[]; - entityAdditions.forEach((addBody) => { - const entity = Entity.from( - addBody.entityName, - addBody.id, - addBody.args - ); - if (entity.hasComponent(ComponentNames.Control)) { - const clientId = this.game.getSystem( - SystemNames.Input - ).clientId; - const control = entity.getComponent( - ComponentNames.Control - ); - - if (control.controllableBy === clientId) { - entity.addComponent(new NetworkUpdateable()); - } - } - - this.game.addEntity(entity); - }); - break; - case MessageType.REMOVE_ENTITIES: - const ids = message.body as unknown as string[]; - ids.forEach((id) => this.game.removeEntity(id)); - break; - case MessageType.UPDATE_ENTITIES: - const entityUpdates = message.body as unknown as EntityUpdateBody[]; - entityUpdates.forEach(({ id, args }) => { - const entity = this.game.getEntity(id); - if (!entity) return; - if (entity && entity.hasComponent(ComponentNames.Control)) { - const clientId = this.game.getSystem( - SystemNames.Input - ).clientId; - const control = entity.getComponent( - ComponentNames.Control - ); - - // don't listen to entities which we control - if (control.controllableBy == clientId) return; - } - entity.setFrom(args); - }); - break; - default: - break; - } - } -} - -class ClientSocketMessageQueueProvider implements MessageQueueProvider { - private socket: WebSocket; - private messages: Message[]; - - constructor(socket: WebSocket) { - this.socket = socket; - this.messages = []; - - this.socket.addEventListener('message', (e) => { - const messages = parse(e.data); - this.messages = this.messages.concat(messages); - }); - } - - public getNewMessages() { - return this.messages; - } - - public clearMessages() { - this.messages = []; - } -} - -class ClientSocketMessagePublisher implements MessagePublisher { - private socket: WebSocket; - private messages: Message[]; - - constructor(socket: WebSocket) { - this.socket = socket; - this.messages = []; - } - - public addMessage(message: Message) { - this.messages.push(message); - } - - public publish() { - if (this.socket.readyState == WebSocket.OPEN) { - this.messages.forEach((message: Message) => - this.socket.send(stringify(message)) - ); - this.messages = []; - } - } -} + ClientMessageProcessor, + ClientSocketMessagePublisher, + ClientSocketMessageQueueProvider +} from './network'; export class JumpStorm { private game: Game; @@ -148,6 +32,7 @@ export class JumpStorm { this.clientId = await this.getAssignedCookie( `${httpMethod}://${host}/assign` ); + const socket = new WebSocket(`${wsMethod}://${host}/game`); const clientSocketMessageQueueProvider = new ClientSocketMessageQueueProvider(socket); @@ -159,8 +44,6 @@ export class JumpStorm { const inputSystem = new Input(this.clientId, clientSocketMessagePublisher); this.addWindowEventListenersToInputSystem(inputSystem); - const grid = new Grid(); - [ new Physics(), new NetworkUpdate( @@ -170,7 +53,7 @@ export class JumpStorm { ), inputSystem, new FacingDirection(), - new Collision(grid), + new Collision(new Grid()), new WallBounds(), new Render(ctx) ].forEach((system) => this.game.addSystem(system)); diff --git a/client/src/network/MessageProcessor.ts b/client/src/network/MessageProcessor.ts new file mode 100644 index 0000000..539937a --- /dev/null +++ b/client/src/network/MessageProcessor.ts @@ -0,0 +1,74 @@ +import type { Game } from '@engine/Game'; +import { ComponentNames, Control, NetworkUpdateable } from '@engine/components'; +import { Entity } from '@engine/entities'; +import { + MessageType, + type Message, + type EntityAddBody, + type EntityUpdateBody, + type MessageProcessor +} from '@engine/network'; +import { Input, SystemNames } from '@engine/systems'; + +export class ClientMessageProcessor implements MessageProcessor { + private game: Game; + + constructor(game: Game) { + this.game = game; + } + + public process(message: Message) { + switch (message.type) { + case MessageType.NEW_ENTITIES: + const entityAdditions = message.body as unknown as EntityAddBody[]; + entityAdditions.forEach((addBody) => { + const entity = Entity.from( + addBody.entityName, + addBody.id, + addBody.args + ); + if (entity.hasComponent(ComponentNames.Control)) { + const clientId = this.game.getSystem( + SystemNames.Input + ).clientId; + const control = entity.getComponent( + ComponentNames.Control + ); + + if (control.controllableBy === clientId) { + entity.addComponent(new NetworkUpdateable()); + } + } + + this.game.addEntity(entity); + }); + break; + case MessageType.REMOVE_ENTITIES: + const ids = message.body as unknown as string[]; + ids.forEach((id) => this.game.removeEntity(id)); + break; + case MessageType.UPDATE_ENTITIES: + const entityUpdates = message.body as unknown as EntityUpdateBody[]; + entityUpdates.forEach(({ id, args }) => { + const entity = this.game.getEntity(id); + if (!entity) return; + + if (entity && entity.hasComponent(ComponentNames.Control)) { + const clientId = this.game.getSystem( + SystemNames.Input + ).clientId; + const control = entity.getComponent( + ComponentNames.Control + ); + + // don't listen to entities which we control + if (control.controllableBy === clientId) return; + } + entity.setFrom(args); + }); + break; + default: + break; + } + } +} diff --git a/client/src/network/MessagePublisher.ts b/client/src/network/MessagePublisher.ts new file mode 100644 index 0000000..de105c5 --- /dev/null +++ b/client/src/network/MessagePublisher.ts @@ -0,0 +1,25 @@ +import type { Message, MessagePublisher } from '@engine/network'; +import { stringify } from '@engine/utils'; + +export class ClientSocketMessagePublisher implements MessagePublisher { + private socket: WebSocket; + private messages: Message[]; + + constructor(socket: WebSocket) { + this.socket = socket; + this.messages = []; + } + + public addMessage(message: Message) { + this.messages.push(message); + } + + public publish() { + if (this.socket.readyState == WebSocket.OPEN) { + this.messages.forEach((message: Message) => + this.socket.send(stringify(message)) + ); + this.messages = []; + } + } +} diff --git a/client/src/network/MessageQueueReceiver.ts b/client/src/network/MessageQueueReceiver.ts new file mode 100644 index 0000000..46ca5cc --- /dev/null +++ b/client/src/network/MessageQueueReceiver.ts @@ -0,0 +1,25 @@ +import type { Message, MessageQueueProvider } from '@engine/network'; +import { parse } from '@engine/utils'; + +export class ClientSocketMessageQueueProvider implements MessageQueueProvider { + private socket: WebSocket; + private messages: Message[]; + + constructor(socket: WebSocket) { + this.socket = socket; + this.messages = []; + + this.socket.addEventListener('message', (e) => { + const messages = parse(e.data); + this.messages = this.messages.concat(messages); + }); + } + + public getNewMessages() { + return this.messages; + } + + public clearMessages() { + this.messages = []; + } +} diff --git a/client/src/network/index.ts b/client/src/network/index.ts new file mode 100644 index 0000000..1f4d1a4 --- /dev/null +++ b/client/src/network/index.ts @@ -0,0 +1,3 @@ +export * from './MessageProcessor'; +export * from './MessagePublisher'; +export * from './MessageQueueReceiver'; diff --git a/engine/entities/Player.ts b/engine/entities/Player.ts index abe3bb5..2786b8a 100644 --- a/engine/entities/Player.ts +++ b/engine/entities/Player.ts @@ -92,7 +92,7 @@ export class Player extends Entity { const distance = Math.sqrt( Math.pow(center.y - myCenter.y, 2) + Math.pow(center.x - myCenter.x, 2) ); - const clientServerPredictionCenterThreshold = 20; + const clientServerPredictionCenterThreshold = 15; if (distance < clientServerPredictionCenterThreshold) center = myCenter; [ diff --git a/server/src/server.ts b/server/src/server.ts index f28c80f..251fd89 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -38,7 +38,7 @@ export class GameServer { public serve() { if (!this.server) this.server = Bun.serve({ - host: Constants.HOST, + hostname: Constants.HOST, port: Constants.SERVER_PORT, fetch: (req, srv) => this.fetchHandler(req, srv), websocket: {