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
JavaScript
/**
* 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
};