decrease CSP threshold, move client networking to its own folder

This commit is contained in:
Elizabeth Hunt 2023-09-05 21:44:37 -06:00
parent ba989bd7f8
commit d19da30f6d
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
7 changed files with 136 additions and 126 deletions

View File

@ -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<Input>(
SystemNames.Input
).clientId;
const control = entity.getComponent<Control>(
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<Input>(
SystemNames.Input
).clientId;
const control = entity.getComponent<Control>(
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<Message[]>(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));

View File

@ -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<Input>(
SystemNames.Input
).clientId;
const control = entity.getComponent<Control>(
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<Input>(
SystemNames.Input
).clientId;
const control = entity.getComponent<Control>(
ComponentNames.Control
);
// don't listen to entities which we control
if (control.controllableBy === clientId) return;
}
entity.setFrom(args);
});
break;
default:
break;
}
}
}

View File

@ -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 = [];
}
}
}

View File

@ -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<Message[]>(e.data);
this.messages = this.messages.concat(messages);
});
}
public getNewMessages() {
return this.messages;
}
public clearMessages() {
this.messages = [];
}
}

View File

@ -0,0 +1,3 @@
export * from './MessageProcessor';
export * from './MessagePublisher';
export * from './MessageQueueReceiver';

View File

@ -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;
[

View File

@ -38,7 +38,7 @@ export class GameServer {
public serve() {
if (!this.server)
this.server = Bun.serve<SessionData>({
host: Constants.HOST,
hostname: Constants.HOST,
port: Constants.SERVER_PORT,
fetch: (req, srv) => this.fetchHandler(req, srv),
websocket: {