diff --git a/src/network/Client.ts b/src/network/Client.ts new file mode 100644 index 0000000..cf4e87b --- /dev/null +++ b/src/network/Client.ts @@ -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; diff --git a/src/network/LocalClient.ts b/src/network/LocalClient.ts index 154d818..152bb59 100644 --- a/src/network/LocalClient.ts +++ b/src/network/LocalClient.ts @@ -1,18 +1,15 @@ import { PeerMetadata, NetworkMessage } from "./types"; -import EventEmitter from "eventemitter3"; +import Client from "./Client"; -export class LocalClient extends EventEmitter { - public metadata: PeerMetadata; +export class LocalClient extends Client { public receiver!: (data: NetworkMessage) => void; public constructor(metadata: PeerMetadata) { - super(); - this.metadata = metadata; + super(metadata); } public receive(data: NetworkMessage) { - this.emit("data", data); - //TODO + this._received(data); } public send(data: T) { diff --git a/src/network/NetworkPeer.ts b/src/network/NetworkPeer.ts deleted file mode 100644 index b889266..0000000 --- a/src/network/NetworkPeer.ts +++ /dev/null @@ -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(conn: DataConnection, data: T) { - conn.send(data); - } -} - -export default NetworkPeer; diff --git a/src/network/PeerClient.ts b/src/network/PeerClient.ts index efa36c2..76b5253 100644 --- a/src/network/PeerClient.ts +++ b/src/network/PeerClient.ts @@ -1,16 +1,17 @@ -import NetworkPeer from "./NetworkPeer"; import Peer, { DataConnection } from "peerjs"; 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 metadata: PeerMetadata; - public players: string[]; public constructor(metadata: PeerMetadata, customPeer?: Peer) { - super(customPeer); - this.metadata = metadata; - this.players = []; + super(metadata); + this.peer = customPeer ? customPeer : new Peer(); + this.peer.on("open", function(id) { + console.info("Peer ID assigned: %s", id); + }); } 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 { return this.metadata.name; } + + protected send(conn: DataConnection, data: T) { + conn.send(data); + } } export default PeerClient; diff --git a/src/network/PeerServer.ts b/src/network/PeerServer.ts index ba0049e..12b34ee 100644 --- a/src/network/PeerServer.ts +++ b/src/network/PeerServer.ts @@ -15,7 +15,8 @@ import { NetworkPlayer, AckMessage } from "./types"; -import { NetworkPeer, LocalClient } from "."; +import { LocalClient } from "."; +import EventEmitter from "eventemitter3"; // Increment name, add number at the end if not present // Examples: @@ -32,7 +33,8 @@ function nextName(name: string): string { 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; public constructor( @@ -40,21 +42,27 @@ export class PeerServer extends NetworkPeer { local: LocalClient, customPeer?: Peer ) { - super(customPeer); + super(); let players: Record = {}; // Add local player to server - players[local.metadata.name] = { + players[local.name] = { kind: "local", - name: local.metadata.name, + name: local.name, client: local }; - local.receiver = this._received.bind(this, players[local.metadata.name]); + local.receiver = this._received.bind(this, players[local.name]); this.room = { info: roomInfo, 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._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; } + public get players() { + return this.room.players; + } + + protected send(conn: DataConnection, data: T) { + conn.send(data); + } + private broadcast(message: T) { for (const playerName in this.room.players) { const player = this.room.players[playerName]; diff --git a/src/network/index.ts b/src/network/index.ts index 66b428b..b90e236 100644 --- a/src/network/index.ts +++ b/src/network/index.ts @@ -1,5 +1,5 @@ export * from "./PeerClient"; export * from "./LocalClient"; -export * from "./NetworkPeer"; +export * from "./Client"; export * from "./PeerServer"; export * from "./types"; diff --git a/src/testing/MockHelper.ts b/src/testing/MockHelper.ts index 8b63404..dc1bdd1 100644 --- a/src/testing/MockHelper.ts +++ b/src/testing/MockHelper.ts @@ -8,11 +8,14 @@ export class MockHelper { createServer( room: RoomInfo, id: string = "test-server", - name: string = "server-client" + player?: LocalClient ) { const serverPeer = new MockPeer(this.mocks, id, {}); - const serverPlayer = new LocalClient({ name: name }); - return new PeerServer(room, serverPlayer, serverPeer as Peer); + return new PeerServer( + room, + player ? player : new LocalClient({ name: "server-player" }), + serverPeer as Peer + ); } createClient(id: string = "test-client", name: string = "client-peer") { diff --git a/src/tests/unit/network.spec.ts b/src/tests/unit/network.spec.ts index a4425f0..cc77397 100644 --- a/src/tests/unit/network.spec.ts +++ b/src/tests/unit/network.spec.ts @@ -1,5 +1,5 @@ import { MockHelper } from "@/testing"; -import { NetworkMessage } from "@/network"; +import { NetworkMessage, LocalClient } from "@/network"; import { EventHook } from "@/testing/EventHook"; const sampleRoom = () => ({ @@ -47,6 +47,17 @@ describe("network/PeerServer", () => { await hook.expect("client2-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", () => {