Break up client & server message handling logic into their own functions for better readability.
This commit is contained in:
parent
6427361d7b
commit
7c06ab473d
2 changed files with 57 additions and 50 deletions
|
@ -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())
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue