Basic networking #9

Merged
hamcha merged 17 commits from feature/basic-networking into master 2019-09-06 12:36:11 +00:00
5 changed files with 116 additions and 37 deletions
Showing only changes of commit 005290d057 - Show all commits

View file

@ -1,6 +1,6 @@
import NetworkPeer from "./peer"; import NetworkPeer from "./peer";
import { DataConnection } from "peerjs"; import { DataConnection } from "peerjs";
import { PeerMetadata } from "./types"; import { PeerMetadata, NetworkMessage } from "./types";
export default class PeerClient extends NetworkPeer { export default class PeerClient extends NetworkPeer {
private connection: DataConnection; private connection: DataConnection;
@ -11,5 +11,11 @@ export default class PeerClient extends NetworkPeer {
metadata, metadata,
reliable: true reliable: true
}); });
this.connection.on("open", () => {
console.info("Connected to server");
});
this.connection.on("data", this._received);
} }
private _received(data: NetworkMessage) {}
} }

19
src/network/local.ts Normal file
View file

@ -0,0 +1,19 @@
import NetworkPeer from "./peer";
import { DataConnection } from "peerjs";
import { PeerMetadata, NetworkMessage } from "./types";
export default class LocalClient {
public metadata: PeerMetadata;
public constructor(metadata: PeerMetadata) {
this.metadata = metadata;
}
public receive(data: NetworkMessage) {
//TODO
}
public send(data: NetworkMessage) {
//TODO
}
}

View file

@ -1,4 +1,5 @@
import Peer, { DataConnection } from "peerjs"; import Peer, { DataConnection } from "peerjs";
import { NetworkMessage } from "./types";
export default class NetworkPeer { export default class NetworkPeer {
protected peer: Peer; protected peer: Peer;
@ -9,7 +10,7 @@ export default class NetworkPeer {
}); });
} }
protected send<T>(conn: DataConnection, data: T) { protected send<T extends NetworkMessage>(conn: DataConnection, data: T) {
//TODO Debugging support? //TODO Debugging support?
conn.send(data); conn.send(data);
} }

View file

@ -8,30 +8,53 @@ import {
PasswordResponse, PasswordResponse,
PeerMetadata, PeerMetadata,
JoinMessage, JoinMessage,
RoomInfoMessage RoomInfoMessage,
Player,
NetworkMessage,
RenameMessage
} from "./types"; } from "./types";
import LocalClient from "./local";
// Increment name, add number at the end if not present
// Examples:
// Guest -> Guest1
// Guest1 -> Guest2
// Guest9 -> Guest10
function nextName(name: string): string {
let i = 1;
for (; i < name.length; i++) {
if (!isNaN(Number(name.slice(i - name.length)))) {
break;
}
}
return name.substr(0, i) + (Number(name.slice(i)) + 1);
}
export default class PeerServer extends NetworkPeer { export default class PeerServer extends NetworkPeer {
private room: Room; private room: Room;
public constructor(roomInfo: RoomInfo) { public constructor(roomInfo: RoomInfo, local: LocalClient) {
super(); super();
let players: Record<string, Player> = {};
players[local.metadata.name] = {
kind: "local",
name: local.metadata.name,
client: local
};
this.room = { this.room = {
info: roomInfo, info: roomInfo,
players: { players
//TODO Add local player
}
}; };
this.peer.on("connection", this._connection); this.peer.on("connection", this._connection);
} }
private async _connection(conn: DataConnection) { private _connection(conn: DataConnection) {
const metadata = conn.metadata as PeerMetadata;
console.info("%s (%s) connected!", metadata.name, conn.label);
//
// Check if this connection should be allowed // Check if this connection should be allowed
console.info( //
"%s (%s) connected!",
(conn.metadata as PeerMetadata).name,
conn.label
);
// Check if room is full // Check if room is full
if (this.playerCount >= this.room.info.max_players) { if (this.playerCount >= this.room.info.max_players) {
@ -40,34 +63,48 @@ export default class PeerServer extends NetworkPeer {
return; return;
} }
// Check if there is already a player called that way
if (this.room.players[metadata.name]) {
// Force rename
const newname = nextName(metadata.name);
this.send<RenameMessage>(conn, {
kind: "rename",
oldname: metadata.name,
newname: newname
});
metadata.name = newname;
}
// Check for password
if (this.room.info.password != "") { if (this.room.info.password != "") {
const checkPasswordResponse = (data: any) => {
try {
let resp = data as PasswordResponse;
if (resp.password != this.room.info.password) {
this.send<ErrorMessage>(conn, {
kind: "error",
error: "invalid password"
});
return;
}
conn.off("data", checkPasswordResponse);
this.addPlayer(conn);
} catch (e) {
this.send<ErrorMessage>(conn, {
kind: "error",
error: "not a password"
});
}
};
this.send<PasswordRequest>(conn, { kind: "password-req" }); this.send<PasswordRequest>(conn, { kind: "password-req" });
conn.on("data", this.checkPasswordResponse.bind(this, conn)); conn.on("data", checkPasswordResponse);
return; return;
} }
this.addPlayer(conn); this.addPlayer(conn);
} }
private checkPasswordResponse(conn: DataConnection, data: any) {
try {
let resp = data as PasswordResponse;
if (resp.password != this.room.info.password) {
this.send<ErrorMessage>(conn, {
kind: "error",
error: "invalid password"
});
return;
}
this.addPlayer(conn);
} catch (e) {
this.send<ErrorMessage>(conn, {
kind: "error",
error: "not a password"
});
}
}
private addPlayer(conn: DataConnection) { private addPlayer(conn: DataConnection) {
const playerName = conn.metadata.name; const playerName = conn.metadata.name;
this.room.players[playerName] = { this.room.players[playerName] = {
@ -76,6 +113,9 @@ export default class PeerServer extends NetworkPeer {
conn: conn conn: conn
}; };
// Start listening for new messages
conn.on("data", this._received.bind(this, this.room.players[playerName]));
// Send the player info about the room // Send the player info about the room
this.send<RoomInfoMessage>(conn, { this.send<RoomInfoMessage>(conn, {
kind: "room-info", kind: "room-info",
@ -93,17 +133,19 @@ export default class PeerServer extends NetworkPeer {
}); });
} }
private _received(player: Player, data: NetworkMessage) {}
private get playerCount(): number { private get playerCount(): number {
return Object.keys(this.room.players).length; return Object.keys(this.room.players).length;
} }
private broadcast<T>(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];
if (player.kind == "remote") { if (player.kind == "remote") {
this.send<T>(player.conn, message); this.send<T>(player.conn, message);
} else { } else {
//TODO Local wrapper player.client.receive(message);
} }
} }
} }

View file

@ -1,8 +1,10 @@
import { DataConnection } from "peerjs"; import { DataConnection } from "peerjs";
import LocalClient from "./local";
export interface LocalPlayer { export interface LocalPlayer {
kind: "local"; kind: "local";
name: string; name: string;
client: LocalClient;
} }
export interface NetworkPlayer { export interface NetworkPlayer {
@ -11,13 +13,15 @@ export interface NetworkPlayer {
conn: DataConnection; conn: DataConnection;
} }
export type Player = NetworkPlayer | LocalPlayer;
export interface PeerMetadata { export interface PeerMetadata {
name: string; name: string;
} }
export interface Room { export interface Room {
info: RoomInfo; info: RoomInfo;
players: Record<string, NetworkPlayer | LocalPlayer>; players: Record<string, Player>;
} }
export interface RoomInfo { export interface RoomInfo {
@ -61,7 +65,8 @@ export type NetworkMessage =
| PasswordResponse | PasswordResponse
| ErrorMessage | ErrorMessage
| RoomInfoMessage | RoomInfoMessage
| JoinMessage; | JoinMessage
| RenameMessage;
export interface PasswordRequest { export interface PasswordRequest {
kind: "password-req"; kind: "password-req";
@ -83,6 +88,12 @@ export interface JoinMessage {
name: string; name: string;
} }
export interface RenameMessage {
kind: "rename";
oldname: string;
newname: string;
}
export interface ErrorMessage { export interface ErrorMessage {
kind: "error"; kind: "error";
error: string; error: string;