Refactor clients to have common base
This commit is contained in:
parent
862d84adf2
commit
942aef279d
8 changed files with 110 additions and 79 deletions
52
src/network/Client.ts
Normal file
52
src/network/Client.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { PeerMetadata, NetworkMessage } from "./types";
|
||||||
|
import EventEmitter from "eventemitter3";
|
||||||
|
|
||||||
|
export class Client extends EventEmitter {
|
||||||
|
public metadata: PeerMetadata;
|
||||||
|
public players: string[];
|
||||||
|
|
||||||
|
public constructor(metadata: PeerMetadata) {
|
||||||
|
super();
|
||||||
|
this.metadata = metadata;
|
||||||
|
this.players = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _received(data: NetworkMessage) {
|
||||||
|
this.emit("data", data);
|
||||||
|
switch (data.kind) {
|
||||||
|
// Someone changed name (or was forced to)
|
||||||
|
case "rename":
|
||||||
|
if (data.oldname == this.metadata.name) {
|
||||||
|
// We got a name change!
|
||||||
|
this.metadata.name = data.newname;
|
||||||
|
} else {
|
||||||
|
let idx = this.players.indexOf(data.oldname);
|
||||||
|
if (idx < 0) {
|
||||||
|
// Weird
|
||||||
|
console.warn(
|
||||||
|
`Someone (${data.oldname}) changed name but wasn't on the player list`
|
||||||
|
);
|
||||||
|
this.players.push(data.newname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.players[idx] = data.newname;
|
||||||
|
}
|
||||||
|
this.emit("rename", data.oldname, data.newname);
|
||||||
|
break;
|
||||||
|
// A new player joined the room (this includes us)
|
||||||
|
case "player-joined":
|
||||||
|
this.players.push(data.name);
|
||||||
|
this.emit("player-joined", data.name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// For most cases, we can just use the kind as event type
|
||||||
|
this.emit(data.kind, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string {
|
||||||
|
return this.metadata.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Client;
|
|
@ -1,18 +1,15 @@
|
||||||
import { PeerMetadata, NetworkMessage } from "./types";
|
import { PeerMetadata, NetworkMessage } from "./types";
|
||||||
import EventEmitter from "eventemitter3";
|
import Client from "./Client";
|
||||||
|
|
||||||
export class LocalClient extends EventEmitter {
|
export class LocalClient extends Client {
|
||||||
public metadata: PeerMetadata;
|
|
||||||
public receiver!: (data: NetworkMessage) => void;
|
public receiver!: (data: NetworkMessage) => void;
|
||||||
|
|
||||||
public constructor(metadata: PeerMetadata) {
|
public constructor(metadata: PeerMetadata) {
|
||||||
super();
|
super(metadata);
|
||||||
this.metadata = metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public receive(data: NetworkMessage) {
|
public receive(data: NetworkMessage) {
|
||||||
this.emit("data", data);
|
this._received(data);
|
||||||
//TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public send<T extends NetworkMessage>(data: T) {
|
public send<T extends NetworkMessage>(data: T) {
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import Peer, { DataConnection } from "peerjs";
|
|
||||||
import { NetworkMessage } from "./types";
|
|
||||||
import EventEmitter from "eventemitter3";
|
|
||||||
|
|
||||||
export class NetworkPeer extends EventEmitter {
|
|
||||||
protected peer: Peer;
|
|
||||||
protected constructor(peer?: Peer) {
|
|
||||||
super();
|
|
||||||
this.peer = peer ? peer : new Peer();
|
|
||||||
this.peer.on("open", function(id) {
|
|
||||||
console.info("Peer ID assigned: %s", id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected send<T extends NetworkMessage>(conn: DataConnection, data: T) {
|
|
||||||
conn.send(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NetworkPeer;
|
|
|
@ -1,16 +1,17 @@
|
||||||
import NetworkPeer from "./NetworkPeer";
|
|
||||||
import Peer, { DataConnection } from "peerjs";
|
import Peer, { DataConnection } from "peerjs";
|
||||||
import { PeerMetadata, NetworkMessage } from "./types";
|
import { PeerMetadata, NetworkMessage } from "./types";
|
||||||
|
import Client from "./Client";
|
||||||
|
|
||||||
export class PeerClient extends NetworkPeer {
|
export class PeerClient extends Client {
|
||||||
|
protected peer: Peer;
|
||||||
private connection?: DataConnection;
|
private connection?: DataConnection;
|
||||||
private metadata: PeerMetadata;
|
|
||||||
public players: string[];
|
|
||||||
|
|
||||||
public constructor(metadata: PeerMetadata, customPeer?: Peer) {
|
public constructor(metadata: PeerMetadata, customPeer?: Peer) {
|
||||||
super(customPeer);
|
super(metadata);
|
||||||
this.metadata = metadata;
|
this.peer = customPeer ? customPeer : new Peer();
|
||||||
this.players = [];
|
this.peer.on("open", function(id) {
|
||||||
|
console.info("Peer ID assigned: %s", id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect(peerid: string) {
|
public connect(peerid: string) {
|
||||||
|
@ -29,42 +30,13 @@ export class PeerClient extends NetworkPeer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _received(data: NetworkMessage) {
|
|
||||||
this.emit("data", data);
|
|
||||||
switch (data.kind) {
|
|
||||||
// Someone changed name (or was forced to)
|
|
||||||
case "rename":
|
|
||||||
if (data.oldname == this.metadata.name) {
|
|
||||||
// We got a name change!
|
|
||||||
this.metadata.name = data.newname;
|
|
||||||
} else {
|
|
||||||
let idx = this.players.indexOf(data.oldname);
|
|
||||||
if (idx < 0) {
|
|
||||||
// Weird
|
|
||||||
console.warn(
|
|
||||||
`Someone (${data.oldname}) changed name but wasn't on the player list`
|
|
||||||
);
|
|
||||||
this.players.push(data.newname);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.players[idx] = data.newname;
|
|
||||||
}
|
|
||||||
this.emit("rename", data.oldname, data.newname);
|
|
||||||
break;
|
|
||||||
// A new player joined the room (this includes us)
|
|
||||||
case "player-joined":
|
|
||||||
this.players.push(data.name);
|
|
||||||
this.emit("player-joined", data.name);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// For most cases, we can just use the kind as event type
|
|
||||||
this.emit(data.kind, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public get name(): string {
|
public get name(): string {
|
||||||
return this.metadata.name;
|
return this.metadata.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected send<T extends NetworkMessage>(conn: DataConnection, data: T) {
|
||||||
|
conn.send(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PeerClient;
|
export default PeerClient;
|
||||||
|
|
|
@ -15,7 +15,8 @@ import {
|
||||||
NetworkPlayer,
|
NetworkPlayer,
|
||||||
AckMessage
|
AckMessage
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { NetworkPeer, LocalClient } from ".";
|
import { LocalClient } from ".";
|
||||||
|
import EventEmitter from "eventemitter3";
|
||||||
|
|
||||||
// Increment name, add number at the end if not present
|
// Increment name, add number at the end if not present
|
||||||
// Examples:
|
// Examples:
|
||||||
|
@ -32,7 +33,8 @@ function nextName(name: string): string {
|
||||||
return name.substr(0, i) + (Number(name.slice(i)) + 1);
|
return name.substr(0, i) + (Number(name.slice(i)) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PeerServer extends NetworkPeer {
|
export class PeerServer extends EventEmitter {
|
||||||
|
protected peer: Peer;
|
||||||
private room: Room;
|
private room: Room;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
@ -40,21 +42,27 @@ export class PeerServer extends NetworkPeer {
|
||||||
local: LocalClient,
|
local: LocalClient,
|
||||||
customPeer?: Peer
|
customPeer?: Peer
|
||||||
) {
|
) {
|
||||||
super(customPeer);
|
super();
|
||||||
let players: Record<string, Player> = {};
|
let players: Record<string, Player> = {};
|
||||||
|
|
||||||
// Add local player to server
|
// Add local player to server
|
||||||
players[local.metadata.name] = {
|
players[local.name] = {
|
||||||
kind: "local",
|
kind: "local",
|
||||||
name: local.metadata.name,
|
name: local.name,
|
||||||
client: local
|
client: local
|
||||||
};
|
};
|
||||||
local.receiver = this._received.bind(this, players[local.metadata.name]);
|
local.receiver = this._received.bind(this, players[local.name]);
|
||||||
|
|
||||||
this.room = {
|
this.room = {
|
||||||
info: roomInfo,
|
info: roomInfo,
|
||||||
players
|
players
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Setup peer
|
||||||
|
this.peer = customPeer ? customPeer : new Peer();
|
||||||
|
this.peer.on("open", function(id) {
|
||||||
|
console.info("Peer ID assigned: %s", id);
|
||||||
|
});
|
||||||
this.peer.on("connection", conn => {
|
this.peer.on("connection", conn => {
|
||||||
this._connection(conn);
|
this._connection(conn);
|
||||||
});
|
});
|
||||||
|
@ -177,10 +185,18 @@ export class PeerServer extends NetworkPeer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get playerCount(): number {
|
public get playerCount(): number {
|
||||||
return Object.keys(this.room.players).length;
|
return Object.keys(this.room.players).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get players() {
|
||||||
|
return this.room.players;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected send<T extends NetworkMessage>(conn: DataConnection, data: T) {
|
||||||
|
conn.send(data);
|
||||||
|
}
|
||||||
|
|
||||||
private broadcast<T extends NetworkMessage>(message: T) {
|
private broadcast<T extends NetworkMessage>(message: T) {
|
||||||
for (const playerName in this.room.players) {
|
for (const playerName in this.room.players) {
|
||||||
const player = this.room.players[playerName];
|
const player = this.room.players[playerName];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export * from "./PeerClient";
|
export * from "./PeerClient";
|
||||||
export * from "./LocalClient";
|
export * from "./LocalClient";
|
||||||
export * from "./NetworkPeer";
|
export * from "./Client";
|
||||||
export * from "./PeerServer";
|
export * from "./PeerServer";
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
@ -8,11 +8,14 @@ export class MockHelper {
|
||||||
createServer(
|
createServer(
|
||||||
room: RoomInfo,
|
room: RoomInfo,
|
||||||
id: string = "test-server",
|
id: string = "test-server",
|
||||||
name: string = "server-client"
|
player?: LocalClient
|
||||||
) {
|
) {
|
||||||
const serverPeer = new MockPeer(this.mocks, id, {});
|
const serverPeer = new MockPeer(this.mocks, id, {});
|
||||||
const serverPlayer = new LocalClient({ name: name });
|
return new PeerServer(
|
||||||
return new PeerServer(room, serverPlayer, serverPeer as Peer);
|
room,
|
||||||
|
player ? player : new LocalClient({ name: "server-player" }),
|
||||||
|
serverPeer as Peer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createClient(id: string = "test-client", name: string = "client-peer") {
|
createClient(id: string = "test-client", name: string = "client-peer") {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { MockHelper } from "@/testing";
|
import { MockHelper } from "@/testing";
|
||||||
import { NetworkMessage } from "@/network";
|
import { NetworkMessage, LocalClient } from "@/network";
|
||||||
import { EventHook } from "@/testing/EventHook";
|
import { EventHook } from "@/testing/EventHook";
|
||||||
|
|
||||||
const sampleRoom = () => ({
|
const sampleRoom = () => ({
|
||||||
|
@ -47,6 +47,17 @@ describe("network/PeerServer", () => {
|
||||||
await hook.expect("client2-data", 1000, messageKind("player-joined"));
|
await hook.expect("client2-data", 1000, messageKind("player-joined"));
|
||||||
await hook.expect("client1-data", 1000, messageKind("player-joined"));
|
await hook.expect("client1-data", 1000, messageKind("player-joined"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Local server clients receives client events", async () => {
|
||||||
|
const mox = new MockHelper();
|
||||||
|
const hook = new EventHook();
|
||||||
|
const local = new LocalClient({ name: "server-player" });
|
||||||
|
const server = mox.createServer(sampleRoom(), "test-server", local);
|
||||||
|
const client = mox.createClient();
|
||||||
|
hook.hookEmitter(local, "player-joined", "local-joined");
|
||||||
|
client.connect("test-server");
|
||||||
|
await hook.expect("local-joined", 1000);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("network/PeerClient", () => {
|
describe("network/PeerClient", () => {
|
||||||
|
|
Loading…
Reference in a new issue