Basic networking #9
5 changed files with 116 additions and 37 deletions
|
@ -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
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 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,16 +63,21 @@ 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 != "") {
|
||||||
this.send<PasswordRequest>(conn, { kind: "password-req" });
|
const checkPasswordResponse = (data: any) => {
|
||||||
conn.on("data", this.checkPasswordResponse.bind(this, conn));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addPlayer(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
private checkPasswordResponse(conn: DataConnection, data: any) {
|
|
||||||
try {
|
try {
|
||||||
let resp = data as PasswordResponse;
|
let resp = data as PasswordResponse;
|
||||||
if (resp.password != this.room.info.password) {
|
if (resp.password != this.room.info.password) {
|
||||||
|
@ -59,6 +87,7 @@ export default class PeerServer extends NetworkPeer {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
conn.off("data", checkPasswordResponse);
|
||||||
this.addPlayer(conn);
|
this.addPlayer(conn);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.send<ErrorMessage>(conn, {
|
this.send<ErrorMessage>(conn, {
|
||||||
|
@ -66,6 +95,14 @@ export default class PeerServer extends NetworkPeer {
|
||||||
error: "not a password"
|
error: "not a password"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.send<PasswordRequest>(conn, { kind: "password-req" });
|
||||||
|
conn.on("data", checkPasswordResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addPlayer(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addPlayer(conn: DataConnection) {
|
private addPlayer(conn: DataConnection) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue