Basic networking #9
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 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<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 { 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<T extends NetworkMessage>(conn: DataConnection, data: T) {
|
||||
conn.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
export default PeerClient;
|
||||
|
|
|
@ -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<string, Player> = {};
|
||||
|
||||
// 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<T extends NetworkMessage>(conn: DataConnection, data: T) {
|
||||
conn.send(data);
|
||||
}
|
||||
|
||||
private broadcast<T extends NetworkMessage>(message: T) {
|
||||
for (const playerName in this.room.players) {
|
||||
const player = this.room.players[playerName];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export * from "./PeerClient";
|
||||
export * from "./LocalClient";
|
||||
export * from "./NetworkPeer";
|
||||
export * from "./Client";
|
||||
export * from "./PeerServer";
|
||||
export * from "./types";
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
Loading…
Reference in a new issue