From 5e29841717beac083be8f98931cc1e5f8e5bd7ea Mon Sep 17 00:00:00 2001 From: Tangent Wantwight Date: Sat, 15 Feb 2020 19:52:00 -0500 Subject: [PATCH] Sketch out first pass of lockstep client code --- src/Ecs/Lockstep.ts | 8 ++-- src/Game/Main.ts | 1 + src/Net/LockstepClient.ts | 85 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/Net/LockstepClient.ts diff --git a/src/Ecs/Lockstep.ts b/src/Ecs/Lockstep.ts index 26e6950..f19d34a 100644 --- a/src/Ecs/Lockstep.ts +++ b/src/Ecs/Lockstep.ts @@ -1,4 +1,6 @@ +export const INPUT_FREQUENCY = 33; // roughly 30fps + export interface DeepCopy { deepCopy(patch: Partial): T; } @@ -17,7 +19,7 @@ function equals(a: T, b: T): boolean { return (a as Equals).equals(b as Equals); } -interface LockstepProcessor { +export interface LockstepProcessor { compareInput(a: Input, b: Input): boolean; cloneState(source: State): State; advanceState(state: State, input: Input): void; @@ -97,7 +99,3 @@ export class Playback { return this.state; } } - -export class LockstepLoop> { - -} diff --git a/src/Game/Main.ts b/src/Game/Main.ts index da6b48a..6ac5e04 100644 --- a/src/Game/Main.ts +++ b/src/Game/Main.ts @@ -1 +1,2 @@ +import {} from "../Ecs/Lockstep"; import {} from "./GameComponents"; diff --git a/src/Net/LockstepClient.ts b/src/Net/LockstepClient.ts new file mode 100644 index 0000000..10ebd84 --- /dev/null +++ b/src/Net/LockstepClient.ts @@ -0,0 +1,85 @@ + +import { Callbag } from "callbag"; + +import { INPUT_FREQUENCY, LockstepProcessor, LockstepState } from "../Ecs/Lockstep"; + +export const enum MessageTypes { + RESET = 0, + INPUT = 1, +} + +export type Packet = { t: TypeId } & Payload; + +export type ClientMessage = + | Packet }>; + +export type ServerMessage = + | Packet }> + | Packet; + +type Server = Callbag, ServerMessage>; + +export abstract class LockstepClient { + + private state: LockstepState; + + public constructor( + public readonly engine: LockstepProcessor, + ) { + const initialState = this.initState({}); + this.state = new LockstepState(initialState, engine); + } + + public abstract initState(init: Partial): State; + + public abstract gatherInput(): Input; + + /** + * Connect to a [perhaps emulated] server and return a disconnect callback + */ + public connect(server: Server): () => void { + let serverTalkback: Server | null = null; + + const sampleInput = () => { + if (serverTalkback) { + const input = this.gatherInput(); + this.state.addLocalInput(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; + + // kickoff input sender + setTimeout(sampleInput, INPUT_FREQUENCY); + } else if (mode == 1) { + // server message + const message = data as ServerMessage; + switch(message.t) { + case MessageTypes.RESET: + const resetState = this.initState(message.s); + this.state = new LockstepState(resetState, this.engine); + break; + case MessageTypes.INPUT: + this.state.addCanonInput(message.i); + break; + } + } else if (mode == 2) { + // disconnected + console.log("Disconnected from server", data); + serverTalkback = null; + } + }); + + // disposal + return () => { + serverTalkback?.(2); + serverTalkback = null; + }; + } + +} \ No newline at end of file