action-engine-js 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +45 -0
- package/README.md +348 -0
- package/actionengine/3rdparty/goblin/goblin.js +9609 -0
- package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
- package/actionengine/camera/actioncamera.js +90 -0
- package/actionengine/camera/cameracollisionhandler.js +69 -0
- package/actionengine/character/actioncharacter.js +360 -0
- package/actionengine/character/actioncharacter3D.js +61 -0
- package/actionengine/core/app.js +430 -0
- package/actionengine/debug/basedebugpanel.js +858 -0
- package/actionengine/display/canvasmanager.js +75 -0
- package/actionengine/display/gl/programmanager.js +570 -0
- package/actionengine/display/gl/shaders/lineshader.js +118 -0
- package/actionengine/display/gl/shaders/objectshader.js +1756 -0
- package/actionengine/display/gl/shaders/particleshader.js +43 -0
- package/actionengine/display/gl/shaders/shadowshader.js +319 -0
- package/actionengine/display/gl/shaders/spriteshader.js +100 -0
- package/actionengine/display/gl/shaders/watershader.js +67 -0
- package/actionengine/display/graphics/actionmodel3D.js +191 -0
- package/actionengine/display/graphics/actionsprite3D.js +230 -0
- package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
- package/actionengine/display/graphics/lighting/actionlight.js +211 -0
- package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
- package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
- package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
- package/actionengine/display/graphics/renderableobject.js +44 -0
- package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
- package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
- package/actionengine/display/graphics/texture/texturemanager.js +242 -0
- package/actionengine/display/graphics/texture/textureregistry.js +177 -0
- package/actionengine/input/actionscrollablearea.js +1405 -0
- package/actionengine/input/inputhandler.js +1647 -0
- package/actionengine/math/geometry/geometrybuilder.js +161 -0
- package/actionengine/math/geometry/glbexporter.js +364 -0
- package/actionengine/math/geometry/glbloader.js +722 -0
- package/actionengine/math/geometry/modelcodegenerator.js +97 -0
- package/actionengine/math/geometry/triangle.js +33 -0
- package/actionengine/math/geometry/triangleutils.js +34 -0
- package/actionengine/math/mathutils.js +25 -0
- package/actionengine/math/matrix4.js +785 -0
- package/actionengine/math/physics/actionphysics.js +108 -0
- package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
- package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
- package/actionengine/math/physics/actionraycast.js +129 -0
- package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
- package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
- package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
- package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
- package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
- package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
- package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
- package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
- package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
- package/actionengine/math/quaternion.js +61 -0
- package/actionengine/math/vector2.js +277 -0
- package/actionengine/math/vector3.js +318 -0
- package/actionengine/math/viewfrustum.js +136 -0
- package/actionengine/network/ACTIONNETREADME.md +810 -0
- package/actionengine/network/client/ActionNetManager.js +802 -0
- package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
- package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
- package/actionengine/network/client/SyncSystem.js +422 -0
- package/actionengine/network/p2p/ActionNetPeer.js +142 -0
- package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
- package/actionengine/network/p2p/DataConnection.js +282 -0
- package/actionengine/network/p2p/README.md +510 -0
- package/actionengine/network/p2p/example.html +502 -0
- package/actionengine/network/server/ActionNetServer.js +577 -0
- package/actionengine/network/server/ActionNetServerSSL.js +579 -0
- package/actionengine/network/server/ActionNetServerUtils.js +458 -0
- package/actionengine/network/server/SERVERREADME.md +314 -0
- package/actionengine/network/server/package-lock.json +35 -0
- package/actionengine/network/server/package.json +13 -0
- package/actionengine/network/server/start.bat +27 -0
- package/actionengine/network/server/start.sh +25 -0
- package/actionengine/network/server/startwss.bat +27 -0
- package/actionengine/sound/audiomanager.js +1589 -0
- package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
- package/actionengine/sound/soundfont/actionparser.js +718 -0
- package/actionengine/sound/soundfont/actionreverb.js +252 -0
- package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
- package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
- package/actionengine/sound/soundfont/soundfont.js +2 -0
- package/dist/action-engine.min.js +328 -0
- package/package.json +35 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActionNetServerUtils - Server-side utilities for ActionNet
|
|
3
|
+
*
|
|
4
|
+
* Provides common patterns for multiplayer servers:
|
|
5
|
+
* - Client tracking
|
|
6
|
+
* - Room/Lobby management
|
|
7
|
+
* - Broadcasting utilities
|
|
8
|
+
* - Connection lifecycle helpers
|
|
9
|
+
*
|
|
10
|
+
* This is NOT a framework - just utilities to make common patterns easier.
|
|
11
|
+
* Developers can use all, some, or none of these helpers.
|
|
12
|
+
*
|
|
13
|
+
* USAGE:
|
|
14
|
+
* ```javascript
|
|
15
|
+
* const WebSocket = require('ws');
|
|
16
|
+
* const ActionNetServerUtils = require('./ActionNetServerUtils');
|
|
17
|
+
*
|
|
18
|
+
* const wss = new WebSocket.Server({ port: 3001 });
|
|
19
|
+
* const utils = new ActionNetServerUtils(wss);
|
|
20
|
+
*
|
|
21
|
+
* wss.on('connection', (ws) => {
|
|
22
|
+
* ws.on('message', (data) => {
|
|
23
|
+
* const msg = JSON.parse(data.toString());
|
|
24
|
+
*
|
|
25
|
+
* if (msg.type === 'connect') {
|
|
26
|
+
* utils.registerClient(ws, msg);
|
|
27
|
+
* }
|
|
28
|
+
* if (msg.type === 'joinRoom') {
|
|
29
|
+
* utils.addToRoom(ws, msg.roomName);
|
|
30
|
+
* utils.broadcastToRoom(msg.roomName, {type: 'userJoined', username: msg.username});
|
|
31
|
+
* }
|
|
32
|
+
* });
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
class ActionNetServerUtils {
|
|
37
|
+
constructor(wss) {
|
|
38
|
+
this.wss = wss;
|
|
39
|
+
|
|
40
|
+
// Client tracking
|
|
41
|
+
this.clients = new Map(); // ws -> client info
|
|
42
|
+
|
|
43
|
+
// Room management
|
|
44
|
+
this.rooms = new Map(); // roomName -> Set of ws clients
|
|
45
|
+
this.roomHosts = new Map(); // roomName -> ws (host client)
|
|
46
|
+
this.lobby = new Set(); // Clients not in any room
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generate a unique display name for a client
|
|
51
|
+
* Checks against all existing display names globally
|
|
52
|
+
*
|
|
53
|
+
* @param {String} username - The requested username
|
|
54
|
+
* @param {String} excludeId - Client ID to exclude from check (for updates)
|
|
55
|
+
* @returns {String} - Unique display name
|
|
56
|
+
*/
|
|
57
|
+
generateUniqueDisplayName(username, excludeId = null) {
|
|
58
|
+
const allDisplayNames = Array.from(this.clients.values())
|
|
59
|
+
.filter(client => client.id !== excludeId)
|
|
60
|
+
.map(client => client.displayName);
|
|
61
|
+
const countMap = {};
|
|
62
|
+
|
|
63
|
+
// Count existing instances of this username (for display name generation)
|
|
64
|
+
const allUsernames = Array.from(this.clients.values())
|
|
65
|
+
.filter(client => client.id !== excludeId)
|
|
66
|
+
.map(client => client.username);
|
|
67
|
+
allUsernames.forEach(name => {
|
|
68
|
+
countMap[name] = (countMap[name] || 0) + 1;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const existingCount = countMap[username] || 0;
|
|
72
|
+
let displayName = existingCount === 0 ? username : `${username} (${existingCount})`;
|
|
73
|
+
|
|
74
|
+
// Ensure the generated display name is unique
|
|
75
|
+
let counter = existingCount;
|
|
76
|
+
while (allDisplayNames.includes(displayName)) {
|
|
77
|
+
counter++;
|
|
78
|
+
displayName = `${username} (${counter})`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return displayName;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Register a client connection
|
|
86
|
+
*
|
|
87
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
88
|
+
* @param {Object} data - Client data (username, clientId, etc.)
|
|
89
|
+
*/
|
|
90
|
+
registerClient(ws, data) {
|
|
91
|
+
// Extract client identity
|
|
92
|
+
const clientId = data.clientId || data.id || `client_${Date.now()}`;
|
|
93
|
+
const username = data.username || data.name || clientId;
|
|
94
|
+
|
|
95
|
+
// Generate unique display name
|
|
96
|
+
const displayName = this.generateUniqueDisplayName(username);
|
|
97
|
+
|
|
98
|
+
// Store client info including display name
|
|
99
|
+
this.clients.set(ws, {
|
|
100
|
+
id: clientId,
|
|
101
|
+
username: username, // Original username
|
|
102
|
+
displayName: displayName, // Unique display name
|
|
103
|
+
roomName: null,
|
|
104
|
+
joinTime: Date.now(),
|
|
105
|
+
metadata: data
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Add to lobby
|
|
109
|
+
this.lobby.add(ws);
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Add client to a room
|
|
116
|
+
*
|
|
117
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
118
|
+
* @param {String} roomName - Room to join
|
|
119
|
+
* @returns {Boolean} - Success
|
|
120
|
+
*/
|
|
121
|
+
addToRoom(ws, roomName) {
|
|
122
|
+
const client = this.clients.get(ws);
|
|
123
|
+
if (!client) {
|
|
124
|
+
console.warn('Attempted to add unregistered client to room');
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Remove from lobby
|
|
129
|
+
this.lobby.delete(ws);
|
|
130
|
+
|
|
131
|
+
// Remove from previous room if exists
|
|
132
|
+
if (client.roomName) {
|
|
133
|
+
this.removeFromRoom(ws);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Create room if doesn't exist
|
|
137
|
+
if (!this.rooms.has(roomName)) {
|
|
138
|
+
this.rooms.set(roomName, new Set());
|
|
139
|
+
// First person to join becomes the host
|
|
140
|
+
this.roomHosts.set(roomName, ws);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add to room
|
|
144
|
+
this.rooms.get(roomName).add(ws);
|
|
145
|
+
client.roomName = roomName;
|
|
146
|
+
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Remove client from their current room
|
|
152
|
+
*
|
|
153
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
154
|
+
* @param {Boolean} returnToLobby - Whether to put client back in lobby
|
|
155
|
+
* @returns {String|null} - Room they were removed from
|
|
156
|
+
*/
|
|
157
|
+
removeFromRoom(ws, returnToLobby = false) {
|
|
158
|
+
const client = this.clients.get(ws);
|
|
159
|
+
if (!client || !client.roomName) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const roomName = client.roomName;
|
|
164
|
+
const room = this.rooms.get(roomName);
|
|
165
|
+
|
|
166
|
+
if (room) {
|
|
167
|
+
room.delete(ws);
|
|
168
|
+
|
|
169
|
+
// Delete empty rooms
|
|
170
|
+
if (room.size === 0) {
|
|
171
|
+
this.rooms.delete(roomName);
|
|
172
|
+
this.roomHosts.delete(roomName);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
client.roomName = null;
|
|
177
|
+
|
|
178
|
+
// Put back in lobby if requested
|
|
179
|
+
if (returnToLobby) {
|
|
180
|
+
this.lobby.add(ws);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return roomName;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Handle client disconnect
|
|
188
|
+
*
|
|
189
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
190
|
+
* @returns {Object|null} - Client info that was removed
|
|
191
|
+
*/
|
|
192
|
+
handleDisconnect(ws) {
|
|
193
|
+
const client = this.clients.get(ws);
|
|
194
|
+
if (!client) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Remove from room
|
|
199
|
+
const roomName = this.removeFromRoom(ws);
|
|
200
|
+
|
|
201
|
+
// Remove from lobby
|
|
202
|
+
this.lobby.delete(ws);
|
|
203
|
+
|
|
204
|
+
// Remove client tracking
|
|
205
|
+
this.clients.delete(ws);
|
|
206
|
+
|
|
207
|
+
return { ...client, roomName };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Send message to a specific client
|
|
212
|
+
*
|
|
213
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
214
|
+
* @param {Object} message - Message to send
|
|
215
|
+
*/
|
|
216
|
+
sendToClient(ws, message) {
|
|
217
|
+
if (ws.readyState === ws.constructor.OPEN) {
|
|
218
|
+
ws.send(JSON.stringify(message));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Broadcast message to all clients in a room
|
|
224
|
+
*
|
|
225
|
+
* @param {String} roomName - Room to broadcast to
|
|
226
|
+
* @param {Object} message - Message to send
|
|
227
|
+
* @param {WebSocket} excludeWs - Client to exclude (optional)
|
|
228
|
+
*/
|
|
229
|
+
broadcastToRoom(roomName, message, excludeWs = null) {
|
|
230
|
+
const room = this.rooms.get(roomName);
|
|
231
|
+
if (!room) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const messageStr = JSON.stringify(message);
|
|
236
|
+
room.forEach(client => {
|
|
237
|
+
if (client !== excludeWs && client.readyState === client.constructor.OPEN) {
|
|
238
|
+
client.send(messageStr);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Broadcast message to all connected clients
|
|
245
|
+
*
|
|
246
|
+
* @param {Object} message - Message to send
|
|
247
|
+
* @param {WebSocket} excludeWs - Client to exclude (optional)
|
|
248
|
+
*/
|
|
249
|
+
broadcastToAll(message, excludeWs = null) {
|
|
250
|
+
const messageStr = JSON.stringify(message);
|
|
251
|
+
this.wss.clients.forEach(client => {
|
|
252
|
+
if (client !== excludeWs && client.readyState === client.constructor.OPEN) {
|
|
253
|
+
client.send(messageStr);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Broadcast message to all clients in lobby
|
|
260
|
+
*
|
|
261
|
+
* @param {Object} message - Message to send
|
|
262
|
+
* @param {WebSocket} excludeWs - Client to exclude (optional)
|
|
263
|
+
*/
|
|
264
|
+
broadcastToLobby(message, excludeWs = null) {
|
|
265
|
+
const messageStr = JSON.stringify(message);
|
|
266
|
+
this.lobby.forEach(client => {
|
|
267
|
+
if (client !== excludeWs && client.readyState === client.constructor.OPEN) {
|
|
268
|
+
client.send(messageStr);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get list of all room names
|
|
275
|
+
*
|
|
276
|
+
* @returns {Array<String>}
|
|
277
|
+
*/
|
|
278
|
+
getRoomList() {
|
|
279
|
+
return Array.from(this.rooms.keys());
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get list of all rooms with player counts
|
|
284
|
+
*
|
|
285
|
+
* @param {Number} maxPlayersPerRoom - Maximum players allowed per room (-1 = no limit)
|
|
286
|
+
* @returns {Array<Object>} - Array of room objects with {name, playerCount, maxPlayers}
|
|
287
|
+
*/
|
|
288
|
+
getRoomListWithCounts(maxPlayersPerRoom = -1) {
|
|
289
|
+
return Array.from(this.rooms.keys()).map(roomName => {
|
|
290
|
+
const playerCount = this.getRoomSize(roomName);
|
|
291
|
+
return {
|
|
292
|
+
name: roomName,
|
|
293
|
+
playerCount: playerCount,
|
|
294
|
+
maxPlayers: maxPlayersPerRoom
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get list of client details in a room (including displayName)
|
|
301
|
+
*
|
|
302
|
+
* @param {String} roomName - Room name
|
|
303
|
+
* @returns {Array<Object>}
|
|
304
|
+
*/
|
|
305
|
+
getClientsInRoom(roomName) {
|
|
306
|
+
const room = this.rooms.get(roomName);
|
|
307
|
+
if (!room) {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const hostWs = this.roomHosts.get(roomName);
|
|
312
|
+
|
|
313
|
+
return Array.from(room).map(ws => {
|
|
314
|
+
const client = this.clients.get(ws);
|
|
315
|
+
return client ? {
|
|
316
|
+
id: client.id,
|
|
317
|
+
username: client.username,
|
|
318
|
+
displayName: client.displayName,
|
|
319
|
+
joinTime: client.joinTime,
|
|
320
|
+
isHost: ws === hostWs
|
|
321
|
+
} : null;
|
|
322
|
+
}).filter(client => client !== null);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get full client info for all clients in a room
|
|
327
|
+
*
|
|
328
|
+
* @param {String} roomName - Room name
|
|
329
|
+
* @returns {Array<Object>}
|
|
330
|
+
*/
|
|
331
|
+
getClientDetailsInRoom(roomName) {
|
|
332
|
+
const room = this.rooms.get(roomName);
|
|
333
|
+
if (!room) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return Array.from(room).map(ws => {
|
|
338
|
+
const client = this.clients.get(ws);
|
|
339
|
+
return client ? {
|
|
340
|
+
id: client.id,
|
|
341
|
+
username: client.username,
|
|
342
|
+
joinTime: client.joinTime
|
|
343
|
+
} : null;
|
|
344
|
+
}).filter(client => client !== null);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get client info for a WebSocket connection
|
|
349
|
+
*
|
|
350
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
351
|
+
* @returns {Object|null}
|
|
352
|
+
*/
|
|
353
|
+
getClient(ws) {
|
|
354
|
+
return this.clients.get(ws) || null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Get room info (name, client count, client IDs)
|
|
359
|
+
*
|
|
360
|
+
* @param {String} roomName - Room name
|
|
361
|
+
* @returns {Object|null}
|
|
362
|
+
*/
|
|
363
|
+
getRoomInfo(roomName) {
|
|
364
|
+
const room = this.rooms.get(roomName);
|
|
365
|
+
if (!room) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
name: roomName,
|
|
371
|
+
clientCount: room.size,
|
|
372
|
+
clients: this.getClientsInRoom(roomName)
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Check if room exists
|
|
378
|
+
*
|
|
379
|
+
* @param {String} roomName - Room name
|
|
380
|
+
* @returns {Boolean}
|
|
381
|
+
*/
|
|
382
|
+
roomExists(roomName) {
|
|
383
|
+
return this.rooms.has(roomName);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Get number of clients in a room
|
|
388
|
+
*
|
|
389
|
+
* @param {String} roomName - Room name
|
|
390
|
+
* @returns {Number}
|
|
391
|
+
*/
|
|
392
|
+
getRoomSize(roomName) {
|
|
393
|
+
const room = this.rooms.get(roomName);
|
|
394
|
+
return room ? room.size : 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Get total number of connected clients
|
|
399
|
+
*
|
|
400
|
+
* @returns {Number}
|
|
401
|
+
*/
|
|
402
|
+
getTotalClients() {
|
|
403
|
+
return this.clients.size;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get number of clients in lobby
|
|
408
|
+
*
|
|
409
|
+
* @returns {Number}
|
|
410
|
+
*/
|
|
411
|
+
getLobbySize() {
|
|
412
|
+
return this.lobby.size;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Get the host (creator) of a room
|
|
417
|
+
*
|
|
418
|
+
* @param {String} roomName - Room name
|
|
419
|
+
* @returns {Object|null} - Host client info or null if room doesn't exist
|
|
420
|
+
*/
|
|
421
|
+
getHostOfRoom(roomName) {
|
|
422
|
+
const hostWs = this.roomHosts.get(roomName);
|
|
423
|
+
if (!hostWs) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const client = this.clients.get(hostWs);
|
|
428
|
+
if (!client) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
id: client.id,
|
|
434
|
+
username: client.username,
|
|
435
|
+
displayName: client.displayName,
|
|
436
|
+
isHost: true
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Check if a client is the host of their room
|
|
442
|
+
*
|
|
443
|
+
* @param {WebSocket} ws - WebSocket connection
|
|
444
|
+
* @returns {Boolean}
|
|
445
|
+
*/
|
|
446
|
+
isHost(ws) {
|
|
447
|
+
const client = this.clients.get(ws);
|
|
448
|
+
if (!client || !client.roomName) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const hostWs = this.roomHosts.get(client.roomName);
|
|
453
|
+
return hostWs === ws;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// CommonJS export for Node.js
|
|
458
|
+
module.exports = ActionNetServerUtils;
|