shoehive
Version:
WebSocket-based multiplayer game framework for real-time, event-driven gameplay
165 lines (164 loc) • 5.74 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Player = void 0;
const EventTypes_1 = require("../events/EventTypes");
const WebSocket = __importStar(require("ws"));
const crypto_1 = __importDefault(require("crypto"));
/**
* Represents a connected client in the game.
*
* ✅ Attribute Support
*
* The Player class handles communication with the client and keeps track
* of the player's current table and custom attributes.
*
*/
class Player {
constructor(socket, eventBus, id) {
this.table = null;
this.attributes = new Map();
this.disconnectCallbacks = [];
this.id = id || crypto_1.default.randomUUID();
this.socket = socket;
this.eventBus = eventBus;
this.setupSocketListeners();
}
setupSocketListeners() {
this.socket.on('close', () => {
this.eventBus.emit(EventTypes_1.PLAYER_EVENTS.DISCONNECTED, { player: this });
// Call all disconnect callbacks
this.disconnectCallbacks.forEach((callback) => callback());
});
this.socket.on('error', (error) => {
console.error(`Socket error for player ${this.id}:`, error);
});
}
/**
* Register a callback to be called when the player disconnects
* @param callback The function to call when the player disconnects
*/
onDisconnect({ callback }) {
this.disconnectCallbacks.push(callback);
}
sendMessage({ message }) {
if (this.socket.readyState === WebSocket.WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
}
/**
* Updates the socket connection for this player.
* Useful for handling reconnections without losing player state.
* @param socket The new WebSocket connection
*/
setSocket({ socket }) {
this.socket = socket;
this.setupSocketListeners();
}
setTable({ table }) {
this.table = table;
}
getTable() {
return this.table;
}
/**
* Set a single attribute on the player and emit an event for the change.
*
* @param key The attribute name
* @param value The attribute value
* @param notify Whether to emit an event (defaults to true)
*/
setAttribute({ key, value, notify = true, }) {
this.attributes.set(key, value);
if (notify) {
this.eventBus.emit(EventTypes_1.PLAYER_EVENTS.ATTRIBUTE_CHANGED, { player: this, key, value });
}
}
/**
* Set multiple attributes at once and emit a single event.
* This is more efficient than calling setAttribute multiple times.
*
* @param attributes Object containing attribute key-value pairs
*/
setAttributes({ attributes }) {
const changedKeys = [];
// Set all attributes first
for (const [key, value] of Object.entries(attributes)) {
this.attributes.set(key, value);
changedKeys.push(key);
}
// Then emit a single event for all changes
if (changedKeys.length > 0) {
this.eventBus.emit(EventTypes_1.PLAYER_EVENTS.ATTRIBUTES_CHANGED, {
player: this,
changedKeys,
attributes,
});
// Also emit individual events for backward compatibility
for (const key of changedKeys) {
this.eventBus.emit(EventTypes_1.PLAYER_EVENTS.ATTRIBUTE_CHANGED, {
player: this,
key,
value: attributes[key],
});
}
}
}
/**
* Get a single attribute from the player.
* @param key - The key of the attribute to get
* @returns The value of the attribute, or undefined if it doesn't exist
*/
getAttribute({ key }) {
return this.attributes.get(key);
}
/**
* Get all attributes from the player.
* @returns An object containing all player attributes
*/
getAttributes() {
return Object.fromEntries(this.attributes.entries());
}
/**
* Check if the player has an attribute.
* @param key - The key of the attribute to check
* @returns True if the attribute exists, false otherwise
*/
hasAttribute({ key }) {
return this.attributes.has(key);
}
/**
* Disconnect the player from the server.
*/
disconnect() {
if (this.socket.readyState === WebSocket.WebSocket.OPEN) {
this.socket.close();
}
}
}
exports.Player = Player;