UNPKG

gameguard

Version:

GameGuard is a NodeJS game server that can be used to manage the players connecting to your game, manage rooms and the players in them, and more.

251 lines (222 loc) 8.27 kB
'use strict' import ws from 'ws'; import Hypergiant from 'hypergiant'; import Message from './Message'; import { messageToBuffer, bufferToMessage } from './utils'; import GameGuardWebSocket from './interfaces/GameGuardWebSocket'; /** * Defines the properties and methods of a player, which is a client that has made * a successful connection to GameGuard. */ export default class Player { /** * The unique id of the player. * * @property {string} */ id: string; /** * The client's most probably ip address. * * @property {string} */ ip: string; /** * The client's WebSocket data. * * @property {GameGuardWebSocket} */ ws: GameGuardWebSocket; /** * The signal that gets dispatched when the player receives a messsage from * the GameGuard client. This signal contains the Message object of the message * received. * * @property {Hypergiant} */ messaged = new Hypergiant(); /** * The signal that gets dispatched when the player gets kicked from the GameGuard * server. This signal contains the player data object and the reason as to why the * player was kicked. * * @property {Hypergiant} */ kicked = new Hypergiant(); /** * The signal that gets dispatched when the player gets banned from the GameGuard * server. This signal contains the player data object and the reason as to why the * player was banned. * * @property {Hypergiant} */ banned = new Hypergiant(); /** * The signal that gets dispatched when the player attempts to connect to the * GameGuard server while banned and subsequently gets rejected. * * @property {Hypergiant} */ rejected = new Hypergiant(); /** * The roundtrip latency of this player. * * @property {number} */ latency = 0; /** * Indicates whether this player is still connected to the GameGuard server or * not. * * @private * * @property {boolean} */ private _isAlive = true; /** * The id of the heartbeat interval timer. * * @private * * @property {number} */ private _heartbeatIntervalId!: ReturnType<typeof setTimeout>; /** * The id of the latency interval timer. * * @private * * @property {number} */ private _latencyIntervalId!: ReturnType<typeof setTimeout>; /** * @param {string} id The unique id of the player. * @param {string} ip The most probably ip address of the player. * @param {GameGuardWebSocket} ws The client's WebSocket data. */ constructor(id: string, ip: string, ws: GameGuardWebSocket) { this.id = id; this.ip = ip; this.ws = ws; // When the player receives a message we have call `_onMessage` to convert it // from binary to a string and then dispatch the signal for it. this.ws.on('message', (event: ArrayBuffer) => this._onmessage(event)); // When the player's socket receives a pong, we respond with `_onPong` to let the // GameGuard server know this player is still connected. this.ws.on('pong', () => this._onpong()); } /** * Sends a message to this player's GameGuard client. * * @param {string} type The type of message that this message is. * @param {string} contents The contents of this message. */ message(type: string, contents: string) { const message = new Message(type, contents); this.ws.send(messageToBuffer(message)); } /** * Kicks this player from the GameGuard server and dispatches the `kicked` * signal. * * @param {string} [reason='You have been kicked from the server'] A custom reason for kicking the player. */ kick(reason: string = 'You have been kicked from the GameGuard server') { this.ws.close(4002, reason); this.kicked.dispatch(this, reason); } /** * Kicks the player from the GameGuard server, dispatches the `banned` signal, * and then adds sets them as banned in the database. * * @param {string} [reason='You have been banned'] A custom reason for banning the player. */ ban(reason: string = 'You have been banned from the GameGuard server') { this.ws.close(4003, reason); this.banned.dispatch(this, reason); } /** * Kicks the player from the GameGuard server and dispatches the `rejected` * signal. * * @param {string} [reason='Your connection to the server has been rejected'] A custom reason for rejecting the player. */ reject(reason: string = 'Your connection to the server has been rejected') { this.ws.close(4004, reason); this.rejected.dispatch(this, reason); } /** * Called when this player receives a message. * * @private * * @param {Object} data The data of the message received. */ private _onmessage(event: ArrayBuffer) { // First we have to get the Message representation of the message received. const message = bufferToMessage(event); // The GameGuard client has responded to our latency-ping request so we now have enough information // to calcualte the roundtrip latency of the player. if (message.type === 'latency-pong') this._calculateLatency(parseInt(message.contents)); // Otherwise the message isn't for us so we just dispatch the `messageReceived` signal. else this.messaged.dispatch(message); } /** * Called when the player receives a 'latency-pong' message. We use this to * calculate the roundtrip latency and send the results back to the player. * * @param {number} previousTime The time calculated by the GameGuard client. */ private _calculateLatency(previousTime: number) { // The GameGuard client has responded to our latency-ping request so we now have enough information // to calcualte the roundtrip latency of the player. const currentTime = Date.now(); this.latency = (currentTime - previousTime) / 2; // Now we send a message to the GameGuard client to let the user know their most up to date latency. this.message('latency', this.latency.toString()); } /** * Called when the player receives a pong and sets `_isAlive` to true because * they have responded to the ping. * * @private */ private _onpong() { this._isAlive = true; } /** * Checks to see if the player has responded to the last ping check by the value * of the `_isAlive` variable and if not then they are kicked from the server. If * the player is still connected then we send another ping request. * * @private */ private _ping() { if (!this._isAlive) { this.kick('Player not responding to heartbeat checks'); return; } this._isAlive = false; this.ws.ping(() => { }); } /** * Creates a hearbeat timer to check if the player is still connected to the * GameGuard server or not. * * @param {number} pingInterval The interval at which the GameGuard server checks to see if the player is still connected. */ createHearbeatCheck(pingInterval: number) { this._heartbeatIntervalId = setInterval(() => { this._ping(); }, pingInterval); } /** * Creates a latency timer to check the amount of time it takes for a packet * of data to get to the player's client and back. * * @param {number} latencyInterval The interval at which the GameGuard server checks the player's latency. */ createLatencyCheck(latencyInterval: number) { this._latencyIntervalId = setInterval(() => { this.message('latency-ping', `${Date.now()}`); }, latencyInterval); } }