Basic networking #9
5 changed files with 116 additions and 37 deletions
|
@ -1,6 +1,6 @@
|
|||
import NetworkPeer from "./peer";
|
||||
import { DataConnection } from "peerjs";
|
||||
import { PeerMetadata } from "./types";
|
||||
import { PeerMetadata, NetworkMessage } from "./types";
|
||||
|
||||
export default class PeerClient extends NetworkPeer {
|
||||
private connection: DataConnection;
|
||||
|
@ -11,5 +11,11 @@ export default class PeerClient extends NetworkPeer {
|
|||
metadata,
|
||||
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
19
src/network/local.ts
Normal 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
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import Peer, { DataConnection } from "peerjs";
|
||||
import { NetworkMessage } from "./types";
|
||||
|
||||
export default class NetworkPeer {
|
||||
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?
|
||||
conn.send(data);
|
||||
}
|
||||
|
|
|
@ -8,30 +8,53 @@ import {
|
|||
PasswordResponse,
|
||||
PeerMetadata,
|
||||
JoinMessage,
|
||||
RoomInfoMessage
|
||||
RoomInfoMessage,
|
||||
Player,
|
||||
NetworkMessage,
|
||||
RenameMessage
|
||||
} 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 {
|
||||
private room: Room;
|
||||
|
||||
public constructor(roomInfo: RoomInfo) {
|
||||
public constructor(roomInfo: RoomInfo, local: LocalClient) {
|
||||
super();
|
||||
let players: Record<string, Player> = {};
|
||||
players[local.metadata.name] = {
|
||||
kind: "local",
|
||||
name: local.metadata.name,
|
||||
client: local
|
||||
};
|
||||
this.room = {
|
||||
info: roomInfo,
|
||||
players: {
|
||||
//TODO Add local player
|
||||
}
|
||||
players
|
||||
};
|
||||
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
|
||||
console.info(
|
||||
"%s (%s) connected!",
|
||||
(conn.metadata as PeerMetadata).name,
|
||||
conn.label
|
||||
);
|
||||
//
|
||||
|
||||
// Check if room is full
|
||||
if (this.playerCount >= this.room.info.max_players) {
|
||||
|
@ -40,16 +63,21 @@ export default class PeerServer extends NetworkPeer {
|
|||
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 != "") {
|
||||
this.send<PasswordRequest>(conn, { kind: "password-req" });
|
||||
conn.on("data", this.checkPasswordResponse.bind(this, conn));
|
||||
return;
|
||||
}
|
||||
|
||||
this.addPlayer(conn);
|
||||
}
|
||||
|
||||
private checkPasswordResponse(conn: DataConnection, data: any) {
|
||||
const checkPasswordResponse = (data: any) => {
|
||||
try {
|
||||
let resp = data as PasswordResponse;
|
||||
if (resp.password != this.room.info.password) {
|
||||
|
@ -59,6 +87,7 @@ export default class PeerServer extends NetworkPeer {
|
|||
});
|
||||
return;
|
||||
}
|
||||
conn.off("data", checkPasswordResponse);
|
||||
this.addPlayer(conn);
|
||||
} catch (e) {
|
||||
this.send<ErrorMessage>(conn, {
|
||||
|
@ -66,6 +95,14 @@ export default class PeerServer extends NetworkPeer {
|
|||
error: "not a password"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.send<PasswordRequest>(conn, { kind: "password-req" });
|
||||
conn.on("data", checkPasswordResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
this.addPlayer(conn);
|
||||
}
|
||||
|
||||
private addPlayer(conn: DataConnection) {
|
||||
|
@ -76,6 +113,9 @@ export default class PeerServer extends NetworkPeer {
|
|||
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
|
||||
this.send<RoomInfoMessage>(conn, {
|
||||
kind: "room-info",
|
||||
|
@ -93,17 +133,19 @@ export default class PeerServer extends NetworkPeer {
|
|||
});
|
||||
}
|
||||
|
||||
private _received(player: Player, data: NetworkMessage) {}
|
||||
|
||||
private get playerCount(): number {
|
||||
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) {
|
||||
const player = this.room.players[playerName];
|
||||
if (player.kind == "remote") {
|
||||
this.send<T>(player.conn, message);
|
||||
} else {
|
||||
//TODO Local wrapper
|
||||
player.client.receive(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { DataConnection } from "peerjs";
|
||||
import LocalClient from "./local";
|
||||
|
||||
export interface LocalPlayer {
|
||||
kind: "local";
|
||||
name: string;
|
||||
client: LocalClient;
|
||||
}
|
||||
|
||||
export interface NetworkPlayer {
|
||||
|
@ -11,13 +13,15 @@ export interface NetworkPlayer {
|
|||
conn: DataConnection;
|
||||
}
|
||||
|
||||
export type Player = NetworkPlayer | LocalPlayer;
|
||||
|
||||
export interface PeerMetadata {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Room {
|
||||
info: RoomInfo;
|
||||
players: Record<string, NetworkPlayer | LocalPlayer>;
|
||||
players: Record<string, Player>;
|
||||
}
|
||||
|
||||
export interface RoomInfo {
|
||||
|
@ -61,7 +65,8 @@ export type NetworkMessage =
|
|||
| PasswordResponse
|
||||
| ErrorMessage
|
||||
| RoomInfoMessage
|
||||
| JoinMessage;
|
||||
| JoinMessage
|
||||
| RenameMessage;
|
||||
|
||||
export interface PasswordRequest {
|
||||
kind: "password-req";
|
||||
|
@ -83,6 +88,12 @@ export interface JoinMessage {
|
|||
name: string;
|
||||
}
|
||||
|
||||
export interface RenameMessage {
|
||||
kind: "rename";
|
||||
oldname: string;
|
||||
newname: string;
|
||||
}
|
||||
|
||||
export interface ErrorMessage {
|
||||
kind: "error";
|
||||
error: string;
|
||||
|
|
Loading…
Reference in a new issue