UNPKG

swordfight-engine

Version:

A multiplayer sword fighting game engine with character management, round-based combat, and real-time multiplayer support

284 lines (281 loc) 7.88 kB
/** * swordfight-engine v1.6.21 * @license MIT */ // src/classes/transports/MultiplayerTransport.js var MultiplayerTransport = class _MultiplayerTransport { constructor(game) { this.game = game; this.started = false; if (new.target === _MultiplayerTransport) { throw new Error("MultiplayerTransport is an abstract class and cannot be instantiated directly"); } } /** * Connect to a room/session * @param {string} _roomId - The room identifier * @returns {Promise<void>} */ async connect(_roomId) { throw new Error("connect() must be implemented by subclass"); } /** * Send a move to the opponent * @param {Object} _data - Move data { move: Object, round: number } */ sendMove(_data) { throw new Error("sendMove() must be implemented by subclass"); } /** * Register callback for receiving opponent's move * @param {Function} _callback - Callback function to handle received move */ getMove(_callback) { throw new Error("getMove() must be implemented by subclass"); } /** * Send player name to opponent * @param {Object} _data - Name data { name: string } */ sendName(_data) { throw new Error("sendName() must be implemented by subclass"); } /** * Register callback for receiving opponent's name * @param {Function} _callback - Callback function to handle received name */ getName(_callback) { throw new Error("getName() must be implemented by subclass"); } /** * Send character slug to opponent * @param {Object} _data - Character data { characterSlug: string } */ sendCharacter(_data) { throw new Error("sendCharacter() must be implemented by subclass"); } /** * Register callback for receiving opponent's character slug * @param {Function} _callback - Callback function to handle received character slug */ getCharacter(_callback) { throw new Error("getCharacter() must be implemented by subclass"); } /** * Disconnect from the session */ disconnect() { throw new Error("disconnect() must be implemented by subclass"); } /** * Get the number of connected peers * @returns {number} */ getPeerCount() { throw new Error("getPeerCount() must be implemented by subclass"); } /** * Check if room is full * @returns {boolean} */ isRoomFull() { return this.getPeerCount() >= 2; } }; // src/classes/transports/WebSocketTransport.js var WebSocketTransport = class extends MultiplayerTransport { constructor(game, options = {}) { super(game); this.serverUrl = options.serverUrl || "ws://localhost:8080"; this.ws = null; this.moveCallbacks = []; this.nameCallbacks = []; this.characterCallbacks = []; this.roomId = null; } /** * Connect to a WebSocket server and join a room * @param {string} roomId - The room identifier */ async connect(roomId) { this.roomId = roomId; return new Promise((resolve, reject) => { console.log("Connecting to WebSocket server:", this.serverUrl); this.ws = new WebSocket(this.serverUrl); this.ws.onopen = () => { console.log("Connected to WebSocket server"); this.ws.send(JSON.stringify({ type: "join", roomId })); }; this.ws.onmessage = (event) => { const message = JSON.parse(event.data); this._handleMessage(message); }; this.ws.onerror = (error) => { console.error("WebSocket error:", error); reject(error); }; this.ws.onclose = () => { console.log("WebSocket connection closed"); this.started = false; }; const joinListener = (event) => { const message = JSON.parse(event.data); if (message.type === "joined") { resolve(); this.ws.removeEventListener("message", joinListener); } else if (message.type === "error") { reject(new Error(message.message)); } }; this.ws.addEventListener("message", joinListener); }); } /** * Handle incoming WebSocket messages * @private */ _handleMessage(message) { switch (message.type) { case "joined": console.log("Successfully joined room:", message.roomId); break; case "peer-joined": { console.log("Peer joined the room"); this.started = true; const playerName = this._getPlayerName(); this.game.myCharacter.name = playerName; this.sendName({ name: playerName, characterSlug: this.game.myCharacterSlug }); if (typeof document !== "undefined") { const startEvent = new CustomEvent("start", { detail: { game: this.game } }); document.dispatchEvent(startEvent); } break; } case "move": this.moveCallbacks.forEach((callback) => callback(message.data)); break; case "name": this.nameCallbacks.forEach((callback) => callback(message.data)); break; case "character": this.characterCallbacks.forEach((callback) => callback(message.data)); break; case "room-full": console.error("Room is full"); if (typeof document !== "undefined") { const roomFullEvent = new CustomEvent("roomFull"); document.dispatchEvent(roomFullEvent); } break; case "peer-left": console.log("Peer left the room"); this.started = false; break; default: console.warn("Unknown message type:", message.type); } } /** * Get player name from storage or character * @private */ _getPlayerName() { if (typeof localStorage !== "undefined" && localStorage.getItem("playerName")) { return localStorage.getItem("playerName"); } return this.game.myCharacter.name; } /** * Send a move to the opponent via WebSocket * @param {Object} data - Move data */ sendMove(data) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify({ type: "move", roomId: this.roomId, data })); } } /** * Send player name to opponent via WebSocket * @param {Object} data - Name data */ sendName(data) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify({ type: "name", roomId: this.roomId, data })); } } /** * Register callback for receiving opponent's move * @param {Function} callback - Callback function */ getMove(callback) { this.moveCallbacks.push(callback); } /** * Register callback for receiving opponent's name * @param {Function} callback - Callback function */ getName(callback) { this.nameCallbacks.push(callback); } /** * Send character slug to opponent via WebSocket * @param {Object} data - Character data { characterSlug: string } */ sendCharacter(data) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify({ type: "character", roomId: this.roomId, data })); } } /** * Register callback for receiving opponent's character slug * @param {Function} callback - Callback function */ getCharacter(callback) { this.characterCallbacks.push(callback); } /** * Get the number of connected peers * @returns {number} */ getPeerCount() { return this.started ? 1 : 0; } /** * Disconnect from the WebSocket */ disconnect() { if (this.ws) { this.ws.send(JSON.stringify({ type: "leave", roomId: this.roomId })); this.ws.close(); this.ws = null; } this.started = false; this.moveCallbacks = []; this.nameCallbacks = []; this.characterCallbacks = []; } }; export { WebSocketTransport };