2020-02-16 05:42:58 +00:00
|
|
|
import { Callbag } from "callbag";
|
2020-05-03 01:14:15 +00:00
|
|
|
import pipe from "callbag-pipe";
|
|
|
|
import share from "callbag-share";
|
2020-02-16 05:42:58 +00:00
|
|
|
|
2020-05-03 01:14:15 +00:00
|
|
|
import { INPUT_FREQUENCY } from "../Ecs/Lockstep";
|
|
|
|
import { catchTalkback, defer, interval, makeSubject, map, merge } from "../Utilities/Callbag";
|
2020-05-09 23:33:19 +00:00
|
|
|
import { ClientMessage, MessageTypes, ServerMessage } from "./LockstepClient";
|
2020-02-16 05:42:58 +00:00
|
|
|
|
2020-05-03 01:14:15 +00:00
|
|
|
/** Stub loopback server that handles multiple clients, for schemes where GlobalInput = LocalInput[] */
|
|
|
|
export class LoopbackServer<LocalInput, State> {
|
|
|
|
|
|
|
|
private nextClientId = 0;
|
|
|
|
private inputBuffer: LocalInput[] = [];
|
|
|
|
|
|
|
|
private heartbeat = pipe(
|
|
|
|
interval(INPUT_FREQUENCY),
|
|
|
|
map(() => ({
|
|
|
|
t: MessageTypes.INPUT,
|
|
|
|
i: this.inputBuffer.slice()
|
|
|
|
} as ServerMessage<LocalInput[], State>)),
|
|
|
|
);
|
|
|
|
private broadcast = makeSubject<ServerMessage<LocalInput[], State>>();
|
|
|
|
|
|
|
|
private serverFeed = share(merge(this.heartbeat, this.broadcast));
|
|
|
|
|
|
|
|
public readonly socket = defer(() => {
|
|
|
|
const playerNumber = this.nextClientId++;
|
|
|
|
return pipe(
|
|
|
|
this.serverFeed,
|
2020-05-09 23:33:19 +00:00
|
|
|
catchTalkback((message: ClientMessage<LocalInput, State>) => this.processMessage(playerNumber, message)),
|
|
|
|
map(message => this.postprocessResponse(playerNumber, message))
|
2020-05-03 01:14:15 +00:00
|
|
|
);
|
2020-02-16 05:42:58 +00:00
|
|
|
});
|
2020-05-03 01:14:15 +00:00
|
|
|
|
2020-05-09 23:33:19 +00:00
|
|
|
private processMessage(playerNumber: number, message: ClientMessage<LocalInput, State>) {
|
|
|
|
switch (message.t) {
|
|
|
|
case MessageTypes.INPUT:
|
|
|
|
if (playerNumber >= 0) {
|
|
|
|
this.inputBuffer[playerNumber] = message.i;
|
|
|
|
}
|
|
|
|
break;
|
2020-05-10 02:08:22 +00:00
|
|
|
case MessageTypes.SET_STATE:
|
|
|
|
this.broadcast(1, { t: MessageTypes.SET_STATE, u: -1, s: message.s });
|
|
|
|
break;
|
2020-05-09 23:33:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2020-05-03 01:14:15 +00:00
|
|
|
}
|