Break up client & server message handling logic into their own functions for better readability.

This commit is contained in:
Tangent Wantwight 2020-05-09 19:33:19 -04:00
parent 6427361d7b
commit 7c06ab473d
2 changed files with 57 additions and 50 deletions

View file

@ -55,6 +55,7 @@ export abstract class LockstepClient<LocalInput, GlobalInput, State> {
private playerNumber = -1; private playerNumber = -1;
private state: LockstepState<LocalInput, GlobalInput, State>; private state: LockstepState<LocalInput, GlobalInput, State>;
private serverTalkback: Server<LocalInput, GlobalInput, State> | null = null;
public constructor( public constructor(
public readonly engine: LockstepProcessor<LocalInput, GlobalInput, State>, public readonly engine: LockstepProcessor<LocalInput, GlobalInput, State>,
@ -69,55 +70,59 @@ export abstract class LockstepClient<LocalInput, GlobalInput, State> {
/** /**
* Connect to a [perhaps emulated] server and return a disconnect callback * Connect to a [perhaps emulated] server and return a disconnect callback
*
* Only call this once or things will break.
*/ */
public connect(server: Server<LocalInput, GlobalInput, State>): () => void { public connect(server: Server<LocalInput, GlobalInput, State>): () => void {
let serverTalkback: Server<LocalInput, GlobalInput, State> | 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 // connect to server
server(0, (mode: number, data: Server<LocalInput, GlobalInput, State> | ServerMessage<GlobalInput, State>) => { server(0, (mode: number, data: Server<LocalInput, GlobalInput, State> | ServerMessage<GlobalInput, State>) => {
if (mode == 0) { if (mode == 0) {
serverTalkback = data as Server<LocalInput, GlobalInput, State>; this.serverTalkback = data as Server<LocalInput, GlobalInput, State>;
// kickoff input sender // kickoff input sender
setTimeout(sampleInput, INPUT_FREQUENCY); setTimeout(this.sampleInput, INPUT_FREQUENCY);
} else if (mode == 1) { } else if (mode == 1) {
// server message // server message
const message = data as ServerMessage<GlobalInput, State>; const message = data as ServerMessage<GlobalInput, State>;
switch (message.t) { this.processMessage(message);
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;
}
} else if (mode == 2) { } else if (mode == 2) {
// disconnected // disconnected
console.log("Disconnected from server", data); console.log("Disconnected from server", data);
serverTalkback = null; this.serverTalkback = null;
} }
}); });
// disposal // disposal
return () => { return () => {
serverTalkback?.(2); this.serverTalkback?.(2);
serverTalkback = null; 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<GlobalInput, State>) {
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( public renderFrames = pipe(
animationFrames, animationFrames,
map(_ms => this.state.getStateToRender()) map(_ms => this.state.getStateToRender())

View file

@ -4,9 +4,7 @@ import share from "callbag-share";
import { INPUT_FREQUENCY } from "../Ecs/Lockstep"; import { INPUT_FREQUENCY } from "../Ecs/Lockstep";
import { catchTalkback, defer, interval, makeSubject, map, merge } from "../Utilities/Callbag"; import { catchTalkback, defer, interval, makeSubject, map, merge } from "../Utilities/Callbag";
import { ClientMessage, MessageTypes, Server, ServerMessage } from "./LockstepClient"; import { ClientMessage, MessageTypes, ServerMessage } from "./LockstepClient";
type Client<LocalInput, State> = Callbag<ServerMessage<LocalInput[], State>, ClientMessage<LocalInput, State>>;
/** Stub loopback server that handles multiple clients, for schemes where GlobalInput = LocalInput[] */ /** Stub loopback server that handles multiple clients, for schemes where GlobalInput = LocalInput[] */
export class LoopbackServer<LocalInput, State> { export class LoopbackServer<LocalInput, State> {
@ -29,28 +27,32 @@ export class LoopbackServer<LocalInput, State> {
const playerNumber = this.nextClientId++; const playerNumber = this.nextClientId++;
return pipe( return pipe(
this.serverFeed, this.serverFeed,
catchTalkback<ServerMessage<LocalInput[], State>, ClientMessage<LocalInput, State>>(message => { catchTalkback((message: ClientMessage<LocalInput, State>) => this.processMessage(playerNumber, message)),
switch (message.t) { map(message => this.postprocessResponse(playerNumber, message))
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;
}
})
); );
}); });
private processMessage(playerNumber: number, message: ClientMessage<LocalInput, State>) {
switch (message.t) {
case MessageTypes.INPUT:
if (playerNumber >= 0) {
this.inputBuffer[playerNumber] = message.i;
}
break;
}
}
private postprocessResponse(playerNumber: number, message: ServerMessage<LocalInput[], State>): ServerMessage<LocalInput[], State> {
if (message.t === MessageTypes.SET_STATE && playerNumber >= 0) {
return {
...message,
u: playerNumber,
};
} else {
return message;
}
}
public resetState(newState: Partial<State>) { public resetState(newState: Partial<State>) {
this.broadcast(1, { t: MessageTypes.SET_STATE, u: -1, s: newState }); this.broadcast(1, { t: MessageTypes.SET_STATE, u: -1, s: newState });
} }