Sketch out first pass of lockstep client code
This commit is contained in:
parent
33ddb75b14
commit
5e29841717
3 changed files with 89 additions and 5 deletions
|
@ -1,4 +1,6 @@
|
|||
|
||||
export const INPUT_FREQUENCY = 33; // roughly 30fps
|
||||
|
||||
export interface DeepCopy<T> {
|
||||
deepCopy(patch: Partial<T>): T;
|
||||
}
|
||||
|
@ -17,7 +19,7 @@ function equals<T extends Equals | string | number>(a: T, b: T): boolean {
|
|||
return (a as Equals).equals(b as Equals);
|
||||
}
|
||||
|
||||
interface LockstepProcessor<Input, State> {
|
||||
export interface LockstepProcessor<Input, State> {
|
||||
compareInput(a: Input, b: Input): boolean;
|
||||
cloneState(source: State): State;
|
||||
advanceState(state: State, input: Input): void;
|
||||
|
@ -97,7 +99,3 @@ export class Playback<Input, State> {
|
|||
return this.state;
|
||||
}
|
||||
}
|
||||
|
||||
export class LockstepLoop<Input, State extends DeepCopy<State>> {
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
import {} from "../Ecs/Lockstep";
|
||||
import {} from "./GameComponents";
|
||||
|
|
85
src/Net/LockstepClient.ts
Normal file
85
src/Net/LockstepClient.ts
Normal file
|
@ -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<TypeId, Payload> = { t: TypeId } & Payload;
|
||||
|
||||
export type ClientMessage<Input, State> =
|
||||
| Packet<MessageTypes.INPUT, { i: Partial<Input> }>;
|
||||
|
||||
export type ServerMessage<Input, State> =
|
||||
| Packet<MessageTypes.RESET, { s: Partial<State> }>
|
||||
| Packet<MessageTypes.INPUT, { i: Input }>;
|
||||
|
||||
type Server<Input, State> = Callbag<ClientMessage<Input, State>, ServerMessage<Input, State>>;
|
||||
|
||||
export abstract class LockstepClient<Input, State> {
|
||||
|
||||
private state: LockstepState<Input, State>;
|
||||
|
||||
public constructor(
|
||||
public readonly engine: LockstepProcessor<Input, State>,
|
||||
) {
|
||||
const initialState = this.initState({});
|
||||
this.state = new LockstepState(initialState, engine);
|
||||
}
|
||||
|
||||
public abstract initState(init: Partial<State>): State;
|
||||
|
||||
public abstract gatherInput(): Input;
|
||||
|
||||
/**
|
||||
* Connect to a [perhaps emulated] server and return a disconnect callback
|
||||
*/
|
||||
public connect(server: Server<Input, State>): () => void {
|
||||
let serverTalkback: Server<Input, State> | 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<Input, State> | ServerMessage<Input, State>) => {
|
||||
if (mode == 0) {
|
||||
serverTalkback = data as Server<Input, State>;
|
||||
|
||||
// kickoff input sender
|
||||
setTimeout(sampleInput, INPUT_FREQUENCY);
|
||||
} else if (mode == 1) {
|
||||
// server message
|
||||
const message = data as ServerMessage<Input, State>;
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue