summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2023-08-25 18:10:09 -0600
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2023-08-25 18:10:09 -0600
commit2fbe0f0595d06800c1a648a4168b57471d395ee4 (patch)
tree830342a963be8e560cf07cfca8b8346bdcb51d90
parent773ce84f4bf559337e132edd7fcce02a0a2598fd (diff)
downloadjumpstorm-2fbe0f0595d06800c1a648a4168b57471d395ee4.tar.gz
jumpstorm-2fbe0f0595d06800c1a648a4168b57471d395ee4.zip
refactor server structure
-rw-r--r--server/src/constants.ts6
-rw-r--r--server/src/main.ts32
-rw-r--r--server/src/network/MessageProcessor.ts8
-rw-r--r--server/src/network/MessagePublisher.ts31
-rw-r--r--server/src/network/MessageReceiver.ts22
-rw-r--r--server/src/network/index.ts16
-rw-r--r--server/src/server.ts228
7 files changed, 205 insertions, 138 deletions
diff --git a/server/src/constants.ts b/server/src/constants.ts
new file mode 100644
index 0000000..a2b3d12
--- /dev/null
+++ b/server/src/constants.ts
@@ -0,0 +1,6 @@
+export namespace Constants {
+ export const SERVER_PORT = 8080;
+ export const SERVER_TICK_RATE = (1 / 60) * 1000;
+ export const GAME_TOPIC = 'game';
+ export const MAX_PLAYERS = 8;
+}
diff --git a/server/src/main.ts b/server/src/main.ts
new file mode 100644
index 0000000..965e0d7
--- /dev/null
+++ b/server/src/main.ts
@@ -0,0 +1,32 @@
+import { Grid } from '@engine/structures';
+import {
+ ServerMessageProcessor,
+ ServerSocketMessagePublisher,
+ ServerSocketMessageReceiver
+} from './network';
+import { Collision, NetworkUpdate, Physics, WallBounds } from '@engine/systems';
+import { Game } from '@engine/Game';
+import { Constants } from './constants';
+import { GameServer } from './server';
+
+const messageReceiver = new ServerSocketMessageReceiver();
+const messagePublisher = new ServerSocketMessagePublisher();
+const messageProcessor = new ServerMessageProcessor();
+
+const game = new Game();
+
+const server = new GameServer(game, messageReceiver, messagePublisher);
+
+[
+ new Physics(),
+ new Collision(new Grid()),
+ new WallBounds(),
+ new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor)
+].forEach((system) => game.addSystem(system));
+
+game.start();
+setInterval(() => {
+ game.doGameLoop(performance.now());
+}, Constants.SERVER_TICK_RATE);
+
+server.serve();
diff --git a/server/src/network/MessageProcessor.ts b/server/src/network/MessageProcessor.ts
new file mode 100644
index 0000000..de42459
--- /dev/null
+++ b/server/src/network/MessageProcessor.ts
@@ -0,0 +1,8 @@
+import { MessageProcessor } from '@engine/network';
+import { ServerMessage } from '.';
+
+export class ServerMessageProcessor implements MessageProcessor {
+ constructor() {}
+
+ public process(_message: ServerMessage) {}
+}
diff --git a/server/src/network/MessagePublisher.ts b/server/src/network/MessagePublisher.ts
new file mode 100644
index 0000000..9c6011f
--- /dev/null
+++ b/server/src/network/MessagePublisher.ts
@@ -0,0 +1,31 @@
+import { Message, MessagePublisher } from '@engine/network';
+import { Server } from 'bun';
+import { Constants } from '../constants';
+import { stringify } from '@engine/utils';
+
+export class ServerSocketMessagePublisher implements MessagePublisher {
+ private server?: Server;
+ private messages: Message[];
+
+ constructor(server?: Server) {
+ this.messages = [];
+
+ if (server) this.setServer(server);
+ }
+
+ public setServer(server: Server) {
+ this.server = server;
+ }
+
+ public addMessage(message: Message) {
+ this.messages.push(message);
+ }
+
+ public publish() {
+ if (this.messages.length) {
+ this.server?.publish(Constants.GAME_TOPIC, stringify(this.messages));
+
+ this.messages = [];
+ }
+ }
+}
diff --git a/server/src/network/MessageReceiver.ts b/server/src/network/MessageReceiver.ts
new file mode 100644
index 0000000..fcac0a4
--- /dev/null
+++ b/server/src/network/MessageReceiver.ts
@@ -0,0 +1,22 @@
+import { MessageQueueProvider } from '@engine/network';
+import type { ServerMessage } from '.';
+
+export class ServerSocketMessageReceiver implements MessageQueueProvider {
+ private messages: ServerMessage[];
+
+ constructor() {
+ this.messages = [];
+ }
+
+ public addMessage(message: ServerMessage) {
+ this.messages.push(message);
+ }
+
+ public getNewMessages() {
+ return this.messages;
+ }
+
+ public clearMessages() {
+ this.messages = [];
+ }
+}
diff --git a/server/src/network/index.ts b/server/src/network/index.ts
new file mode 100644
index 0000000..8ffa689
--- /dev/null
+++ b/server/src/network/index.ts
@@ -0,0 +1,16 @@
+import { Message } from '@engine/network';
+
+export * from './MessageProcessor';
+export * from './MessagePublisher';
+export * from './MessageReceiver';
+
+export type SessionData = { sessionId: string };
+
+export type Session = {
+ sessionId: string;
+ controllableEntities: Set<string>;
+};
+
+export interface ServerMessage extends Message {
+ sessionData: SessionData;
+}
diff --git a/server/src/server.ts b/server/src/server.ts
index c77bfef..6acbe74 100644
--- a/server/src/server.ts
+++ b/server/src/server.ts
@@ -1,110 +1,124 @@
import { Game } from '@engine/Game';
import { EntityNames, Player } from '@engine/entities';
-import { WallBounds, Physics, Collision, NetworkUpdate } from '@engine/systems';
+import { MessageType } from '@engine/network';
+import { Constants } from './constants';
import {
- type MessageQueueProvider,
- type MessagePublisher,
- MessageType,
- type MessageProcessor,
- type Message
-} from '@engine/network';
-import { stringify, parse } from '@engine/utils';
-import { Grid } from '@engine/structures';
-import { Miscellaneous } from '@engine/config';
-import { Server } from 'bun';
-
-const SERVER_PORT = 8080;
-const SERVER_TICK_RATE = (1 / 60) * 1000;
-const GAME_TOPIC = 'game';
-const MAX_PLAYERS = 8;
-
-type SessionData = { sessionId: string };
-
-interface ServerMessage extends Message {
- sessionData: SessionData;
-}
-
-class ServerSocketMessageReceiver implements MessageQueueProvider {
- private messages: ServerMessage[];
+ ServerSocketMessageReceiver,
+ ServerSocketMessagePublisher,
+ SessionData,
+ ServerMessage,
+ Session
+} from './network';
+import { parse } from '@engine/utils';
+import { Server, ServerWebSocket } from 'bun';
+
+export class GameServer {
+ private sessions: Map<string, Session>;
- constructor() {
- this.messages = [];
+ private server?: Server;
+ private game: Game;
+ private messageReceiver: ServerSocketMessageReceiver;
+ private messagePublisher: ServerSocketMessagePublisher;
+
+ constructor(
+ game: Game,
+ messageReceiver: ServerSocketMessageReceiver,
+ messagePublisher: ServerSocketMessagePublisher
+ ) {
+ this.sessions = new Map();
+
+ this.game = game;
+ this.messageReceiver = messageReceiver;
+ this.messagePublisher = messagePublisher;
}
- public addMessage(message: ServerMessage) {
- this.messages.push(message);
- }
+ public serve() {
+ if (!this.server)
+ this.server = Bun.serve<SessionData>({
+ port: Constants.SERVER_PORT,
+ fetch: (req, srv) => this.fetchHandler(req, srv),
+ websocket: {
+ open: (ws) => this.openWebsocket(ws),
+ message: (ws, msg) => this.websocketMessage(ws, msg),
+ close: (ws) => this.closeWebsocket(ws)
+ }
+ });
- public getNewMessages() {
- return this.messages;
- }
+ this.messagePublisher.setServer(this.server);
- public clearMessages() {
- this.messages = [];
+ console.log(`Listening on ${this.server.hostname}:${this.server.port}`);
}
-}
-
-class ServerMessageProcessor implements MessageProcessor {
- constructor() {}
- public process(_message: ServerMessage) {}
-}
-
-class ServerSocketMessagePublisher implements MessagePublisher {
- private server?: Server;
- private messages: Message[];
+ private websocketMessage(
+ websocket: ServerWebSocket<SessionData>,
+ message: string | Uint8Array
+ ) {
+ if (typeof message == 'string') {
+ const receivedMessage = parse<ServerMessage>(message);
+ receivedMessage.sessionData = websocket.data;
- constructor(server?: Server) {
- if (server) {
- this.server = server;
+ this.messageReceiver.addMessage(receivedMessage);
}
-
- this.messages = [];
}
- public setServer(server: Server) {
- this.server = server;
- }
+ private closeWebsocket(websocket: ServerWebSocket<SessionData>) {
+ const { sessionId } = websocket.data;
- public addMessage(message: Message) {
- this.messages.push(message);
- }
+ const sessionEntities = this.sessions.get(sessionId)!.controllableEntities;
- public publish() {
- if (this.messages.length) {
- this.server?.publish(GAME_TOPIC, stringify(this.messages));
+ this.sessions.delete(sessionId);
- this.messages = [];
- }
+ if (!sessionEntities) return;
+ this.messagePublisher.addMessage({
+ type: MessageType.REMOVE_ENTITIES,
+ body: Array.from(sessionEntities)
+ });
}
-}
-const game = new Game();
+ private openWebsocket(websocket: ServerWebSocket<SessionData>) {
+ websocket.subscribe(Constants.GAME_TOPIC);
-const messageReceiver = new ServerSocketMessageReceiver();
-const messagePublisher = new ServerSocketMessagePublisher();
-const messageProcessor = new ServerMessageProcessor();
-const sessionControllableEntities: Map<string, Set<string>> = new Map();
+ const { sessionId } = websocket.data;
+ if (this.sessions.has(sessionId)) {
+ return;
+ }
-const sessions = new Set<string>();
+ this.sessions.set(sessionId, {
+ sessionId,
+ controllableEntities: new Set()
+ });
+
+ const player = new Player(sessionId);
+ this.game.addEntity(player);
+ this.messagePublisher.addMessage({
+ type: MessageType.NEW_ENTITIES,
+ body: [
+ {
+ entityName: EntityNames.Player,
+ args: { playerId: sessionId, id: player.id }
+ }
+ ]
+ });
+
+ this.sessions.get(sessionId)!.controllableEntities.add(player.id);
+ }
-const server = Bun.serve<SessionData>({
- port: SERVER_PORT,
- fetch: async (req, server): Promise<Response> => {
+ private fetchHandler(
+ req: Request,
+ server: Server
+ ): Promise<Response> | Response {
const url = new URL(req.url);
const headers = new Headers();
headers.set('Access-Control-Allow-Origin', '*');
if (url.pathname == '/assign') {
- if (sessions.size > MAX_PLAYERS)
+ if (this.sessions.size > Constants.MAX_PLAYERS)
return new Response('too many players', { headers, status: 400 });
const sessionId = crypto.randomUUID();
headers.set('Set-Cookie', `SessionId=${sessionId};`);
- sessions.add(sessionId);
-
return new Response(sessionId, { headers });
}
@@ -127,7 +141,7 @@ const server = Bun.serve<SessionData>({
}
});
- return new Response('upgraded', { headers });
+ return new Response('upgraded to ws', { headers });
}
if (url.pathname == '/me') {
@@ -135,67 +149,5 @@ const server = Bun.serve<SessionData>({
}
return new Response('Not found', { headers, status: 404 });
- },
- websocket: {
- open(ws) {
- const { sessionId } = ws.data;
-
- if (sessionControllableEntities.has(sessionId)) {
- return;
- }
-
- const player = new Player(sessionId);
- game.addEntity(player);
- sessionControllableEntities.set(sessionId, new Set([player.id]));
-
- messagePublisher.addMessage({
- type: MessageType.NEW_ENTITIES,
- body: [
- {
- entityName: EntityNames.Player,
- args: { playerId: sessionId, id: player.id }
- }
- ]
- });
-
- ws.subscribe(GAME_TOPIC);
- },
- message(ws, message) {
- if (typeof message == 'string') {
- const receivedMessage = parse<ServerMessage>(message);
- receivedMessage.sessionData = ws.data;
-
- messageReceiver.addMessage(receivedMessage);
- }
- },
- close(ws) {
- const { sessionId } = ws.data;
-
- sessions.delete(sessionId);
-
- const sessionEntities = sessionControllableEntities.get(sessionId);
- if (!sessionEntities) return;
-
- messagePublisher.addMessage({
- type: MessageType.REMOVE_ENTITIES,
- body: Array.from(sessionEntities)
- });
- }
}
-});
-
-messagePublisher.setServer(server);
-
-[
- new Physics(),
- new Collision(new Grid()),
- new WallBounds(),
- new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor)
-].forEach((system) => game.addSystem(system));
-
-game.start();
-setInterval(() => {
- game.doGameLoop(performance.now());
-}, SERVER_TICK_RATE);
-
-console.log(`Listening on ${server.hostname}:${server.port}`);
+}