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-06-07 05:56:06 +00:00
|
|
|
import { TICK_LENGTH } from "../ecs/Lockstep";
|
2020-05-18 03:39:28 +00:00
|
|
|
import {
|
|
|
|
catchTalkback,
|
|
|
|
defer,
|
|
|
|
interval,
|
|
|
|
lazy,
|
|
|
|
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-18 03:39:28 +00:00
|
|
|
const heloSource: Callbag<never, ServerMessage<never, never>> = lazy(() => ({ t: MessageTypes.META, helo: "In-Process Loopback Server" }));
|
|
|
|
|
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(
|
2020-06-07 05:56:06 +00:00
|
|
|
interval(TICK_LENGTH),
|
2020-05-03 01:14:15 +00:00
|
|
|
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(
|
2020-05-18 03:39:28 +00:00
|
|
|
merge(this.serverFeed, heloSource),
|
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
|
|
|
}
|