diff --git a/src/Net/LockstepClient.ts b/src/Net/LockstepClient.ts index 02fddcb..7e36fbf 100644 --- a/src/Net/LockstepClient.ts +++ b/src/Net/LockstepClient.ts @@ -55,6 +55,7 @@ export abstract class LockstepClient { private playerNumber = -1; private state: LockstepState; + private serverTalkback: Server | null = null; public constructor( public readonly engine: LockstepProcessor, @@ -69,55 +70,59 @@ export abstract class LockstepClient { /** * Connect to a [perhaps emulated] server and return a disconnect callback + * + * Only call this once or things will break. */ public connect(server: Server): () => void { - let serverTalkback: Server | null = null; - - const sampleInput = () => { - if (serverTalkback) { - const input = this.gatherInput(); - if(this.playerNumber >= 0) { - this.state.addLocalInput(this.playerNumber, input); - serverTalkback(1, { t: MessageTypes.INPUT, i: input }); - } - setTimeout(sampleInput, INPUT_FREQUENCY); - } - }; - // connect to server server(0, (mode: number, data: Server | ServerMessage) => { if (mode == 0) { - serverTalkback = data as Server; + this.serverTalkback = data as Server; // kickoff input sender - setTimeout(sampleInput, INPUT_FREQUENCY); + setTimeout(this.sampleInput, INPUT_FREQUENCY); } else if (mode == 1) { // server message const message = data as ServerMessage; - switch (message.t) { - case MessageTypes.SET_STATE: - const resetState = this.initState(message.s); - this.state = new LockstepState(resetState, this.engine); - this.playerNumber = message.u; - break; - case MessageTypes.INPUT: - this.state.addCanonInput(message.i); - break; - } + this.processMessage(message); } else if (mode == 2) { // disconnected console.log("Disconnected from server", data); - serverTalkback = null; + this.serverTalkback = null; } }); // disposal return () => { - serverTalkback?.(2); - serverTalkback = null; + this.serverTalkback?.(2); + this.serverTalkback = null; }; } + private sampleInput = () => { + if (this.serverTalkback) { + const input = this.gatherInput(); + if(this.playerNumber >= 0) { + this.state.addLocalInput(this.playerNumber, input); + this.serverTalkback(1, { t: MessageTypes.INPUT, i: input }); + } + setTimeout(this.sampleInput, INPUT_FREQUENCY); + } + }; + + private processMessage(message: ServerMessage) { + switch (message.t) { + case MessageTypes.SET_STATE: + const resetState = this.initState(message.s); + this.state = new LockstepState(resetState, this.engine); + this.playerNumber = message.u; + break; + case MessageTypes.INPUT: + this.state.addCanonInput(message.i); + break; + } + } + public renderFrames = pipe( animationFrames, map(_ms => this.state.getStateToRender()) diff --git a/src/Net/LoopbackServer.ts b/src/Net/LoopbackServer.ts index 58624a5..1293658 100644 --- a/src/Net/LoopbackServer.ts +++ b/src/Net/LoopbackServer.ts @@ -4,9 +4,7 @@ import share from "callbag-share"; 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>; +import { ClientMessage, MessageTypes, ServerMessage } from "./LockstepClient"; /** Stub loopback server that handles multiple clients, for schemes where GlobalInput = LocalInput[] */ export class LoopbackServer { @@ -29,28 +27,32 @@ export class LoopbackServer { 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; - } - }) + catchTalkback((message: ClientMessage) => this.processMessage(playerNumber, message)), + map(message => this.postprocessResponse(playerNumber, message)) ); }); + private processMessage(playerNumber: number, message: ClientMessage) { + switch (message.t) { + case MessageTypes.INPUT: + if (playerNumber >= 0) { + this.inputBuffer[playerNumber] = message.i; + } + break; + } + } + + private postprocessResponse(playerNumber: number, message: ServerMessage): ServerMessage { + if (message.t === MessageTypes.SET_STATE && playerNumber >= 0) { + return { + ...message, + u: playerNumber, + }; + } else { + return message; + } + } + public resetState(newState: Partial) { this.broadcast(1, { t: MessageTypes.SET_STATE, u: -1, s: newState }); }