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.
Files changed (93) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +348 -0
  3. package/actionengine/3rdparty/goblin/goblin.js +9609 -0
  4. package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
  5. package/actionengine/camera/actioncamera.js +90 -0
  6. package/actionengine/camera/cameracollisionhandler.js +69 -0
  7. package/actionengine/character/actioncharacter.js +360 -0
  8. package/actionengine/character/actioncharacter3D.js +61 -0
  9. package/actionengine/core/app.js +430 -0
  10. package/actionengine/debug/basedebugpanel.js +858 -0
  11. package/actionengine/display/canvasmanager.js +75 -0
  12. package/actionengine/display/gl/programmanager.js +570 -0
  13. package/actionengine/display/gl/shaders/lineshader.js +118 -0
  14. package/actionengine/display/gl/shaders/objectshader.js +1756 -0
  15. package/actionengine/display/gl/shaders/particleshader.js +43 -0
  16. package/actionengine/display/gl/shaders/shadowshader.js +319 -0
  17. package/actionengine/display/gl/shaders/spriteshader.js +100 -0
  18. package/actionengine/display/gl/shaders/watershader.js +67 -0
  19. package/actionengine/display/graphics/actionmodel3D.js +191 -0
  20. package/actionengine/display/graphics/actionsprite3D.js +230 -0
  21. package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
  22. package/actionengine/display/graphics/lighting/actionlight.js +211 -0
  23. package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
  24. package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
  25. package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
  26. package/actionengine/display/graphics/renderableobject.js +44 -0
  27. package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
  28. package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
  29. package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
  30. package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
  31. package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
  32. package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
  33. package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
  34. package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
  35. package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
  36. package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
  37. package/actionengine/display/graphics/texture/texturemanager.js +242 -0
  38. package/actionengine/display/graphics/texture/textureregistry.js +177 -0
  39. package/actionengine/input/actionscrollablearea.js +1405 -0
  40. package/actionengine/input/inputhandler.js +1647 -0
  41. package/actionengine/math/geometry/geometrybuilder.js +161 -0
  42. package/actionengine/math/geometry/glbexporter.js +364 -0
  43. package/actionengine/math/geometry/glbloader.js +722 -0
  44. package/actionengine/math/geometry/modelcodegenerator.js +97 -0
  45. package/actionengine/math/geometry/triangle.js +33 -0
  46. package/actionengine/math/geometry/triangleutils.js +34 -0
  47. package/actionengine/math/mathutils.js +25 -0
  48. package/actionengine/math/matrix4.js +785 -0
  49. package/actionengine/math/physics/actionphysics.js +108 -0
  50. package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
  51. package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
  52. package/actionengine/math/physics/actionraycast.js +129 -0
  53. package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
  54. package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
  55. package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
  56. package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
  57. package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
  58. package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
  59. package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
  60. package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
  61. package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
  62. package/actionengine/math/quaternion.js +61 -0
  63. package/actionengine/math/vector2.js +277 -0
  64. package/actionengine/math/vector3.js +318 -0
  65. package/actionengine/math/viewfrustum.js +136 -0
  66. package/actionengine/network/ACTIONNETREADME.md +810 -0
  67. package/actionengine/network/client/ActionNetManager.js +802 -0
  68. package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
  69. package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
  70. package/actionengine/network/client/SyncSystem.js +422 -0
  71. package/actionengine/network/p2p/ActionNetPeer.js +142 -0
  72. package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
  73. package/actionengine/network/p2p/DataConnection.js +282 -0
  74. package/actionengine/network/p2p/README.md +510 -0
  75. package/actionengine/network/p2p/example.html +502 -0
  76. package/actionengine/network/server/ActionNetServer.js +577 -0
  77. package/actionengine/network/server/ActionNetServerSSL.js +579 -0
  78. package/actionengine/network/server/ActionNetServerUtils.js +458 -0
  79. package/actionengine/network/server/SERVERREADME.md +314 -0
  80. package/actionengine/network/server/package-lock.json +35 -0
  81. package/actionengine/network/server/package.json +13 -0
  82. package/actionengine/network/server/start.bat +27 -0
  83. package/actionengine/network/server/start.sh +25 -0
  84. package/actionengine/network/server/startwss.bat +27 -0
  85. package/actionengine/sound/audiomanager.js +1589 -0
  86. package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
  87. package/actionengine/sound/soundfont/actionparser.js +718 -0
  88. package/actionengine/sound/soundfont/actionreverb.js +252 -0
  89. package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
  90. package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
  91. package/actionengine/sound/soundfont/soundfont.js +2 -0
  92. package/dist/action-engine.min.js +328 -0
  93. 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;