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,510 @@
1
+ # ActionNetP2P Library
2
+
3
+ A lightweight, zero-dependency WebRTC-based peer-to-peer networking library. Uses WebSocket trackers for peer discovery, built on BitTorrent infrastructure (infohash + tracker protocol).
4
+
5
+ <img width="3002" height="1757" alt="image" src="https://github.com/user-attachments/assets/886ed373-bb7a-482e-b74c-2d8759d6370e" />
6
+
7
+ ## Architecture Overview
8
+
9
+ The library has three main layers:
10
+
11
+ 1. **Tracker Layer** - Peer discovery via WebSocket tracker
12
+ 2. **ActionNetPeer Layer** - WebRTC signaling connections between peers
13
+ 3. **DataConnection Layer** - Application protocol through WebRTC data channels
14
+
15
+ ## Library vs Application
16
+
17
+ **This library's responsibility:**
18
+ - Discover peers via tracker
19
+ - Establish WebRTC ActionNetPeer connections
20
+ - Emit `peer` event with connected ActionNetPeer instance
21
+ - Emit `connection` event with ready-to-use DataConnection instance
22
+
23
+ **Your application's responsibility:**
24
+ - Handle DataConnection connections
25
+ - Implement handshaking (validation/confirmation of peer identity)
26
+ - Send/receive application-specific messages
27
+ - Handle disconnects and reconnects
28
+
29
+ See `example.html` for a complete example of how to build on top of this library.
30
+
31
+ ## Core Components
32
+
33
+ ### ActionNetPeer (ActionNetPeer.js)
34
+
35
+ A thin wrapper around DataConnection that provides compatibility and delegation.
36
+
37
+ **Constructor:**
38
+ ```javascript
39
+ const peer = new ActionNetPeer({
40
+ initiator: true, // Whether this peer creates the offer
41
+ trickle: false, // Wait for complete ICE before signaling (default: true)
42
+ localPeerId: 'peer_xyz', // This peer's ID
43
+ remotePeerId: 'peer_abc', // Remote peer's ID
44
+ iceServers: [{ urls: 'stun:...' }] // STUN/TURN servers
45
+ });
46
+ ```
47
+
48
+ **Key Features:**
49
+ - Wraps an internal DataConnection
50
+ - Initiators create WebRTC offers automatically
51
+ - Responders handle incoming offers via `signal(data)`
52
+ - Exposes `peer.connection` which is the internal DataConnection
53
+ - Delegates all calls to the internal DataConnection
54
+
55
+ **Events:**
56
+ - `signal` - Emit offer/answer with complete SDP (ready to send to remote peer)
57
+ - `connect` - WebRTC connection established (tracker signaling complete)
58
+ - `data` - Received data through DataConnection
59
+ - `close` - Connection closed
60
+ - `error` - Connection error
61
+
62
+ **Methods:**
63
+ - `signal(data)` - Handle remote offer/answer/ICE candidate (delegates to DataConnection)
64
+ - `send(data)` - Send data through DataConnection
65
+ - `destroy()` - Clean up connection
66
+
67
+ ### ActionNetTrackerClient (ActionNetTrackerClient.js)
68
+
69
+ Handles peer discovery through a WebSocket tracker server and manages ActionNetPeer lifecycle.
70
+
71
+ **Constructor:**
72
+ ```javascript
73
+ const tracker = new ActionNetTrackerClient(
74
+ ['wss://tracker.openwebtorrent.com/', 'wss://tracker.btorrent.xyz/'], // URL or array of URLs
75
+ infohash, // SHA-1 hash of application ID
76
+ peerId, // Random peer identifier
77
+ {
78
+ numwant: 50, // Max peers to request
79
+ announceInterval: 5000, // Milliseconds between announces
80
+ iceServers: [...] // Optional STUN/TURN servers
81
+ }
82
+ );
83
+ ```
84
+
85
+ **Flow:**
86
+ 1. `tracker.connect()` opens WebSocket connection
87
+ 2. Creates first ActionNetPeer and waits for ICE gathering to complete (ready state)
88
+ 3. Emits `ready` event when first peer is prepared
89
+ 4. Every 30 seconds: generates new WebRTC offers and announces them
90
+ 5. Each offer has 50-second timeout to receive an answer
91
+ 6. Tracker relays other peers' offers back via `peer-offer` event
92
+ 7. When answers arrive, signals them to stored peers
93
+ 8. Emits `peer` event when WebRTC connection succeeds
94
+ 9. Tracks connected peer IDs to avoid duplicate connections
95
+
96
+ **Events:**
97
+ - `ready` - First peer ready, starts announcing with offers
98
+ - `peer` - Successfully connected to peer: `{ id: peerId, peer: ActionNetPeer instance, source: 'tracker' }`
99
+ - `connection` - DataConnection (application protocol layer) ready: DataConnection instance with `localPeerId` and `remotePeerId` exposed
100
+ - `peer-disconnected` - ActionNetPeer connection closed: `{ id: peerId }`
101
+ - `peer-failed` - ActionNetPeer connection failed: `{ id: peerId, error: Error }`
102
+ - `update` - Tracker sent stats: `{ complete: seeders, incomplete: leechers }`
103
+ - `error` - Connection or protocol error
104
+ - `close` - Disconnected from all trackers
105
+
106
+ **Methods:**
107
+ - `connect()` - Connect to tracker and start announcing
108
+ - `disconnect()` - Stop announcing and close connection
109
+ - `sendAnswer(offerId, peerId, answer)` - Send answer back to tracker for responder flow
110
+ - `on(event, handler)` - Register event listener
111
+
112
+ **Peer Lifecycle:**
113
+ - Library manages all ActionNetPeer creation, connection, and cleanup
114
+ - When ActionNetPeer connects: marked as connected, emits `peer` event
115
+ - When ActionNetPeer disconnects: removed from tracking, emits `peer-disconnected`
116
+ - When ActionNetPeer errors: removed from tracking, emits `peer-failed`
117
+ - Developer should listen for these events and clean up their DataConnection instances
118
+
119
+ ### DataConnection (DataConnection.js)
120
+
121
+ Standalone RTCPeerConnection for tracker-level signaling (offer/answer/ICE exchange).
122
+
123
+ **Purpose:**
124
+ Manages the RTCPeerConnection for tracker discovery and signaling. Receives offer/answer/ICE via `signal()` method. Used internally by ActionNetPeer and emitted by ActionNetTrackerClient.
125
+
126
+ **Constructor:**
127
+ ```javascript
128
+ const connection = new DataConnection({
129
+ localPeerId: 'peer_xyz', // This peer's ID
130
+ remotePeerId: 'peer_abc', // Remote peer's ID
131
+ initiator: true, // Whether this peer creates the offer
132
+ iceServers: [...] // Optional STUN/TURN servers
133
+ });
134
+ ```
135
+
136
+ **Negotiation Flow:**
137
+ 1. Initiator (determined by constructor option or peer ID comparison) creates offer
138
+ 2. Offer/answer/ICE candidates exchanged via `signal()` method
139
+ 3. Emits `signal` events with offer/answer/ICE to be relayed (e.g., through tracker)
140
+ 4. When ICE connection completes, emits `connect` event
141
+
142
+ **Events:**
143
+ - `signal` - Emit offer/answer/ICE for relay to remote peer (e.g., via tracker)
144
+ - `connect` - Tracker-level RTCPeerConnection established
145
+ - `close` - Connection closed
146
+ - `error` - Connection error
147
+
148
+ **Methods:**
149
+ - `signal(data)` - Receive offer/answer/ICE from remote peer
150
+ - `{ type: 'offer', sdp: '...' }` - WebRTC offer
151
+ - `{ type: 'answer', sdp: '...' }` - WebRTC answer
152
+ - `{ candidate: {...} }` - ICE candidate
153
+ - `send(message)` - Send JSON object through data channel (auto-stringified, returns boolean)
154
+ - `close()` - Clean up connection
155
+ - `on(event, handler)` - Register event listener
156
+
157
+ **Events:**
158
+ - `signal` - Emit offer/answer/ICE for relay to remote peer
159
+ - `connect` - Data channel established
160
+ - `data` - Received message on data channel (already parsed JSON)
161
+ - `close` - Connection closed
162
+ - `error` - Connection error
163
+
164
+ **Data Channel Messages:**
165
+
166
+ DataConnection automatically serializes/deserializes JSON. Send objects, receive parsed objects:
167
+
168
+ ```javascript
169
+ // Send (automatically JSON.stringify'd)
170
+ connection.send({
171
+ type: 'greeting',
172
+ message: 'Hello from peer',
173
+ timestamp: Date.now()
174
+ });
175
+
176
+ // Receive (automatically JSON.parse'd)
177
+ connection.on('data', (message) => {
178
+ console.log(message.type); // 'greeting'
179
+ console.log(message.message); // 'Hello from peer'
180
+ });
181
+ ```
182
+
183
+ **Handshake Pattern (Recommended):**
184
+
185
+ Peers should exchange handshakes immediately upon connection to validate compatibility and ensure you're only communicating with peers running compatible versions of your application. Peers that fail validation should be rejected:
186
+
187
+ ```javascript
188
+ connection.on('connect', () => {
189
+ // Send handshake for confirmation
190
+ connection.send({
191
+ type: 'handshake',
192
+ peerId: localPeerId,
193
+ version: '1.0',
194
+ metadata: { /* your app-specific data */ }
195
+ });
196
+ });
197
+
198
+ connection.on('data', (message) => {
199
+ if (message.type === 'handshake') {
200
+ // Validate: is peer compatible with our application?
201
+ if (message.version !== '1.0') {
202
+ // Reject this peer, close connection
203
+ connection.close();
204
+ return;
205
+ }
206
+ // Confirm: peer is valid, proceed with communication
207
+ registerConfirmedPeer(message.peerId, message.metadata);
208
+ } else {
209
+ // Handle other application messages
210
+ }
211
+ });
212
+ ```
213
+
214
+ **Note:** DataConnection handles both tracker signaling (offer/answer/ICE via `signal()`) and application communication (messages via `send()`/`on('data')`). This provides a unified communication channel that works for both peer discovery and application-level messaging.
215
+
216
+ ## Message Protocol
217
+
218
+ ### Tracker Announce (JSON via WebSocket)
219
+
220
+ **Client announces to tracker:**
221
+ ```javascript
222
+ {
223
+ action: "announce",
224
+ info_hash: "sha1_of_application_id",
225
+ peer_id: "peer_xyz",
226
+ port: 6881,
227
+ numwant: 50,
228
+ offers: [
229
+ {
230
+ offer_id: "random_20_byte_hex",
231
+ offer: { type: "offer", sdp: "..." }
232
+ }
233
+ ]
234
+ }
235
+ ```
236
+
237
+ **Tracker responds with stats:**
238
+ ```javascript
239
+ {
240
+ action: "announce",
241
+ interval: 120,
242
+ complete: 0,
243
+ incomplete: 2
244
+ }
245
+ ```
246
+
247
+ **Tracker relays peer offer:**
248
+ ```javascript
249
+ {
250
+ action: "announce",
251
+ offer_id: "...",
252
+ peer_id: "peer_abc",
253
+ offer: { type: "offer", sdp: "..." }
254
+ }
255
+ ```
256
+
257
+ **Tracker relays peer answer:**
258
+ ```javascript
259
+ {
260
+ action: "announce",
261
+ offer_id: "...",
262
+ peer_id: "peer_abc",
263
+ answer: { type: "answer", sdp: "..." }
264
+ }
265
+ ```
266
+
267
+ ## Usage Example
268
+
269
+ ```javascript
270
+ // Create tracker client with multiple trackers for redundancy
271
+ const tracker = new ActionNetTrackerClient(
272
+ ['wss://tracker.openwebtorrent.com/', 'wss://tracker.btorrent.xyz/'],
273
+ infohash,
274
+ peerId,
275
+ { numwant: 50 }
276
+ );
277
+
278
+ // Tracker is ready (first peer prepared, will start announcing)
279
+ tracker.on('ready', () => {
280
+ console.log('Tracker ready, discovering peers...');
281
+ });
282
+
283
+ // DataConnection created and ready (TrackerClient creates it automatically)
284
+ tracker.on('connection', (connection) => {
285
+ const peerId = connection.remotePeerId;
286
+
287
+ connection.on('connect', () => {
288
+ // Send handshake to confirm peer
289
+ connection.send({
290
+ type: 'handshake',
291
+ peerId: myPeerId,
292
+ version: '1.0'
293
+ });
294
+ });
295
+
296
+ connection.on('data', (message) => {
297
+ console.log('Received from', peerId, ':', message);
298
+ if (message.type === 'handshake') {
299
+ // Validate and confirm peer
300
+ } else {
301
+ // Handle application messages
302
+ }
303
+ });
304
+
305
+ connection.on('close', () => {
306
+ console.log('DataConnection closed with peer:', peerId);
307
+ });
308
+
309
+ connection.on('error', (err) => {
310
+ console.error('DataConnection error with peer:', peerId, err);
311
+ });
312
+ });
313
+
314
+ // Peer disconnected or failed
315
+ tracker.on('peer-disconnected', (data) => {
316
+ console.log('Peer disconnected:', data.id);
317
+ // Clean up any DataConnection associated with this peer
318
+ });
319
+
320
+ tracker.on('peer-failed', (data) => {
321
+ console.log('Peer connection failed:', data.id, data.error.message);
322
+ // Clean up any DataConnection associated with this peer
323
+ });
324
+
325
+ // Connect to tracker (TrackerClient handles all peer offer/answer negotiation internally)
326
+ await tracker.connect();
327
+ ```
328
+
329
+ ## Connection Lifecycle
330
+
331
+ ```
332
+ Initiator (Tab A) Tracker Responder (Tab B)
333
+ | | |
334
+ | 1. Connect to tracker | |
335
+ |--announce (with offers)--------> | |
336
+ | |--announce-----------------> |
337
+ | | 2. Relay offer to peers |
338
+ | | 3. Create responder Peer |
339
+ | | 4. Handle offer |
340
+ | | 5. Generate answer |
341
+ | | <------------------answer--|
342
+ | 6. Receive answer | |
343
+ | 7. Signal answer to Peer | |
344
+ | | |
345
+ |===== Peer WebRTC Connected ======| |
346
+ | | |
347
+ | 8. Create DataConnection | |
348
+ | <---------------- DataConnection negotiation ----------------> |
349
+ | | |
350
+ |====== DataConnection Ready ======| |
351
+ | | |
352
+ | 9. Send application message | |
353
+ |--application-data-message------> | |
354
+ | | 10. Receive message |
355
+ ```
356
+
357
+ ## Connection Model
358
+
359
+ The library handles peer discovery and provides a ready-to-use communication channel:
360
+
361
+ 1. **Peer Discovery (ActionNetTrackerClient)**:
362
+ - Announces offers to tracker
363
+ - Tracker relays offers to other peers
364
+ - Manages peer lifecycle (connect, disconnect, failures)
365
+
366
+ 2. **Peer-to-Peer Communication (DataConnection)**:
367
+ - Each peer gets a DataConnection with its own RTCPeerConnection
368
+ - Handles offer/answer/ICE signaling automatically
369
+ - Provides a data channel for sending/receiving application messages
370
+ - Application uses `connection.send()` and `connection.on('data')`
371
+
372
+ **Why this design?** The library handles all the complexity of peer discovery and WebRTC negotiation. Your application just listens for connections and sends/receives messages. No need to manage offers/answers yourself.
373
+
374
+ ## Key Design Decisions
375
+
376
+ **One RTCPeerConnection Per Peer:**
377
+ - DataConnection wraps a single RTCPeerConnection for all peer-to-peer communication
378
+ - Handles both tracker signaling and application messaging through the same connection
379
+
380
+ **Deterministic Initiator Selection:**
381
+ - DataConnection initiator selected based on peer ID comparison (lexicographically highest initiates)
382
+ - Guarantees both sides agree on who initiates, preventing simultaneous offers
383
+
384
+ **Complete ICE Gathering:**
385
+ - DataConnection waits for ICE gathering to complete before emitting offer/answer
386
+ - Ensures SDP includes all ICE candidates
387
+ - Prevents trickle ICE complexity at tracker level
388
+
389
+ **Ordered Messages:**
390
+ - Data channels created with `ordered: true`
391
+ - Guarantees in-order message delivery
392
+
393
+ **Zero Dependencies:**
394
+ - Native WebRTC APIs only
395
+ - No external libraries required
396
+ - Works in any modern browser
397
+
398
+ ## Configuration
399
+
400
+ **TrackerClient Options:**
401
+ - `port` - Local port number (default: 6881)
402
+ - `numwant` - Maximum peers to request from tracker (default: 50)
403
+ - `announceInterval` - Time between periodic announces in milliseconds (default: 30000)
404
+ - `iceServers` - Array of STUN/TURN server configurations
405
+
406
+ **ActionNetPeer Options:**
407
+ - `initiator` - Whether this peer creates the offer (default: false)
408
+ - `trickle` - Use trickle ICE (default: true)
409
+ - `iceServers` - Array of STUN/TURN server configurations
410
+
411
+ **ActionNetTrackerClient:**
412
+ - `debug` - Enable logging for debugging (default: false)
413
+ - `numwant` - Maximum peers to request from tracker (default: 50)
414
+ - `announceInterval` - Initial time between announces in milliseconds (default: 5000)
415
+ - `maxAnnounceInterval` - Maximum interval cap in milliseconds (default: 120000)
416
+ - `backoffMultiplier` - Multiply interval by this after each announce (default: 1.1)
417
+ - `iceServers` - Array of STUN/TURN server configurations
418
+
419
+ **Announce Backoff Strategy:**
420
+
421
+ After each announce, the interval is multiplied by `backoffMultiplier` until reaching `maxAnnounceInterval`. With default settings (`announceInterval: 5000`, `backoffMultiplier: 1.1`, `maxAnnounceInterval: 120000`):
422
+
423
+ - 5s → 5.5s → 6s → 6.6s → 7.3s → 8s → 8.8s → 9.7s → 10.7s → 11.7s → 12.9s → 14.2s → 15.6s → 17.2s → 18.9s → 20.8s → 22.9s → 25.2s → 27.7s → 30.5s ... → 120s (capped after ~36 announces)
424
+
425
+ Higher multipliers (e.g., 1.5) reach the cap faster: 5s → 7.5s → 11s → 17s → 26s → 39s → 59s → 88s → 120s (capped).
426
+
427
+ **DataConnection:**
428
+ - No configuration; automatically uses STUN servers for ICE
429
+
430
+ **Infohash Generation:**
431
+
432
+ The infohash is derived from the application ID using a cryptographic hash. The example uses SHA-1, but SHA-256 or any consistent hash works. **Important:** All peers must use the same hash function to generate the same infohash for discovery.
433
+
434
+ ```javascript
435
+ // SHA-1 (example.html approach)
436
+ const hashBuffer = await crypto.subtle.digest('SHA-1', applicationIdBytes);
437
+ ```
438
+
439
+ ## NAT Traversal: STUN and TURN
440
+
441
+ WebRTC uses ICE (Interactive Connectivity Establishment) to find the best network path between peers. This requires STUN and optionally TURN servers.
442
+
443
+ **STUN (Session Traversal Utilities for NAT):**
444
+ - Helps peers discover their public IP address
445
+ - Works for most NAT configurations (90%+ of cases)
446
+ - Free, no auth required
447
+ - Default: Multiple Google STUN servers for redundancy
448
+
449
+ **TURN (Traversal Using Relays around NAT):**
450
+ - Relays traffic when peers can't reach each other directly
451
+ - Needed for restrictive firewalls, double NAT, mobile networks
452
+ - Requires credentials and infrastructure (paid or self-hosted)
453
+ - Only use if STUN fails
454
+
455
+ **Default Configuration (STUN only):**
456
+ ```javascript
457
+ iceServers: [
458
+ { urls: 'stun:stun.l.google.com:19302' },
459
+ { urls: 'stun:stun1.l.google.com:19302' },
460
+ { urls: 'stun:stun2.l.google.com:19302' }
461
+ ]
462
+ ```
463
+
464
+ **Production Configuration (STUN + TURN):**
465
+ ```javascript
466
+ iceServers: [
467
+ // STUN servers (free)
468
+ { urls: 'stun:stun.l.google.com:19302' },
469
+ { urls: 'stun:stun1.l.google.com:19302' },
470
+
471
+ // TURN server (your infrastructure or paid service)
472
+ {
473
+ urls: 'turn:your-turn-server.com',
474
+ username: 'user',
475
+ credential: 'password'
476
+ }
477
+ ]
478
+ ```
479
+
480
+ **Passing Custom ICE Servers:**
481
+ ```javascript
482
+ const tracker = new ActionNetTrackerClient(trackerUrls, infohash, peerId, {
483
+ iceServers: [
484
+ { urls: 'stun:your-stun.example.com:3478' },
485
+ { urls: 'turn:your-turn.example.com:3478', username: 'user', credential: 'pass' }
486
+ ]
487
+ });
488
+ ```
489
+
490
+ ## Testing
491
+
492
+ Open `example.html` in two browser tabs with the same application ID (or create a test app):
493
+
494
+ 1. Both peers connect to tracker
495
+ 2. First peer announces an offer
496
+ 3. Second peer receives offer, creates responder Peer connection
497
+ 4. First peer receives answer, completes WebRTC signaling connection
498
+ 5. Both create DataConnection connections automatically for application data
499
+ 6. Peers exchange handshakes to confirm each other
500
+ 7. Exchange application messages through DataConnection data channels
501
+
502
+ Watch the stats and confirmed peers list update as connections establish.
503
+
504
+ ## Limitations
505
+
506
+ **Current Limitations:**
507
+ 1. Requires public WebSocket tracker (no DHT)
508
+ 2. Peer connections not persisted across disconnects (rejoin requires new TrackerClient)
509
+ 3. No connection pooling (single Peer per remote peer)
510
+ 4. No message fragmentation for large payloads