From c8589828c7f38045ba2c413157ed86c6bd06bc3f Mon Sep 17 00:00:00 2001 From: Tangent Wantwight Date: Sat, 2 May 2020 21:14:15 -0400 Subject: [PATCH] Replace the stub loopback server with a local, but multi-client server. --- src/Game/Main.ts | 6 ++-- src/Net/LoopbackServer.ts | 69 +++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/Game/Main.ts b/src/Game/Main.ts index 2f75099..4142aeb 100644 --- a/src/Game/Main.ts +++ b/src/Game/Main.ts @@ -7,7 +7,7 @@ import { Location, Polygon, PolygonComponent, RenderBounds } from "../Ecs/Compon import { Create } from "../Ecs/Data"; import { RunRenderBounds } from "../Ecs/Renderers"; import { LockstepClient } from "../Net/LockstepClient"; -import { Loopback } from "../Net/LoopbackServer"; +import { LoopbackServer } from "../Net/LoopbackServer"; import { Data, Engine, PlayerControl } from "./GameComponents"; import { Buttons } from "./Input"; @@ -19,7 +19,9 @@ export class Main extends LockstepClient { super(new Engine()); keys.setHandler(this.buttons); - this.connect(Loopback); + const server = new LoopbackServer(); + this.connect(server.socket); + server.resetState({}); pipe( this.renderFrames, diff --git a/src/Net/LoopbackServer.ts b/src/Net/LoopbackServer.ts index fde9bde..58624a5 100644 --- a/src/Net/LoopbackServer.ts +++ b/src/Net/LoopbackServer.ts @@ -1,28 +1,57 @@ import { Callbag } from "callbag"; +import pipe from "callbag-pipe"; +import share from "callbag-share"; -import { ClientMessage, MessageTypes, ServerMessage } from "./LockstepClient"; +import { INPUT_FREQUENCY } from "../Ecs/Lockstep"; +import { catchTalkback, defer, interval, makeSubject, map, merge } from "../Utilities/Callbag"; +import { ClientMessage, MessageTypes, Server, ServerMessage } from "./LockstepClient"; type Client = Callbag, ClientMessage>; -/** Stub loopback server that handles a single client, for schemes where GlobalInput = LocalInput[] */ -export function Loopback(start: number, data?: Client | ClientMessage) { - if(start != 0) return; +/** Stub loopback server that handles multiple clients, for schemes where GlobalInput = LocalInput[] */ +export class LoopbackServer { - const sink = data as Client; + private nextClientId = 0; + private inputBuffer: LocalInput[] = []; - sink(0, (type: number, data?: Client | ClientMessage) => { - if(type == 1) { - // message from client; just reflect for now - const message = data as ClientMessage; - switch(message.t) { - case MessageTypes.INPUT: - sink(1, { - t: MessageTypes.INPUT, - i: [message.i], - }); - break; - } - } + private heartbeat = pipe( + interval(INPUT_FREQUENCY), + map(() => ({ + t: MessageTypes.INPUT, + i: this.inputBuffer.slice() + } as ServerMessage)), + ); + private broadcast = makeSubject>(); + + private serverFeed = share(merge(this.heartbeat, this.broadcast)); + + public readonly socket = defer(() => { + const playerNumber = this.nextClientId++; + return pipe( + this.serverFeed, + catchTalkback, ClientMessage>(message => { + switch (message.t) { + case MessageTypes.INPUT: + if (playerNumber >= 0) { + this.inputBuffer[playerNumber] = message.i; + } + break; + } + }), + map(message => { + if (message.t === MessageTypes.SET_STATE && playerNumber >= 0) { + return { + ...message, + u: playerNumber, + }; + } else { + return message; + } + }) + ); }); - sink(1, {t: MessageTypes.SET_STATE, u: 0, s: {}}); -}; + + public resetState(newState: Partial) { + this.broadcast(1, { t: MessageTypes.SET_STATE, u: -1, s: newState }); + } +}