Replace the stub loopback server with a local, but multi-client server.

This commit is contained in:
Tangent Wantwight 2020-05-02 21:14:15 -04:00
parent 633611056e
commit c8589828c7
2 changed files with 53 additions and 22 deletions

View file

@ -7,7 +7,7 @@ import { Location, Polygon, PolygonComponent, RenderBounds } from "../Ecs/Compon
import { Create } from "../Ecs/Data"; import { Create } from "../Ecs/Data";
import { RunRenderBounds } from "../Ecs/Renderers"; import { RunRenderBounds } from "../Ecs/Renderers";
import { LockstepClient } from "../Net/LockstepClient"; import { LockstepClient } from "../Net/LockstepClient";
import { Loopback } from "../Net/LoopbackServer"; import { LoopbackServer } from "../Net/LoopbackServer";
import { Data, Engine, PlayerControl } from "./GameComponents"; import { Data, Engine, PlayerControl } from "./GameComponents";
import { Buttons } from "./Input"; import { Buttons } from "./Input";
@ -19,7 +19,9 @@ export class Main extends LockstepClient<KeyName[], KeyName[][], Data> {
super(new Engine()); super(new Engine());
keys.setHandler(this.buttons); keys.setHandler(this.buttons);
this.connect(Loopback); const server = new LoopbackServer<KeyName[], Data>();
this.connect(server.socket);
server.resetState({});
pipe( pipe(
this.renderFrames, this.renderFrames,

View file

@ -1,28 +1,57 @@
import { Callbag } from "callbag"; import { Callbag } from "callbag";
import pipe from "callbag-pipe";
import share from "callbag-share";
import { ClientMessage, MessageTypes, ServerMessage } from "./LockstepClient"; import { INPUT_FREQUENCY } from "../Ecs/Lockstep";
import { catchTalkback, defer, interval, makeSubject, map, merge } from "../Utilities/Callbag";
import { ClientMessage, MessageTypes, Server, ServerMessage } from "./LockstepClient";
type Client<LocalInput, State> = Callbag<ServerMessage<LocalInput[], State>, ClientMessage<LocalInput, State>>; type Client<LocalInput, State> = Callbag<ServerMessage<LocalInput[], State>, ClientMessage<LocalInput, State>>;
/** Stub loopback server that handles a single client, for schemes where GlobalInput = LocalInput[] */ /** Stub loopback server that handles multiple clients, for schemes where GlobalInput = LocalInput[] */
export function Loopback<LocalInput, State>(start: number, data?: Client<LocalInput, State> | ClientMessage<LocalInput, State>) { export class LoopbackServer<LocalInput, State> {
if(start != 0) return;
const sink = data as Client<LocalInput, State>; private nextClientId = 0;
private inputBuffer: LocalInput[] = [];
sink(0, (type: number, data?: Client<LocalInput, State> | ClientMessage<LocalInput, State>) => { private heartbeat = pipe(
if(type == 1) { interval(INPUT_FREQUENCY),
// message from client; just reflect for now map(() => ({
const message = data as ClientMessage<LocalInput, State>; 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,
catchTalkback<ServerMessage<LocalInput[], State>, ClientMessage<LocalInput, State>>(message => {
switch (message.t) { switch (message.t) {
case MessageTypes.INPUT: case MessageTypes.INPUT:
sink(1, { if (playerNumber >= 0) {
t: MessageTypes.INPUT, this.inputBuffer[playerNumber] = message.i;
i: [message.i], }
});
break; break;
} }
} }),
}); map(message => {
sink(1, {t: MessageTypes.SET_STATE, u: 0, s: {}}); if (message.t === MessageTypes.SET_STATE && playerNumber >= 0) {
return {
...message,
u: playerNumber,
}; };
} else {
return message;
}
})
);
});
public resetState(newState: Partial<State>) {
this.broadcast(1, { t: MessageTypes.SET_STATE, u: -1, s: newState });
}
}