@needle-tools/engine 5.1.0-canary.fbdfce3 → 5.1.0-experimental.03e8105

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 (311) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/SKILL.md +4 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{needle-engine.bundle-BFSj2Fz8.js → needle-engine.bundle-BNqUjnSQ.js} +19180 -18386
  5. package/dist/needle-engine.bundle-Bt8ULD7E.umd.cjs +1733 -0
  6. package/dist/needle-engine.bundle-DF6ovbwD.min.js +1733 -0
  7. package/dist/needle-engine.d.ts +1487 -356
  8. package/dist/needle-engine.js +544 -542
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/dist/three.js +1 -0
  12. package/dist/three.min.js +21 -21
  13. package/dist/three.umd.cjs +16 -16
  14. package/lib/engine/api.d.ts +8 -1
  15. package/lib/engine/api.js +7 -1
  16. package/lib/engine/api.js.map +1 -1
  17. package/lib/engine/codegen/register_types.js +10 -18
  18. package/lib/engine/codegen/register_types.js.map +1 -1
  19. package/lib/engine/engine_audio.d.ts +68 -0
  20. package/lib/engine/engine_audio.js +172 -0
  21. package/lib/engine/engine_audio.js.map +1 -1
  22. package/lib/engine/engine_camera.fit.js +16 -4
  23. package/lib/engine/engine_camera.fit.js.map +1 -1
  24. package/lib/engine/engine_components.js +1 -1
  25. package/lib/engine/engine_components.js.map +1 -1
  26. package/lib/engine/engine_context.d.ts +21 -8
  27. package/lib/engine/engine_context.js +32 -16
  28. package/lib/engine/engine_context.js.map +1 -1
  29. package/lib/engine/engine_context_eventbus.d.ts +47 -0
  30. package/lib/engine/engine_context_eventbus.js +47 -0
  31. package/lib/engine/engine_context_eventbus.js.map +1 -0
  32. package/lib/engine/engine_disposable.d.ts +172 -0
  33. package/lib/engine/engine_disposable.js +136 -0
  34. package/lib/engine/engine_disposable.js.map +1 -0
  35. package/lib/engine/engine_gameobject.d.ts +1 -10
  36. package/lib/engine/engine_gameobject.js +22 -120
  37. package/lib/engine/engine_gameobject.js.map +1 -1
  38. package/lib/engine/engine_gltf_builtin_components.js +7 -69
  39. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  40. package/lib/engine/engine_init.js +7 -7
  41. package/lib/engine/engine_init.js.map +1 -1
  42. package/lib/engine/engine_input.d.ts +24 -5
  43. package/lib/engine/engine_input.js +3 -2
  44. package/lib/engine/engine_input.js.map +1 -1
  45. package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
  46. package/lib/engine/engine_instantiate_resolve.js +372 -0
  47. package/lib/engine/engine_instantiate_resolve.js.map +1 -0
  48. package/lib/engine/engine_license.d.ts +7 -7
  49. package/lib/engine/engine_license.js +183 -57
  50. package/lib/engine/engine_license.js.map +1 -1
  51. package/lib/engine/engine_mainloop_utils.js +7 -4
  52. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  53. package/lib/engine/engine_networking.d.ts +51 -37
  54. package/lib/engine/engine_networking.js +132 -82
  55. package/lib/engine/engine_networking.js.map +1 -1
  56. package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
  57. package/lib/engine/engine_networking.transport.websocket.js +38 -0
  58. package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
  59. package/lib/engine/engine_networking_blob.js +4 -4
  60. package/lib/engine/engine_networking_blob.js.map +1 -1
  61. package/lib/engine/engine_networking_instantiate.js +2 -2
  62. package/lib/engine/engine_networking_instantiate.js.map +1 -1
  63. package/lib/engine/engine_networking_types.d.ts +39 -1
  64. package/lib/engine/engine_networking_types.js +7 -0
  65. package/lib/engine/engine_networking_types.js.map +1 -1
  66. package/lib/engine/engine_physics_rapier.d.ts +21 -3
  67. package/lib/engine/engine_physics_rapier.js +94 -25
  68. package/lib/engine/engine_physics_rapier.js.map +1 -1
  69. package/lib/engine/engine_scenedata.js +2 -2
  70. package/lib/engine/engine_scenedata.js.map +1 -1
  71. package/lib/engine/engine_serialization_builtin_serializer.js +28 -5
  72. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  73. package/lib/engine/engine_serialization_core.d.ts +1 -0
  74. package/lib/engine/engine_serialization_core.js +7 -0
  75. package/lib/engine/engine_serialization_core.js.map +1 -1
  76. package/lib/engine/engine_types.d.ts +29 -11
  77. package/lib/engine/engine_types.js +1 -1
  78. package/lib/engine/engine_types.js.map +1 -1
  79. package/lib/engine/engine_util_decorator.js +7 -2
  80. package/lib/engine/engine_util_decorator.js.map +1 -1
  81. package/lib/engine/engine_utils.d.ts +1 -1
  82. package/lib/engine/engine_utils.js +19 -5
  83. package/lib/engine/engine_utils.js.map +1 -1
  84. package/lib/engine/engine_utils_qrcode.js +2 -2
  85. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  86. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  87. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  88. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  89. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  90. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -1
  91. package/lib/engine/webcomponents/needle menu/needle-menu.js +6 -6
  92. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  93. package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
  94. package/lib/engine/webcomponents/needle-engine.js +3 -3
  95. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  96. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  97. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  98. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  99. package/lib/engine/xr/NeedleXRSession.js +50 -14
  100. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  101. package/lib/engine/xr/TempXRContext.js +2 -2
  102. package/lib/engine/xr/TempXRContext.js.map +1 -1
  103. package/lib/engine/xr/events.d.ts +1 -1
  104. package/lib/engine/xr/events.js.map +1 -1
  105. package/lib/engine-components/Animation.js +17 -16
  106. package/lib/engine-components/Animation.js.map +1 -1
  107. package/lib/engine-components/AnimationBuilder.d.ts +158 -0
  108. package/lib/engine-components/AnimationBuilder.js +305 -0
  109. package/lib/engine-components/AnimationBuilder.js.map +1 -0
  110. package/lib/engine-components/Animator.d.ts +6 -0
  111. package/lib/engine-components/Animator.js +23 -13
  112. package/lib/engine-components/Animator.js.map +1 -1
  113. package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
  114. package/lib/engine-components/AnimatorController.builder.js +263 -0
  115. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  116. package/lib/engine-components/AnimatorController.d.ts +2 -119
  117. package/lib/engine-components/AnimatorController.js +33 -232
  118. package/lib/engine-components/AnimatorController.js.map +1 -1
  119. package/lib/engine-components/AudioSource.d.ts +19 -3
  120. package/lib/engine-components/AudioSource.js +121 -68
  121. package/lib/engine-components/AudioSource.js.map +1 -1
  122. package/lib/engine-components/Collider.d.ts +18 -9
  123. package/lib/engine-components/Collider.js +61 -14
  124. package/lib/engine-components/Collider.js.map +1 -1
  125. package/lib/engine-components/Component.d.ts +72 -9
  126. package/lib/engine-components/Component.js +114 -10
  127. package/lib/engine-components/Component.js.map +1 -1
  128. package/lib/engine-components/ContactShadows.d.ts +1 -0
  129. package/lib/engine-components/ContactShadows.js +14 -1
  130. package/lib/engine-components/ContactShadows.js.map +1 -1
  131. package/lib/engine-components/DragControls.d.ts +7 -0
  132. package/lib/engine-components/DragControls.js +19 -7
  133. package/lib/engine-components/DragControls.js.map +1 -1
  134. package/lib/engine-components/DropListener.js +3 -0
  135. package/lib/engine-components/DropListener.js.map +1 -1
  136. package/lib/engine-components/EventList.d.ts +31 -9
  137. package/lib/engine-components/EventList.js +37 -76
  138. package/lib/engine-components/EventList.js.map +1 -1
  139. package/lib/engine-components/Joints.d.ts +4 -2
  140. package/lib/engine-components/Joints.js +19 -3
  141. package/lib/engine-components/Joints.js.map +1 -1
  142. package/lib/engine-components/Light.js +9 -1
  143. package/lib/engine-components/Light.js.map +1 -1
  144. package/lib/engine-components/Networking.d.ts +1 -1
  145. package/lib/engine-components/Networking.js +1 -1
  146. package/lib/engine-components/OrbitControls.d.ts +0 -2
  147. package/lib/engine-components/OrbitControls.js +30 -12
  148. package/lib/engine-components/OrbitControls.js.map +1 -1
  149. package/lib/engine-components/RigidBody.d.ts +12 -4
  150. package/lib/engine-components/RigidBody.js +18 -4
  151. package/lib/engine-components/RigidBody.js.map +1 -1
  152. package/lib/engine-components/SceneSwitcher.js +3 -0
  153. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  154. package/lib/engine-components/SeeThrough.js +2 -2
  155. package/lib/engine-components/SeeThrough.js.map +1 -1
  156. package/lib/engine-components/api.d.ts +2 -1
  157. package/lib/engine-components/api.js +2 -1
  158. package/lib/engine-components/api.js.map +1 -1
  159. package/lib/engine-components/codegen/components.d.ts +7 -13
  160. package/lib/engine-components/codegen/components.js +7 -13
  161. package/lib/engine-components/codegen/components.js.map +1 -1
  162. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  163. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  164. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  165. package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
  166. package/lib/engine-components/timeline/PlayableDirector.js +75 -67
  167. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  168. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  169. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  170. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  171. package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
  172. package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
  173. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  174. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  175. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  176. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  177. package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
  178. package/lib/engine-components/timeline/TimelineTracks.js +92 -26
  179. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  180. package/lib/engine-components/timeline/index.d.ts +2 -1
  181. package/lib/engine-components/timeline/index.js +2 -0
  182. package/lib/engine-components/timeline/index.js.map +1 -1
  183. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  184. package/lib/engine-components/ui/Canvas.js +2 -8
  185. package/lib/engine-components/ui/Canvas.js.map +1 -1
  186. package/lib/engine-components/ui/Text.d.ts +1 -0
  187. package/lib/engine-components/ui/Text.js +10 -7
  188. package/lib/engine-components/ui/Text.js.map +1 -1
  189. package/lib/engine-components/web/CursorFollow.d.ts +0 -1
  190. package/lib/engine-components/web/CursorFollow.js +21 -13
  191. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  192. package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
  193. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  194. package/package.json +2 -83
  195. package/plugins/common/cloud.js +6 -1
  196. package/plugins/common/license.js +31 -10
  197. package/plugins/common/worker.js +9 -4
  198. package/plugins/vite/asap.js +17 -8
  199. package/plugins/vite/dependencies.js +29 -10
  200. package/plugins/vite/dependency-watcher.js +2 -2
  201. package/plugins/vite/editor-connection.js +3 -3
  202. package/plugins/vite/license.js +46 -7
  203. package/plugins/vite/local-files-core.js +3 -3
  204. package/plugins/vite/local-files-utils.d.ts +3 -1
  205. package/plugins/vite/local-files-utils.js +29 -5
  206. package/plugins/vite/reload.js +1 -1
  207. package/plugins/vite/server.js +2 -1
  208. package/src/engine/api.ts +11 -1
  209. package/src/engine/codegen/register_types.ts +10 -18
  210. package/src/engine/engine_audio.ts +184 -0
  211. package/src/engine/engine_camera.fit.ts +15 -4
  212. package/src/engine/engine_components.ts +1 -1
  213. package/src/engine/engine_context.ts +34 -18
  214. package/src/engine/engine_context_eventbus.ts +73 -0
  215. package/src/engine/engine_disposable.ts +214 -0
  216. package/src/engine/engine_gameobject.ts +54 -159
  217. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  218. package/src/engine/engine_init.ts +7 -7
  219. package/src/engine/engine_input.ts +28 -7
  220. package/src/engine/engine_instantiate_resolve.ts +407 -0
  221. package/src/engine/engine_license.ts +197 -55
  222. package/src/engine/engine_mainloop_utils.ts +7 -4
  223. package/src/engine/engine_networking.transport.websocket.ts +45 -0
  224. package/src/engine/engine_networking.ts +161 -137
  225. package/src/engine/engine_networking_blob.ts +4 -4
  226. package/src/engine/engine_networking_instantiate.ts +2 -2
  227. package/src/engine/engine_networking_types.ts +41 -1
  228. package/src/engine/engine_physics_rapier.ts +102 -33
  229. package/src/engine/engine_scenedata.ts +3 -3
  230. package/src/engine/engine_serialization_builtin_serializer.ts +32 -9
  231. package/src/engine/engine_serialization_core.ts +9 -0
  232. package/src/engine/engine_types.ts +46 -27
  233. package/src/engine/engine_util_decorator.ts +7 -2
  234. package/src/engine/engine_utils.ts +16 -5
  235. package/src/engine/engine_utils_qrcode.ts +2 -2
  236. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  237. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  238. package/src/engine/webcomponents/needle menu/needle-menu.ts +6 -6
  239. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  240. package/src/engine/webcomponents/needle-engine.ts +12 -6
  241. package/src/engine/xr/NeedleXRSession.ts +48 -13
  242. package/src/engine/xr/TempXRContext.ts +2 -2
  243. package/src/engine/xr/events.ts +1 -1
  244. package/src/engine-components/Animation.ts +19 -16
  245. package/src/engine-components/AnimationBuilder.ts +472 -0
  246. package/src/engine-components/Animator.ts +24 -12
  247. package/src/engine-components/AnimatorController.builder.ts +387 -0
  248. package/src/engine-components/AnimatorController.ts +20 -291
  249. package/src/engine-components/AudioSource.ts +130 -79
  250. package/src/engine-components/Collider.ts +66 -18
  251. package/src/engine-components/Component.ts +118 -20
  252. package/src/engine-components/ContactShadows.ts +15 -1
  253. package/src/engine-components/DragControls.ts +18 -11
  254. package/src/engine-components/DropListener.ts +3 -0
  255. package/src/engine-components/EventList.ts +45 -83
  256. package/src/engine-components/Joints.ts +20 -4
  257. package/src/engine-components/Light.ts +10 -2
  258. package/src/engine-components/Networking.ts +1 -1
  259. package/src/engine-components/OrbitControls.ts +34 -14
  260. package/src/engine-components/RigidBody.ts +18 -4
  261. package/src/engine-components/SceneSwitcher.ts +3 -0
  262. package/src/engine-components/SeeThrough.ts +2 -2
  263. package/src/engine-components/api.ts +2 -1
  264. package/src/engine-components/codegen/components.ts +7 -13
  265. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  266. package/src/engine-components/timeline/PlayableDirector.ts +83 -81
  267. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  268. package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
  269. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  270. package/src/engine-components/timeline/TimelineTracks.ts +96 -27
  271. package/src/engine-components/timeline/index.ts +2 -1
  272. package/src/engine-components/ui/Canvas.ts +2 -8
  273. package/src/engine-components/ui/Text.ts +12 -8
  274. package/src/engine-components/web/CursorFollow.ts +21 -14
  275. package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
  276. package/dist/needle-engine.bundle-CmxIO5uH.min.js +0 -1732
  277. package/dist/needle-engine.bundle-tJIZukCz.umd.cjs +0 -1732
  278. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  279. package/lib/engine-components/AvatarLoader.js +0 -232
  280. package/lib/engine-components/AvatarLoader.js.map +0 -1
  281. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  282. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  283. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  284. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  285. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  286. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  287. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  288. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  289. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  290. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  291. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  292. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  293. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  294. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  295. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  296. package/plugins/dts-generator/dts.codegen.js +0 -334
  297. package/plugins/dts-generator/dts.scan.js +0 -99
  298. package/plugins/dts-generator/dts.writer.js +0 -59
  299. package/plugins/dts-generator/glb.discovery.js +0 -279
  300. package/plugins/dts-generator/glb.extractor.js +0 -215
  301. package/plugins/dts-generator/glb.reader.js +0 -167
  302. package/plugins/dts-generator/index.js +0 -36
  303. package/plugins/dts-generator/manifest.types.js +0 -174
  304. package/plugins/gltf-packer.mjs +0 -1
  305. package/src/engine-components/AvatarLoader.ts +0 -264
  306. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  307. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  308. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  309. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  310. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
  311. package/src/vite-env.d.ts +0 -16
@@ -1,7 +1,8 @@
1
1
  import * as flatbuffers from 'flatbuffers';
2
2
  import { PeerNetworking } from './engine_networking_peer.js';
3
- import { type IModel, type INetworkConnection, SendQueue } from './engine_networking_types.js';
3
+ import { type IModel, type INetworkConnection, type INetworkTransport, SendQueue } from './engine_networking_types.js';
4
4
  import { Context } from './engine_setup.js';
5
+ import type { DisposeFn } from "./engine_disposable.js";
5
6
  export declare const debugNet: boolean;
6
7
  export declare const debugOwner: boolean;
7
8
  export interface INetworkingWebsocketUrlProvider {
@@ -75,7 +76,7 @@ declare type OwnershipResponse = {
75
76
  guid: string;
76
77
  value: boolean;
77
78
  };
78
- declare type WebsocketSendType = IModel | object | boolean | null | string | number;
79
+ declare type WebsocketSendType = IModel | object | boolean | string | number;
79
80
  /** Maps known networking event keys to their callback signatures.
80
81
  * Used by {@link NetworkConnection.beginListen} and {@link NetworkConnection.stopListen} for type-safe event handling.
81
82
  */
@@ -365,7 +366,7 @@ export declare class NetworkConnection implements INetworkConnection {
365
366
  /** Use to leave a room that you are currently connected to (use `leaveRoom()` to disconnect from the currently active room but you can also specify a room name) */
366
367
  leaveRoom(room?: string | null): boolean;
367
368
  /** Send a message to the networking backend - it will be broadcasted to all connected users (except yourself) in the same room by default */
368
- send<K extends NetworkEventKey>(key: K, data?: (K extends keyof NetworkEventMap ? NetworkEventData<K> : WebsocketSendType) | null, queue?: SendQueue): void;
369
+ send<K extends NetworkEventKey>(key: K, data?: (K extends keyof NetworkEventMap ? NetworkEventData<K> : WebsocketSendType), queue?: SendQueue): void;
369
370
  /**
370
371
  * Deletes the network state for a specific object on the server.
371
372
  * This removes the object's state from the room, preventing it from being sent to newly joining users.
@@ -387,61 +388,69 @@ export declare class NetworkConnection implements INetworkConnection {
387
388
  private _defaultMessagesBufferArray;
388
389
  sendBufferedMessagesNow(): void;
389
390
  /** Use to start listening to networking events.
390
- * To unsubscribe from events use the `{@link stopListen}` method.
391
+ * Returns an unsubscribe function that removes the listener when called.
392
+ * The returned function can also be passed to {@link stopListen} or {@link Component.autoCleanup} for automatic lifecycle management.
391
393
  *
392
- * @example Custom event example
394
+ * @example With autoCleanup (recommended)
393
395
  * ```ts
394
- * // Listen to a custom event sent by the server
395
- * this.context.connection.beginListen<MyDataType>("my-custom-event", (data) => {
396
- * console.log("Received custom event:", data);
397
- * });
396
+ * export class MyComponent extends Behaviour {
397
+ * onEnable() {
398
+ * this.autoCleanup(this.context.connection.beginListen("joined-room", () => {
399
+ * console.log("I joined a networked room");
400
+ * }));
401
+ * }
402
+ * // Automatically unsubscribed on disable — no manual cleanup needed!
403
+ * }
398
404
  * ```
399
405
  *
400
- * @example Listening to room events
406
+ * @example Manual unsubscribe
407
+ * ```ts
408
+ * const unsub = this.context.connection.beginListen("joined-room", () => { });
409
+ * // Later:
410
+ * unsub(); // removes the listener
411
+ * ```
412
+ *
413
+ * @example With stopListen (legacy pattern, still supported)
401
414
  * ```ts
402
- * // Make sure to unsubscribe from events when the component is disabled
403
415
  * export class MyComponent extends Behaviour {
404
416
  * onEnable() {
405
- * this.connection.beginListen("joined-room", this.onJoinedRoom)
417
+ * this.context.connection.beginListen("joined-room", this.onJoinedRoom);
406
418
  * }
407
419
  * onDisable() {
408
- * this.connection.stopListen("joined-room", this.onJoinedRoom)
420
+ * this.context.connection.stopListen("joined-room", this.onJoinedRoom);
409
421
  * }
410
422
  * onJoinedRoom = () => {
411
- * console.log("I joined a networked room")
423
+ * console.log("I joined a networked room");
412
424
  * }
413
425
  * }
414
426
  * ```
415
427
  * @link https://engine.needle.tools/docs/networking.html
416
428
  *
417
429
  */
418
- beginListen<K extends NetworkEventKey>(key: K, callback: K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void): K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void;
430
+ beginListen<K extends NetworkEventKey>(key: K, callback: K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void): DisposeFn;
419
431
  /**@deprecated please use stopListen instead (2.65.2-pre) */
420
432
  stopListening<K extends NetworkEventKey>(key: K, callback: (K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void) | null): void;
421
- /** Use to stop listening to networking events
433
+ /** Use to stop listening to networking events.
434
+ * Accepts either the original callback or the unsubscribe function returned by {@link beginListen}.
422
435
  * To subscribe to events use the `{@link beginListen}` method.
423
- * See the example below for typical usage:
424
436
  *
425
- * ### Component Example
437
+ * @example
426
438
  * ```ts
427
- * // Make sure to unsubscribe from events when the component is disabled
428
- * export class MyComponent extends Behaviour {
429
- * onEnable() {
430
- * this.connection.beginListen("joined-room", this.onJoinedRoom)
431
- * }
432
- * onDisable() {
433
- * this.connection.stopListen("joined-room", this.onJoinedRoom)
434
- * }
435
- * onJoinedRoom = () => {
436
- * console.log("I joined a networked room")
437
- * }
438
- * }
439
+ * // Both patterns work:
440
+ * this.context.connection.stopListen("joined-room", this.onJoinedRoom); // original callback
441
+ * this.context.connection.stopListen("joined-room", unsub); // unsubscribe fn from beginListen
439
442
  * ```
440
443
  */
441
- stopListen<K extends NetworkEventKey>(key: K, callback: (K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void) | null): void;
442
- /** Use to start listening to networking binary events */
443
- beginListenBinary(identifier: string, callback: BinaryCallback): BinaryCallback;
444
- /** Use to stop listening to networking binary events */
444
+ private static _didLogStopListenHint;
445
+ stopListen<K extends NetworkEventKey>(key: K, callback: (K extends keyof NetworkEventMap ? NetworkEventMap[K] : (...args: any[]) => void) | Function | null): void;
446
+ /** Use to start listening to networking binary events.
447
+ * Returns an unsubscribe function that removes the listener when called.
448
+ * The returned function can also be passed to {@link stopListenBinary} or {@link Component.autoCleanup}.
449
+ */
450
+ beginListenBinary(identifier: string, callback: BinaryCallback): DisposeFn;
451
+ /** Use to stop listening to networking binary events.
452
+ * Accepts either the original callback or the unsubscribe function returned by {@link beginListenBinary}.
453
+ */
445
454
  stopListenBinary(identifier: string, callback: any): void;
446
455
  private netWebSocketUrlProvider?;
447
456
  /** Use to override the networking server backend url.
@@ -450,16 +459,19 @@ export declare class NetworkConnection implements INetworkConnection {
450
459
  registerProvider(prov: INetworkingWebsocketUrlProvider): void;
451
460
  /** Used to connect to the networking server
452
461
  * @param url Optional url to connect to. If not provided, it will use the url from the registered `INetworkingWebsocketUrlProvider` or the default backend networking url. If you want to change the url after connecting, you need to disconnect first and then connect again with the new url.
462
+ * @param transport Optional custom transport to use instead of the default websocket. Useful for testing or alternative transports.
453
463
  */
454
- connect(url?: string): Promise<boolean>;
464
+ connect(url?: string, transport?: INetworkTransport): Promise<boolean>;
455
465
  /** Disconnect from the networking backend + reset internal state */
456
466
  disconnect(): void;
467
+ /** Full teardown: disconnect, clear all listeners, and release all resources.
468
+ * Called when the owning Context is destroyed. After dispose(), this instance should not be reused. */
469
+ dispose(): void;
457
470
  private _listeners;
458
471
  private _listenersBinary;
459
472
  private connected;
460
- private channelId;
461
473
  private _connectionId;
462
- private _ws;
474
+ private _transport;
463
475
  private _waitingForSocket;
464
476
  private _isInRoom;
465
477
  private _currentRoomName;
@@ -469,6 +481,8 @@ export declare class NetworkConnection implements INetworkConnection {
469
481
  private _state;
470
482
  private _currentDelay;
471
483
  private _connectingToWebsocketPromise;
484
+ /** Wire up a transport's event callbacks and start it */
485
+ private connectTransport;
472
486
  private connectWebsocket;
473
487
  private onMessage;
474
488
  private handleIncomingBinaryMessage;
@@ -6,8 +6,11 @@ import { isDevEnvironment } from './debug/index.js';
6
6
  import { Telemetry } from './engine_license.js';
7
7
  import { PeerNetworking } from './engine_networking_peer.js';
8
8
  import { SendQueue } from './engine_networking_types.js';
9
+ import { WebsocketTransport } from './engine_networking.transport.websocket.js';
9
10
  import { isHostedOnGlitch } from './engine_networking_utils.js';
10
11
  import * as utils from "./engine_utils.js";
12
+ /** @internal Symbol used to attach the original callback to unsubscribe functions returned by beginListen/beginListenBinary */
13
+ const $originalCallback = Symbol("originalCallback");
11
14
  export const debugNet = utils.getParam("debugnet") ? true : false;
12
15
  export const debugOwner = debugNet || utils.getParam("debugowner") ? true : false;
13
16
  const debugnetBin = utils.getParam("debugnetbin");
@@ -442,8 +445,7 @@ export class NetworkConnection {
442
445
  * The current server url that the networking backend is connected to (e.g. the url of the websocket server)
443
446
  */
444
447
  get currentServerUrl() {
445
- // @ts-ignore (in ts-websocket 2.x this property is exposed)
446
- return this._ws?.url ?? null;
448
+ return this._transport?.url ?? null;
447
449
  }
448
450
  /** A ping is sent to the server at a regular interval while the browser tab is active. This method can be used to send additional ping messages when needed so that the user doesn't get disconnected from the networking backend */
449
451
  sendPing() {
@@ -494,20 +496,11 @@ export class NetworkConnection {
494
496
  this.send(RoomEvents.Leave, { room: room });
495
497
  return true;
496
498
  }
497
- send(key, data = null, queue = SendQueue.Queued) {
498
- //@ts-ignore
499
- if (data === null)
500
- data = {};
499
+ send(key, data, queue = SendQueue.Queued) {
501
500
  if (queue === SendQueue.Queued) {
502
501
  this._defaultMessagesBuffer.push({ key: key, value: data });
503
502
  return;
504
503
  }
505
- // if (!this.connected) return;
506
- // if (this.channelId)
507
- // data["__id"] = this.channelId;
508
- // else if (this.connectionId)
509
- // data["__id"] = this.connectionId;
510
- // this.sendGeckosIo(key, data);
511
504
  return this.sendWithWebsocket(key, data, queue);
512
505
  }
513
506
  /**
@@ -535,12 +528,12 @@ export class NetworkConnection {
535
528
  sendBinary(bin) {
536
529
  if (debugnetBin)
537
530
  console.log("<< send binary", this.context.time.frame, (bin.length / 1024) + " KB");
538
- this._ws?.send(bin);
531
+ this._transport?.send(bin);
539
532
  }
540
533
  _defaultMessagesBuffer = [];
541
534
  _defaultMessagesBufferArray = [];
542
535
  sendBufferedMessagesNow() {
543
- if (!this._ws)
536
+ if (!this._transport)
544
537
  return;
545
538
  this._defaultMessagesBufferArray.length = 0;
546
539
  const count = Object.keys(this._defaultMessagesBuffer).length;
@@ -560,31 +553,42 @@ export class NetworkConnection {
560
553
  if (this._defaultMessagesBufferArray.length <= 0)
561
554
  return;
562
555
  const message = JSON.stringify(this._defaultMessagesBufferArray);
563
- this._ws?.send(message);
556
+ this._transport?.send(message);
564
557
  }
565
558
  /** Use to start listening to networking events.
566
- * To unsubscribe from events use the `{@link stopListen}` method.
559
+ * Returns an unsubscribe function that removes the listener when called.
560
+ * The returned function can also be passed to {@link stopListen} or {@link Component.autoCleanup} for automatic lifecycle management.
567
561
  *
568
- * @example Custom event example
562
+ * @example With autoCleanup (recommended)
569
563
  * ```ts
570
- * // Listen to a custom event sent by the server
571
- * this.context.connection.beginListen<MyDataType>("my-custom-event", (data) => {
572
- * console.log("Received custom event:", data);
573
- * });
564
+ * export class MyComponent extends Behaviour {
565
+ * onEnable() {
566
+ * this.autoCleanup(this.context.connection.beginListen("joined-room", () => {
567
+ * console.log("I joined a networked room");
568
+ * }));
569
+ * }
570
+ * // Automatically unsubscribed on disable — no manual cleanup needed!
571
+ * }
574
572
  * ```
575
573
  *
576
- * @example Listening to room events
574
+ * @example Manual unsubscribe
575
+ * ```ts
576
+ * const unsub = this.context.connection.beginListen("joined-room", () => { });
577
+ * // Later:
578
+ * unsub(); // removes the listener
579
+ * ```
580
+ *
581
+ * @example With stopListen (legacy pattern, still supported)
577
582
  * ```ts
578
- * // Make sure to unsubscribe from events when the component is disabled
579
583
  * export class MyComponent extends Behaviour {
580
584
  * onEnable() {
581
- * this.connection.beginListen("joined-room", this.onJoinedRoom)
585
+ * this.context.connection.beginListen("joined-room", this.onJoinedRoom);
582
586
  * }
583
587
  * onDisable() {
584
- * this.connection.stopListen("joined-room", this.onJoinedRoom)
588
+ * this.context.connection.stopListen("joined-room", this.onJoinedRoom);
585
589
  * }
586
590
  * onJoinedRoom = () => {
587
- * console.log("I joined a networked room")
591
+ * console.log("I joined a networked room");
588
592
  * }
589
593
  * }
590
594
  * ```
@@ -595,33 +599,62 @@ export class NetworkConnection {
595
599
  if (!this._listeners[key])
596
600
  this._listeners[key] = [];
597
601
  this._listeners[key].push(callback);
598
- // TODO: for a breaking change it would be easier to return a function to unsubscribe directly
599
- return callback;
602
+ const unsub = () => this.stopListen(key, callback);
603
+ unsub[$originalCallback] = callback;
604
+ return unsub;
600
605
  }
601
606
  /**@deprecated please use stopListen instead (2.65.2-pre) */
602
607
  stopListening(key, callback) { return this.stopListen(key, callback); }
608
+ /** Use to stop listening to networking events.
609
+ * Accepts either the original callback or the unsubscribe function returned by {@link beginListen}.
610
+ * To subscribe to events use the `{@link beginListen}` method.
611
+ *
612
+ * @example
613
+ * ```ts
614
+ * // Both patterns work:
615
+ * this.context.connection.stopListen("joined-room", this.onJoinedRoom); // original callback
616
+ * this.context.connection.stopListen("joined-room", unsub); // unsubscribe fn from beginListen
617
+ * ```
618
+ */
619
+ static _didLogStopListenHint = false;
603
620
  stopListen(key, callback) {
604
621
  if (!callback)
605
622
  return;
606
623
  if (!this._listeners[key])
607
624
  return;
608
- const index = this._listeners[key].indexOf(callback);
625
+ // Backwards compat: if an unsubscribe function returned by beginListen was passed to stopListen,
626
+ // resolve it to the original callback via symbol so the listener can be found and removed.
627
+ const original = callback[$originalCallback] ?? callback;
628
+ if (original !== callback && isDevEnvironment() && !NetworkConnection._didLogStopListenHint) {
629
+ NetworkConnection._didLogStopListenHint = true;
630
+ console.warn("[Needle Engine] Tip: beginListen() returns an unsubscribe function — you can call it directly instead of using stopListen().");
631
+ }
632
+ const index = this._listeners[key].indexOf(original);
609
633
  if (index >= 0) {
610
634
  this._listeners[key].splice(index, 1);
611
635
  }
612
636
  }
613
- /** Use to start listening to networking binary events */
637
+ /** Use to start listening to networking binary events.
638
+ * Returns an unsubscribe function that removes the listener when called.
639
+ * The returned function can also be passed to {@link stopListenBinary} or {@link Component.autoCleanup}.
640
+ */
614
641
  beginListenBinary(identifier, callback) {
615
642
  if (!this._listenersBinary[identifier])
616
643
  this._listenersBinary[identifier] = [];
617
644
  this._listenersBinary[identifier].push(callback);
618
- return callback;
645
+ const unsub = () => this.stopListenBinary(identifier, callback);
646
+ unsub[$originalCallback] = callback;
647
+ return unsub;
619
648
  }
620
- /** Use to stop listening to networking binary events */
649
+ /** Use to stop listening to networking binary events.
650
+ * Accepts either the original callback or the unsubscribe function returned by {@link beginListenBinary}.
651
+ */
621
652
  stopListenBinary(identifier, callback) {
622
653
  if (!this._listenersBinary[identifier])
623
654
  return;
624
- const index = this._listenersBinary[identifier].indexOf(callback);
655
+ // Backwards compat: resolve unsubscribe function to original callback via symbol
656
+ const original = callback?.[$originalCallback] ?? callback;
657
+ const index = this._listenersBinary[identifier].indexOf(original);
625
658
  if (index >= 0) {
626
659
  this._listenersBinary[identifier].splice(index, 1);
627
660
  }
@@ -635,14 +668,18 @@ export class NetworkConnection {
635
668
  }
636
669
  /** Used to connect to the networking server
637
670
  * @param url Optional url to connect to. If not provided, it will use the url from the registered `INetworkingWebsocketUrlProvider` or the default backend networking url. If you want to change the url after connecting, you need to disconnect first and then connect again with the new url.
671
+ * @param transport Optional custom transport to use instead of the default websocket. Useful for testing or alternative transports.
638
672
  */
639
- async connect(url) {
673
+ async connect(url, transport) {
640
674
  if (this.connected && url && url !== networkingServerUrl) {
641
675
  return Promise.reject("Can not connect to different server url. Please disconnect first.");
642
676
  }
643
677
  if (this.connected) {
644
678
  return Promise.resolve(true);
645
679
  }
680
+ if (transport) {
681
+ return this.connectTransport(transport);
682
+ }
646
683
  if (url)
647
684
  console.debug("Connecting to user provided url " + url);
648
685
  const overrideUrl = url || this.netWebSocketUrlProvider?.getWebsocketUrl();
@@ -657,10 +694,19 @@ export class NetworkConnection {
657
694
  ;
658
695
  /** Disconnect from the networking backend + reset internal state */
659
696
  disconnect() {
660
- this._ws?.close();
661
- this._ws = undefined;
697
+ if (this._transport) {
698
+ // Clear callbacks before closing so the onClose handler doesn't fire
699
+ this._transport.onOpen = null;
700
+ this._transport.onClose = null;
701
+ this._transport.onError = null;
702
+ this._transport.onMessage = null;
703
+ this._transport.close();
704
+ this._transport = undefined;
705
+ }
662
706
  networkingServerUrl = undefined;
663
- // Reset all state
707
+ // Reset all state synchronously so callers can rely on isConnected/isInRoom immediately
708
+ this.connected = false;
709
+ this._connectionId = null;
664
710
  this._currentRoomAllowEditing = true;
665
711
  this._currentRoomName = null;
666
712
  this._currentRoomViewId = null;
@@ -668,14 +714,23 @@ export class NetworkConnection {
668
714
  this._currentInRoom.length = 0;
669
715
  this._state = {};
670
716
  this._currentDelay = -1;
717
+ this._connectingToWebsocketPromise = null;
718
+ }
719
+ /** Full teardown: disconnect, clear all listeners, and release all resources.
720
+ * Called when the owning Context is destroyed. After dispose(), this instance should not be reused. */
721
+ dispose() {
722
+ this.disconnect();
723
+ this._listeners = {};
724
+ this._listenersBinary = {};
725
+ this._waitingForSocket = {};
726
+ this._peer = null;
671
727
  }
672
728
  _listeners = {};
673
729
  _listenersBinary = {};
674
730
  connected = false;
675
- channelId;
676
731
  _connectionId = null;
677
- // Websocket ------------------------------------------------------------
678
- _ws;
732
+ // Transport ------------------------------------------------------------
733
+ _transport;
679
734
  _waitingForSocket = {};
680
735
  _isInRoom = false;
681
736
  _currentRoomName = null;
@@ -685,10 +740,11 @@ export class NetworkConnection {
685
740
  _state = {};
686
741
  _currentDelay = -1;
687
742
  _connectingToWebsocketPromise = null;
688
- connectWebsocket() {
743
+ /** Wire up a transport's event callbacks and start it */
744
+ connectTransport(transport) {
689
745
  if (this._connectingToWebsocketPromise)
690
746
  return this._connectingToWebsocketPromise;
691
- return this._connectingToWebsocketPromise = new Promise(async (res, _) => {
747
+ return this._connectingToWebsocketPromise = new Promise((res) => {
692
748
  let didResolve = false;
693
749
  const resolve = (val) => {
694
750
  if (didResolve)
@@ -696,63 +752,57 @@ export class NetworkConnection {
696
752
  didResolve = true;
697
753
  res(val);
698
754
  };
699
- if (networkingServerUrl === undefined) {
700
- console.log("Fetch default backend url: " + defaultNetworkingBackendUrlProvider);
701
- const failed = false;
702
- const defaultUrlResponse = await fetch(defaultNetworkingBackendUrlProvider);
703
- networkingServerUrl = await defaultUrlResponse.text();
704
- if (failed)
705
- return;
706
- }
707
- if (networkingServerUrl === undefined) {
708
- resolve(false);
709
- return;
710
- }
711
- console.debug("Connecting to networking backend on\n" + networkingServerUrl);
712
- const pkg = await import('websocket-ts');
713
- // @ts-ignore
714
- const WebsocketBuilder = pkg.default?.WebsocketBuilder ?? pkg.WebsocketBuilder;
715
- const ExponentialBackoff = pkg.default?.ExponentialBackoff ?? pkg.ExponentialBackoff;
716
- const ws = new WebsocketBuilder(networkingServerUrl)
717
- .withMaxRetries(10)
718
- .withBackoff(new ExponentialBackoff(2000, 4))
719
- .onOpen(() => {
755
+ transport.onOpen = () => {
720
756
  this._connectingToWebsocketPromise = null;
721
- this._ws = ws;
757
+ this._transport = transport;
722
758
  this.connected = true;
723
759
  if (isDevEnvironment() || debugNet)
724
- console.log("Connected to networking backend\n" + networkingServerUrl);
760
+ console.log("Connected to networking backend\n" + (transport.url ?? ""));
725
761
  else
726
- console.debug("Connected to networking backend", networkingServerUrl);
762
+ console.debug("Connected to networking backend", transport.url ?? "");
727
763
  resolve(true);
728
764
  this.onSendQueued(SendQueue.OnConnection);
729
- })
730
- .onClose((_evt) => {
765
+ };
766
+ transport.onClose = () => {
731
767
  this._connectingToWebsocketPromise = null;
732
768
  this.connected = false;
733
769
  this._isInRoom = false;
734
770
  resolve(false);
735
771
  let msg = "Websocket connection closed...";
736
- if (!networkingServerUrl?.includes("/socket"))
737
- msg += ` Do you perhaps mean to connect to \"/socket\"?`;
772
+ if (!transport.url?.includes("/socket"))
773
+ msg += ` Do you perhaps mean to connect to "/socket"?`;
738
774
  console.error(msg);
739
- })
740
- .onError((_e) => {
775
+ };
776
+ transport.onError = () => {
741
777
  console.error("Websocket connection failed...");
742
778
  resolve(false);
743
779
  Telemetry.sendEvent(this.context, "networking", {
744
780
  event: "connection_error",
745
781
  });
746
- })
747
- .onRetry(() => { console.log("Retry connecting to networking websocket"); })
748
- .build();
749
- ws.addEventListener(pkg.WebsocketEvent.message, (socket, msg) => {
750
- this.onMessage(socket, msg);
751
- });
782
+ };
783
+ transport.onMessage = (data) => {
784
+ this.onMessage(data);
785
+ };
786
+ transport.start();
752
787
  });
753
788
  }
754
- onMessage(_, ev) {
755
- const msg = ev.data;
789
+ async connectWebsocket() {
790
+ if (this._connectingToWebsocketPromise)
791
+ return this._connectingToWebsocketPromise;
792
+ if (networkingServerUrl === undefined) {
793
+ console.log("Fetch default backend url: " + defaultNetworkingBackendUrlProvider);
794
+ const defaultUrlResponse = await fetch(defaultNetworkingBackendUrlProvider);
795
+ networkingServerUrl = await defaultUrlResponse.text();
796
+ }
797
+ if (networkingServerUrl === undefined) {
798
+ return false;
799
+ }
800
+ console.debug("Connecting to networking backend on\n" + networkingServerUrl);
801
+ const transport = new WebsocketTransport(networkingServerUrl);
802
+ return this.connectTransport(transport);
803
+ }
804
+ onMessage(data) {
805
+ const msg = data;
756
806
  try {
757
807
  if (typeof msg !== "string") {
758
808
  if (msg.size) {
@@ -927,7 +977,7 @@ export class NetworkConnection {
927
977
  }
928
978
  sendWithWebsocket(key, data, queue = SendQueue.OnRoomJoin) {
929
979
  // console.log(key);
930
- if (!this._ws) {
980
+ if (!this._transport) {
931
981
  const arr = this._waitingForSocket[queue] || [];
932
982
  arr.push(() => this.sendWithWebsocket(key, data, queue));
933
983
  this._waitingForSocket[queue] = arr;
@@ -937,7 +987,7 @@ export class NetworkConnection {
937
987
  const str = JSON.stringify(this.toMessage(key, data));
938
988
  if (debugNet)
939
989
  console.log(">>", key);
940
- this._ws.send(str);
990
+ this._transport.send(str);
941
991
  }
942
992
  onSendQueued(queue) {
943
993
  const queued = this._waitingForSocket[queue];