Add basic multiplayer

This commit is contained in:
schmelczerandras 2020-10-05 19:07:17 +02:00
parent 0f0a1eaf67
commit 46a48e7c15
113 changed files with 1362 additions and 754 deletions

View file

@ -1,11 +0,0 @@
import { CommandReceiver } from './command-receiver';
import { CommandGenerator } from './command-generator';
export class CommandBroadcaster {
constructor(
commandGenerators: Array<CommandGenerator>,
commandReceivers: Array<CommandReceiver>
) {
commandReceivers.forEach((r) => commandGenerators.forEach((g) => g.subscribe(r)));
}
}

View file

@ -1,14 +0,0 @@
import { CommandReceiver } from './command-receiver';
import { Command } from './command';
export class CommandGenerator {
private subscribers: Array<CommandReceiver> = [];
public subscribe(subscriber: CommandReceiver): void {
this.subscribers.push(subscriber);
}
protected sendCommand(command: Command): void {
this.subscribers.forEach((s) => s.sendCommand(command));
}
}

View file

@ -1,5 +0,0 @@
import { Command } from './command';
export interface CommandReceiver {
sendCommand(command: Command): void;
}

View file

@ -1,6 +0,0 @@
import { Typed } from '../transport/serializable';
import { Id } from '../identity/identity';
export abstract class Command extends Typed {
target?: Id;
}

View file

@ -0,0 +1,42 @@
import { vec2 } from 'gl-matrix';
import { CommandGenerator, MoveActionCommand } from 'shared';
export class KeyboardListener extends CommandGenerator {
private keysDown: Set<string> = new Set();
constructor(target: Element, private moveScale: number) {
super();
target.addEventListener('keydown', (event: KeyboardEvent) => {
const key = this.normalize(event.key);
this.keysDown.add(key);
});
target.addEventListener('keyup', (event: KeyboardEvent) => {
const key = this.normalize(event.key);
this.keysDown.delete(key);
});
}
public generateCommands() {
const up = ~~(
this.keysDown.has('w') ||
this.keysDown.has('arrowup') ||
this.keysDown.has(' ')
);
const down = ~~(this.keysDown.has('s') || this.keysDown.has('arrowdown'));
const left = ~~(this.keysDown.has('a') || this.keysDown.has('arrowleft'));
const right = ~~(this.keysDown.has('d') || this.keysDown.has('arrowright'));
const movement = vec2.fromValues(right - left, up - down);
if (vec2.squaredLength(movement) > 0) {
vec2.normalize(movement, movement);
vec2.scale(movement, movement, this.moveScale);
this.sendCommandToSubcribers(new MoveActionCommand(movement));
}
}
private normalize(key: string): string {
return key.toLowerCase();
}
}

View file

@ -0,0 +1,36 @@
import { vec2 } from 'gl-matrix';
import {
CommandGenerator,
PrimaryActionCommand,
SecondaryActionCommand,
TernaryActionCommand,
} from 'shared';
export class MouseListener extends CommandGenerator {
constructor(target: Element) {
super();
target.addEventListener('mousemove', (event: MouseEvent) => {
const position = this.positionFromEvent(event);
this.sendCommandToSubcribers(new PrimaryActionCommand(position));
});
target.addEventListener('mousedown', (event: MouseEvent) => {
const position = this.positionFromEvent(event);
if (event.button == 0) {
this.sendCommandToSubcribers(new SecondaryActionCommand(position));
}
});
target.addEventListener('contextmenu', (event: MouseEvent) => {
event.preventDefault();
const position = this.positionFromEvent(event);
this.sendCommandToSubcribers(new TernaryActionCommand(position));
});
}
private positionFromEvent(event: MouseEvent): vec2 {
return vec2.fromValues(event.clientX, event.clientY);
}
}

View file

@ -0,0 +1,57 @@
import { vec2 } from 'gl-matrix';
import {
CommandGenerator,
MoveActionCommand,
PrimaryActionCommand,
SecondaryActionCommand,
TernaryActionCommand,
} from 'shared';
export class TouchListener extends CommandGenerator {
private previousPosition = vec2.create();
constructor(target: HTMLElement) {
super();
target.addEventListener('touchstart', (event: TouchEvent) => {
event.preventDefault();
const touchCount = event.touches.length;
const position = this.positionFromEvent(event);
this.previousPosition = position;
if (touchCount == 1) {
this.sendCommandToSubcribers(new PrimaryActionCommand(position));
} else if (touchCount == 2) {
this.sendCommandToSubcribers(new SecondaryActionCommand(position));
} else {
this.sendCommandToSubcribers(new TernaryActionCommand(position));
}
});
target.addEventListener('touchmove', (event: TouchEvent) => {
event.preventDefault();
const position = this.positionFromEvent(event);
this.sendCommandToSubcribers(
new MoveActionCommand(
vec2.subtract(vec2.create(), position, this.previousPosition)
)
);
this.previousPosition = position;
});
}
private positionFromEvent(event: TouchEvent): vec2 {
const center = Array.prototype.reduce.call(
event.touches,
(center: vec2, touch: Touch) =>
vec2.add(center, center, vec2.fromValues(-touch.clientX, touch.clientY)),
vec2.create()
);
return vec2.scale(center, center, 1 / event.touches.length);
}
}

View file

@ -1,12 +0,0 @@
import { vec2 } from 'gl-matrix';
import { Command } from './command';
export class MoveToCommand extends Command {
public constructor(public readonly position?: vec2) {
super();
}
public get type(): string {
return 'MoveToCommand';
}
}

View file

@ -0,0 +1,11 @@
import { Command, CommandReceiver, TransportEvents } from 'shared';
export class CommandReceiverSocket extends CommandReceiver {
constructor(private readonly socket: SocketIOClient.Socket) {
super();
}
protected defaultCommandExecutor(command: Command) {
this.socket.emit(TransportEvents.PlayerToServer, JSON.stringify(command));
}
}

View file

@ -1,12 +0,0 @@
import { Renderer } from 'sdf-2d';
import { Command } from './command';
export class RenderCommand extends Command {
public constructor(public readonly renderer?: Renderer) {
super();
}
public get type(): string {
return 'RenderCommand';
}
}

View file

@ -1,11 +0,0 @@
import { Command } from './command';
export class StepCommand extends Command {
public constructor(public readonly deltaTimeInMiliseconds?: DOMHighResTimeStamp) {
super();
}
public get type(): string {
return 'StepCommand';
}
}

View file

@ -1,12 +0,0 @@
import { vec2 } from 'gl-matrix';
import { Command } from './command';
export class TeleportToCommand extends Command {
public constructor(public readonly position?: vec2) {
super();
}
public get type(): string {
return 'TeleportToCommand';
}
}

View file

@ -0,0 +1,10 @@
import { vec2 } from 'gl-matrix';
import { Command } from 'shared';
export class MoveToCommand extends Command {
public static readonly type = 'MoveToCommand';
public constructor(public readonly position: vec2) {
super();
}
}

View file

@ -0,0 +1,10 @@
import { Renderer } from 'sdf-2d';
import { Command } from 'shared';
export class RenderCommand extends Command {
public static readonly type = 'RenderCommand';
public constructor(public readonly renderer: Renderer) {
super();
}
}

View file

@ -0,0 +1,9 @@
import { Command } from 'shared';
export class StepCommand extends Command {
public static readonly type = 'StepCommand';
public constructor(public readonly deltaTimeInMiliseconds: DOMHighResTimeStamp) {
super();
}
}