diff options
author | Elizabeth Hunt <elizabeth.hunt@simponic.xyz> | 2023-08-26 17:55:27 -0600 |
---|---|---|
committer | Elizabeth Hunt <elizabeth.hunt@simponic.xyz> | 2023-08-26 17:55:27 -0600 |
commit | 6ce6946a4401d2ee6fa5cb747fab7d4c658a63c8 (patch) | |
tree | e60767dc5295edf379cf421e20171dc418e548b7 /server | |
parent | 594921352c8d82fe5f1a6201a4d5f9fbd9b719fc (diff) | |
download | jumpstorm-6ce6946a4401d2ee6fa5cb747fab7d4c658a63c8.tar.gz jumpstorm-6ce6946a4401d2ee6fa5cb747fab7d4c658a63c8.zip |
add entity updates over network!
Diffstat (limited to 'server')
-rw-r--r-- | server/src/main.ts | 41 | ||||
-rw-r--r-- | server/src/network/MessageProcessor.ts | 36 | ||||
-rw-r--r-- | server/src/network/SessionInputSystem.ts | 32 | ||||
-rw-r--r-- | server/src/network/SessionManager.ts | 33 | ||||
-rw-r--r-- | server/src/network/index.ts | 13 | ||||
-rw-r--r-- | server/src/server.ts | 76 |
6 files changed, 194 insertions, 37 deletions
diff --git a/server/src/main.ts b/server/src/main.ts index 965e0d7..0e47491 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -2,28 +2,55 @@ import { Grid } from '@engine/structures'; import { ServerMessageProcessor, ServerSocketMessagePublisher, - ServerSocketMessageReceiver + ServerSocketMessageReceiver, + MemorySessionManager, + SessionInputSystem } from './network'; import { Collision, NetworkUpdate, Physics, WallBounds } from '@engine/systems'; import { Game } from '@engine/Game'; import { Constants } from './constants'; import { GameServer } from './server'; +import { Floor } from '@engine/entities'; +import { BoundingBox } from '@engine/components'; +import { Miscellaneous } from '@engine/config'; + +const game = new Game(); + +const sessionManager = new MemorySessionManager(); const messageReceiver = new ServerSocketMessageReceiver(); const messagePublisher = new ServerSocketMessagePublisher(); -const messageProcessor = new ServerMessageProcessor(); +const messageProcessor = new ServerMessageProcessor(game, sessionManager); -const game = new Game(); - -const server = new GameServer(game, messageReceiver, messagePublisher); +const server = new GameServer( + game, + messageReceiver, + messagePublisher, + sessionManager +); [ + new SessionInputSystem(sessionManager), + new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor), new Physics(), new Collision(new Grid()), - new WallBounds(), - new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor) + new WallBounds() ].forEach((system) => game.addSystem(system)); +const floor = new Floor(160); +const floorHeight = 200; + +floor.addComponent( + new BoundingBox( + { + x: Miscellaneous.WIDTH / 2, + y: Miscellaneous.HEIGHT + floorHeight / 2 + }, + { width: Miscellaneous.WIDTH, height: floorHeight } + ) +); +game.addEntity(floor); + game.start(); setInterval(() => { game.doGameLoop(performance.now()); diff --git a/server/src/network/MessageProcessor.ts b/server/src/network/MessageProcessor.ts index de42459..2d9f11f 100644 --- a/server/src/network/MessageProcessor.ts +++ b/server/src/network/MessageProcessor.ts @@ -1,8 +1,36 @@ -import { MessageProcessor } from '@engine/network'; -import { ServerMessage } from '.'; +import { + EntityUpdateBody, + MessageProcessor, + MessageType +} from '@engine/network'; +import { ServerMessage, SessionManager } from '.'; +import { Game } from '@engine/Game'; export class ServerMessageProcessor implements MessageProcessor { - constructor() {} + private game: Game; + private sessionManager: SessionManager; - public process(_message: ServerMessage) {} + constructor(game: Game, sessionManager: SessionManager) { + this.game = game; + this.sessionManager = sessionManager; + } + + public process(message: ServerMessage) { + switch (message.type) { + case MessageType.NEW_INPUT: { + const { sessionId } = message.sessionData; + const session = this.sessionManager.getSession(sessionId); + session?.inputSystem.keyPressed(message.body as string); + break; + } + case MessageType.REMOVE_INPUT: { + const { sessionId } = message.sessionData; + const session = this.sessionManager.getSession(sessionId); + session?.inputSystem.keyReleased(message.body as string); + break; + } + default: + break; + } + } } diff --git a/server/src/network/SessionInputSystem.ts b/server/src/network/SessionInputSystem.ts new file mode 100644 index 0000000..44fba54 --- /dev/null +++ b/server/src/network/SessionInputSystem.ts @@ -0,0 +1,32 @@ +import { Game } from '@engine/Game'; +import { SessionManager } from '.'; +import { System } from '@engine/systems'; +import { BoundingBox, ComponentNames, Control } from '@engine/components'; + +export class SessionInputSystem extends System { + private sessionManager: SessionManager; + + constructor(sessionManager: SessionManager) { + super('SessionInputSystem'); + + this.sessionManager = sessionManager; + } + + public update(_dt: number, game: Game) { + this.sessionManager.getSessions().forEach((sessionId) => { + const session = this.sessionManager.getSession(sessionId); + + if (!session) return; + + const { inputSystem } = session; + session.controllableEntities.forEach((entityId) => { + const entity = game.getEntity(entityId); + if (!entity) return; + + if (entity.hasComponent(ComponentNames.Control)) { + inputSystem.handleInput(entity); + } + }); + }); + } +} diff --git a/server/src/network/SessionManager.ts b/server/src/network/SessionManager.ts new file mode 100644 index 0000000..dbd4364 --- /dev/null +++ b/server/src/network/SessionManager.ts @@ -0,0 +1,33 @@ +import { Session, SessionManager } from '.'; + +export class MemorySessionManager implements SessionManager { + private sessions: Map<string, Session>; + + constructor() { + this.sessions = new Map(); + } + + public getSessions() { + return Array.from(this.sessions.keys()); + } + + public uniqueSessionId() { + return crypto.randomUUID(); + } + + public getSession(id: string) { + return this.sessions.get(id); + } + + public putSession(id: string, session: Session) { + return this.sessions.set(id, session); + } + + public numSessions() { + return this.sessions.size; + } + + public removeSession(id: string) { + this.sessions.delete(id); + } +} diff --git a/server/src/network/index.ts b/server/src/network/index.ts index 8ffa689..3cbf0ac 100644 --- a/server/src/network/index.ts +++ b/server/src/network/index.ts @@ -1,16 +1,29 @@ import { Message } from '@engine/network'; +import { Input } from '@engine/systems'; export * from './MessageProcessor'; export * from './MessagePublisher'; export * from './MessageReceiver'; +export * from './SessionManager'; +export * from './SessionInputSystem'; export type SessionData = { sessionId: string }; export type Session = { sessionId: string; controllableEntities: Set<string>; + inputSystem: Input; }; export interface ServerMessage extends Message { sessionData: SessionData; } + +export interface SessionManager { + uniqueSessionId(): string; + getSession(id: string): Session | undefined; + getSessions(): string[]; + putSession(id: string, session: Session): void; + removeSession(id: string): void; + numSessions(): number; +} diff --git a/server/src/server.ts b/server/src/server.ts index 303d2b5..575e916 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,35 +1,38 @@ import { Game } from '@engine/Game'; -import { EntityNames, Player } from '@engine/entities'; -import { MessageType } from '@engine/network'; +import { Player } from '@engine/entities'; +import { Message, MessageType } from '@engine/network'; import { Constants } from './constants'; import { ServerSocketMessageReceiver, ServerSocketMessagePublisher, SessionData, ServerMessage, - Session + Session, + SessionManager } from './network'; import { parse } from '@engine/utils'; import { Server, ServerWebSocket } from 'bun'; +import { Input } from '@engine/systems'; +import { Control, NetworkUpdateable } from '@engine/components'; +import { stringify } from '@engine/utils'; export class GameServer { - private sessions: Map<string, Session>; - private server?: Server; private game: Game; private messageReceiver: ServerSocketMessageReceiver; private messagePublisher: ServerSocketMessagePublisher; + private sessionManager: SessionManager; constructor( game: Game, messageReceiver: ServerSocketMessageReceiver, - messagePublisher: ServerSocketMessagePublisher + messagePublisher: ServerSocketMessagePublisher, + sessionManager: SessionManager ) { - this.sessions = new Map(); - this.game = game; this.messageReceiver = messageReceiver; this.messagePublisher = messagePublisher; + this.sessionManager = sessionManager; } public serve() { @@ -64,10 +67,12 @@ export class GameServer { private closeWebsocket(websocket: ServerWebSocket<SessionData>) { const { sessionId } = websocket.data; - const sessionEntities = this.sessions.get(sessionId)!.controllableEntities; - this.sessions.delete(sessionId); + const sessionEntities = + this.sessionManager.getSession(sessionId)!.controllableEntities; + this.sessionManager.removeSession(sessionId); if (!sessionEntities) return; + sessionEntities.forEach((id) => this.game.removeEntity(id)); this.messagePublisher.addMessage({ type: MessageType.REMOVE_ENTITIES, @@ -79,28 +84,51 @@ export class GameServer { websocket.subscribe(Constants.GAME_TOPIC); const { sessionId } = websocket.data; - if (this.sessions.has(sessionId)) { + if (this.sessionManager.getSession(sessionId)) { return; } - this.sessions.set(sessionId, { + const newSession: Session = { sessionId, - controllableEntities: new Set() - }); + controllableEntities: new Set(), + inputSystem: new Input(sessionId) + }; - const player = new Player(sessionId); + const player = new Player(); + player.addComponent(new Control(sessionId)); + player.addComponent(new NetworkUpdateable()); this.game.addEntity(player); - this.messagePublisher.addMessage({ + + newSession.controllableEntities.add(player.id); + this.sessionManager.putSession(sessionId, newSession); + + const addCurrentEntities: Message[] = [ + { + type: MessageType.NEW_ENTITIES, + body: Array.from(this.game.entities.values()) + .filter((entity) => entity.id != player.id) + .map((entity) => { + return { + id: entity.id, + entityName: entity.name, + args: entity.serialize() + }; + }) + } + ]; + websocket.sendText(stringify(addCurrentEntities)); + + const addNewPlayer: Message = { type: MessageType.NEW_ENTITIES, body: [ { - entityName: EntityNames.Player, - args: { playerId: sessionId, id: player.id } + id: player.id, + entityName: player.name, + args: player.serialize() } ] - }); - - this.sessions.get(sessionId)!.controllableEntities.add(player.id); + }; + this.messagePublisher.addMessage(addNewPlayer); } private fetchHandler(req: Request, server: Server): Response { @@ -110,7 +138,7 @@ export class GameServer { headers.set('Access-Control-Allow-Origin', '*'); if (url.pathname == '/assign') { - if (this.sessions.size > Constants.MAX_PLAYERS) + if (this.sessionManager.numSessions() > Constants.MAX_PLAYERS) return new Response('too many players', { headers, status: 400 }); const sessionId = crypto.randomUUID(); @@ -127,10 +155,6 @@ export class GameServer { const sessionId = cookie.split(';').at(0)!.split('SessionId=').at(1); if (url.pathname == '/game') { - headers.set( - 'Set-Cookie', - `SessionId=${sessionId}; HttpOnly; SameSite=Strict;` - ); server.upgrade(req, { headers, data: { |