rn-gamekit 0.1.0 → 0.2.1

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 (601) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +97 -16
  3. package/lib/module/assets/defineAssets.js +28 -3
  4. package/lib/module/assets/defineAssets.js.map +1 -1
  5. package/lib/module/assets.js +11 -0
  6. package/lib/module/assets.js.map +1 -0
  7. package/lib/module/audio/createGameAudio.js +784 -0
  8. package/lib/module/audio/createGameAudio.js.map +1 -0
  9. package/lib/module/audio/errors.js +12 -0
  10. package/lib/module/audio/errors.js.map +1 -0
  11. package/lib/module/audio/index.js +5 -0
  12. package/lib/module/audio/index.js.map +1 -0
  13. package/lib/module/audio/resolver.js +37 -0
  14. package/lib/module/audio/resolver.js.map +1 -0
  15. package/lib/module/audio/types.js +4 -0
  16. package/lib/module/audio/types.js.map +1 -0
  17. package/lib/module/audio.js +9 -0
  18. package/lib/module/audio.js.map +1 -0
  19. package/lib/module/camera2d/bounds.js +59 -0
  20. package/lib/module/camera2d/bounds.js.map +1 -0
  21. package/lib/module/camera2d/extents.js +25 -0
  22. package/lib/module/camera2d/extents.js.map +1 -0
  23. package/lib/module/camera2d/follow.js +138 -0
  24. package/lib/module/camera2d/follow.js.map +1 -0
  25. package/lib/module/camera2d/index.js +20 -0
  26. package/lib/module/camera2d/index.js.map +1 -0
  27. package/lib/module/camera2d/interpolation.js +98 -0
  28. package/lib/module/camera2d/interpolation.js.map +1 -0
  29. package/lib/module/camera2d/shake.js +85 -0
  30. package/lib/module/camera2d/shake.js.map +1 -0
  31. package/lib/module/camera2d/transform.js +151 -0
  32. package/lib/module/camera2d/transform.js.map +1 -0
  33. package/lib/module/camera2d/types.js +4 -0
  34. package/lib/module/camera2d/types.js.map +1 -0
  35. package/lib/module/camera2d/validation.js +98 -0
  36. package/lib/module/camera2d/validation.js.map +1 -0
  37. package/lib/module/camera2d/visibility.js +138 -0
  38. package/lib/module/camera2d/visibility.js.map +1 -0
  39. package/lib/module/camera2d.js +9 -0
  40. package/lib/module/camera2d.js.map +1 -0
  41. package/lib/module/collision2d/colliders.js +266 -0
  42. package/lib/module/collision2d/colliders.js.map +1 -0
  43. package/lib/module/collision2d/debug.js +105 -0
  44. package/lib/module/collision2d/debug.js.map +1 -0
  45. package/lib/module/collision2d/filters.js +40 -0
  46. package/lib/module/collision2d/filters.js.map +1 -0
  47. package/lib/module/collision2d/index.js +12 -0
  48. package/lib/module/collision2d/index.js.map +1 -0
  49. package/lib/module/collision2d/intersections.js +67 -0
  50. package/lib/module/collision2d/intersections.js.map +1 -0
  51. package/lib/module/collision2d/manifolds.js +218 -0
  52. package/lib/module/collision2d/manifolds.js.map +1 -0
  53. package/lib/module/collision2d/segments.js +187 -0
  54. package/lib/module/collision2d/segments.js.map +1 -0
  55. package/lib/module/collision2d/spatialHash.js +167 -0
  56. package/lib/module/collision2d/spatialHash.js.map +1 -0
  57. package/lib/module/collision2d/sweeps.js +255 -0
  58. package/lib/module/collision2d/sweeps.js.map +1 -0
  59. package/lib/module/collision2d.js +9 -0
  60. package/lib/module/collision2d.js.map +1 -0
  61. package/lib/module/core/input/buttonPad.js +214 -0
  62. package/lib/module/core/input/buttonPad.js.map +1 -0
  63. package/lib/module/core/input/createInputBuffer.js +66 -14
  64. package/lib/module/core/input/createInputBuffer.js.map +1 -1
  65. package/lib/module/core/session/createGameSession.js +454 -50
  66. package/lib/module/core/session/createGameSession.js.map +1 -1
  67. package/lib/module/core/session/deepFreeze.js +92 -2
  68. package/lib/module/core/session/deepFreeze.js.map +1 -1
  69. package/lib/module/core/session/types.js +12 -0
  70. package/lib/module/core/session/types.js.map +1 -1
  71. package/lib/module/definition/defineGame.js +23 -0
  72. package/lib/module/definition/defineGame.js.map +1 -1
  73. package/lib/module/events/defineGameEvents.js +46 -0
  74. package/lib/module/events/defineGameEvents.js.map +1 -0
  75. package/lib/module/events/errors.js +16 -0
  76. package/lib/module/events/errors.js.map +1 -0
  77. package/lib/module/events/index.js +7 -0
  78. package/lib/module/events/index.js.map +1 -0
  79. package/lib/module/events/payload.js +219 -0
  80. package/lib/module/events/payload.js.map +1 -0
  81. package/lib/module/events/seed.js +33 -0
  82. package/lib/module/events/seed.js.map +1 -0
  83. package/lib/module/events/types.js +4 -0
  84. package/lib/module/events/types.js.map +1 -0
  85. package/lib/module/events.js +13 -0
  86. package/lib/module/events.js.map +1 -0
  87. package/lib/module/geometry/helpers.js +140 -0
  88. package/lib/module/geometry/helpers.js.map +1 -0
  89. package/lib/module/geometry/index.js +5 -0
  90. package/lib/module/geometry/index.js.map +1 -0
  91. package/lib/module/geometry/validation.js +104 -0
  92. package/lib/module/geometry/validation.js.map +1 -0
  93. package/lib/module/geometry.js +9 -0
  94. package/lib/module/geometry.js.map +1 -0
  95. package/lib/module/haptics/createGameHaptics.js +214 -0
  96. package/lib/module/haptics/createGameHaptics.js.map +1 -0
  97. package/lib/module/haptics/errors.js +12 -0
  98. package/lib/module/haptics/errors.js.map +1 -0
  99. package/lib/module/haptics/index.js +5 -0
  100. package/lib/module/haptics/index.js.map +1 -0
  101. package/lib/module/haptics/resolver.js +37 -0
  102. package/lib/module/haptics/resolver.js.map +1 -0
  103. package/lib/module/haptics/types.js +4 -0
  104. package/lib/module/haptics/types.js.map +1 -0
  105. package/lib/module/haptics.js +9 -0
  106. package/lib/module/haptics.js.map +1 -0
  107. package/lib/module/index.js +8 -1
  108. package/lib/module/index.js.map +1 -1
  109. package/lib/module/particles/createParticleSystem.js +481 -0
  110. package/lib/module/particles/createParticleSystem.js.map +1 -0
  111. package/lib/module/particles/defineParticleEffect.js +178 -0
  112. package/lib/module/particles/defineParticleEffect.js.map +1 -0
  113. package/lib/module/particles/errors.js +9 -0
  114. package/lib/module/particles/errors.js.map +1 -0
  115. package/lib/module/particles/index.js +8 -0
  116. package/lib/module/particles/index.js.map +1 -0
  117. package/lib/module/particles/sampling.js +121 -0
  118. package/lib/module/particles/sampling.js.map +1 -0
  119. package/lib/module/particles/types.js +4 -0
  120. package/lib/module/particles/types.js.map +1 -0
  121. package/lib/module/particles.js +4 -0
  122. package/lib/module/particles.js.map +1 -0
  123. package/lib/module/react/GameButtonPad.js +344 -0
  124. package/lib/module/react/GameButtonPad.js.map +1 -0
  125. package/lib/module/react/GamePointerInput.js +74 -16
  126. package/lib/module/react/GamePointerInput.js.map +1 -1
  127. package/lib/module/react/GameView.js +139 -60
  128. package/lib/module/react/GameView.js.map +1 -1
  129. package/lib/module/react/assets/createGameAssetStore.js +145 -95
  130. package/lib/module/react/assets/createGameAssetStore.js.map +1 -1
  131. package/lib/module/react/assets/useGameAssets.js +60 -35
  132. package/lib/module/react/assets/useGameAssets.js.map +1 -1
  133. package/lib/module/react/bindAppLifecycle.js +46 -17
  134. package/lib/module/react/bindAppLifecycle.js.map +1 -1
  135. package/lib/module/react/bindGameSession.js +13 -2
  136. package/lib/module/react/bindGameSession.js.map +1 -1
  137. package/lib/module/react/camera2d/GameLayer2D.js +50 -0
  138. package/lib/module/react/camera2d/GameLayer2D.js.map +1 -0
  139. package/lib/module/react/camera2d/defineGameCamera2D.js +30 -0
  140. package/lib/module/react/camera2d/defineGameCamera2D.js.map +1 -0
  141. package/lib/module/react/camera2d/usePresentedCameraBinding.js +177 -0
  142. package/lib/module/react/camera2d/usePresentedCameraBinding.js.map +1 -0
  143. package/lib/module/react/cleanupList.js +38 -0
  144. package/lib/module/react/cleanupList.js.map +1 -0
  145. package/lib/module/react/lifecycleSource.js +49 -0
  146. package/lib/module/react/lifecycleSource.js.map +1 -0
  147. package/lib/module/react/particles/ParticleView.js +308 -0
  148. package/lib/module/react/particles/ParticleView.js.map +1 -0
  149. package/lib/module/react/particles/culling.js +115 -0
  150. package/lib/module/react/particles/culling.js.map +1 -0
  151. package/lib/module/react/particles/index.js +7 -0
  152. package/lib/module/react/particles/index.js.map +1 -0
  153. package/lib/module/react/particles/spriteXForm.js +45 -0
  154. package/lib/module/react/particles/spriteXForm.js.map +1 -0
  155. package/lib/module/react/particles/useParticlePresentation.js +204 -0
  156. package/lib/module/react/particles/useParticlePresentation.js.map +1 -0
  157. package/lib/module/react/pointerBinding.js +55 -17
  158. package/lib/module/react/pointerBinding.js.map +1 -1
  159. package/lib/module/react/pointerCamera.js +28 -0
  160. package/lib/module/react/pointerCamera.js.map +1 -0
  161. package/lib/module/react/pointerCoalescer.js +16 -9
  162. package/lib/module/react/pointerCoalescer.js.map +1 -1
  163. package/lib/module/react/sprites/GameSprite.js +5 -17
  164. package/lib/module/react/sprites/GameSprite.js.map +1 -1
  165. package/lib/module/react/sprites/GameWorld2D.js +112 -9
  166. package/lib/module/react/sprites/GameWorld2D.js.map +1 -1
  167. package/lib/module/react/sprites/Sprite.js +48 -63
  168. package/lib/module/react/sprites/Sprite.js.map +1 -1
  169. package/lib/module/react/sprites/SpriteBatch.js +53 -28
  170. package/lib/module/react/sprites/SpriteBatch.js.map +1 -1
  171. package/lib/module/react/sprites/batchVisibility.js +58 -0
  172. package/lib/module/react/sprites/batchVisibility.js.map +1 -0
  173. package/lib/module/react/sprites/spriteSelection.js +103 -0
  174. package/lib/module/react/sprites/spriteSelection.js.map +1 -0
  175. package/lib/module/react/sprites/spriteTransform.js +46 -25
  176. package/lib/module/react/sprites/spriteTransform.js.map +1 -1
  177. package/lib/module/react/tilemap/TileMapLayer2D.js +217 -0
  178. package/lib/module/react/tilemap/TileMapLayer2D.js.map +1 -0
  179. package/lib/module/react/tilemap/tilePresentation.js +374 -0
  180. package/lib/module/react/tilemap/tilePresentation.js.map +1 -0
  181. package/lib/module/react/useGameSession.js +70 -0
  182. package/lib/module/react/useGameSession.js.map +1 -0
  183. package/lib/module/react/useGameSessionStatus.js +46 -0
  184. package/lib/module/react/useGameSessionStatus.js.map +1 -0
  185. package/lib/module/react/viewportBinding.js +5 -1
  186. package/lib/module/react/viewportBinding.js.map +1 -1
  187. package/lib/module/react.js +12 -1
  188. package/lib/module/react.js.map +1 -1
  189. package/lib/module/scene/defineScene.js +17 -0
  190. package/lib/module/scene/defineScene.js.map +1 -1
  191. package/lib/module/sprites/sampleSpriteClip.js +66 -6
  192. package/lib/module/sprites/sampleSpriteClip.js.map +1 -1
  193. package/lib/module/sprites/spriteAnimationState.js +10 -20
  194. package/lib/module/sprites/spriteAnimationState.js.map +1 -1
  195. package/lib/module/sprites.js +11 -0
  196. package/lib/module/sprites.js.map +1 -0
  197. package/lib/module/storage/adapters/asyncStorage.js +57 -0
  198. package/lib/module/storage/adapters/asyncStorage.js.map +1 -0
  199. package/lib/module/storage/adapters/memory.js +50 -0
  200. package/lib/module/storage/adapters/memory.js.map +1 -0
  201. package/lib/module/storage/errors.js +34 -0
  202. package/lib/module/storage/errors.js.map +1 -0
  203. package/lib/module/storage/index.js +11 -0
  204. package/lib/module/storage/index.js.map +1 -0
  205. package/lib/module/storage/schema.js +151 -0
  206. package/lib/module/storage/schema.js.map +1 -0
  207. package/lib/module/storage/serialization.js +249 -0
  208. package/lib/module/storage/serialization.js.map +1 -0
  209. package/lib/module/storage/store.js +387 -0
  210. package/lib/module/storage/store.js.map +1 -0
  211. package/lib/module/storage/types.js +16 -0
  212. package/lib/module/storage/types.js.map +1 -0
  213. package/lib/module/storage/validation.js +111 -0
  214. package/lib/module/storage/validation.js.map +1 -0
  215. package/lib/module/storage.js +4 -0
  216. package/lib/module/storage.js.map +1 -0
  217. package/lib/module/testing.js +4 -0
  218. package/lib/module/testing.js.map +1 -1
  219. package/lib/module/tilemap/chunkStats.js +27 -0
  220. package/lib/module/tilemap/chunkStats.js.map +1 -0
  221. package/lib/module/tilemap/definitions.js +308 -0
  222. package/lib/module/tilemap/definitions.js.map +1 -0
  223. package/lib/module/tilemap/errors.js +13 -0
  224. package/lib/module/tilemap/errors.js.map +1 -0
  225. package/lib/module/tilemap/index.js +10 -0
  226. package/lib/module/tilemap/index.js.map +1 -0
  227. package/lib/module/tilemap/movement.js +379 -0
  228. package/lib/module/tilemap/movement.js.map +1 -0
  229. package/lib/module/tilemap/queries.js +114 -0
  230. package/lib/module/tilemap/queries.js.map +1 -0
  231. package/lib/module/tilemap/tiledAdapter.js +141 -0
  232. package/lib/module/tilemap/tiledAdapter.js.map +1 -0
  233. package/lib/module/tilemap/types.js +4 -0
  234. package/lib/module/tilemap/types.js.map +1 -0
  235. package/lib/module/tilemap.js +4 -0
  236. package/lib/module/tilemap.js.map +1 -0
  237. package/lib/typescript/src/assets/defineAssets.d.ts +1 -1
  238. package/lib/typescript/src/assets/defineAssets.d.ts.map +1 -1
  239. package/lib/typescript/src/assets.d.ts +11 -0
  240. package/lib/typescript/src/assets.d.ts.map +1 -0
  241. package/lib/typescript/src/audio/createGameAudio.d.ts +5 -0
  242. package/lib/typescript/src/audio/createGameAudio.d.ts.map +1 -0
  243. package/lib/typescript/src/audio/errors.d.ts +6 -0
  244. package/lib/typescript/src/audio/errors.d.ts.map +1 -0
  245. package/lib/typescript/src/audio/index.d.ts +4 -0
  246. package/lib/typescript/src/audio/index.d.ts.map +1 -0
  247. package/lib/typescript/src/audio/resolver.d.ts +44 -0
  248. package/lib/typescript/src/audio/resolver.d.ts.map +1 -0
  249. package/lib/typescript/src/audio/types.d.ts +42 -0
  250. package/lib/typescript/src/audio/types.d.ts.map +1 -0
  251. package/lib/typescript/src/audio.d.ts +8 -0
  252. package/lib/typescript/src/audio.d.ts.map +1 -0
  253. package/lib/typescript/src/camera2d/bounds.d.ts +25 -0
  254. package/lib/typescript/src/camera2d/bounds.d.ts.map +1 -0
  255. package/lib/typescript/src/camera2d/extents.d.ts +17 -0
  256. package/lib/typescript/src/camera2d/extents.d.ts.map +1 -0
  257. package/lib/typescript/src/camera2d/follow.d.ts +24 -0
  258. package/lib/typescript/src/camera2d/follow.d.ts.map +1 -0
  259. package/lib/typescript/src/camera2d/index.d.ts +19 -0
  260. package/lib/typescript/src/camera2d/index.d.ts.map +1 -0
  261. package/lib/typescript/src/camera2d/interpolation.d.ts +17 -0
  262. package/lib/typescript/src/camera2d/interpolation.d.ts.map +1 -0
  263. package/lib/typescript/src/camera2d/shake.d.ts +20 -0
  264. package/lib/typescript/src/camera2d/shake.d.ts.map +1 -0
  265. package/lib/typescript/src/camera2d/transform.d.ts +44 -0
  266. package/lib/typescript/src/camera2d/transform.d.ts.map +1 -0
  267. package/lib/typescript/src/camera2d/types.d.ts +74 -0
  268. package/lib/typescript/src/camera2d/types.d.ts.map +1 -0
  269. package/lib/typescript/src/camera2d/validation.d.ts +30 -0
  270. package/lib/typescript/src/camera2d/validation.d.ts.map +1 -0
  271. package/lib/typescript/src/camera2d/visibility.d.ts +47 -0
  272. package/lib/typescript/src/camera2d/visibility.d.ts.map +1 -0
  273. package/lib/typescript/src/camera2d.d.ts +7 -0
  274. package/lib/typescript/src/camera2d.d.ts.map +1 -0
  275. package/lib/typescript/src/collision2d/colliders.d.ts +120 -0
  276. package/lib/typescript/src/collision2d/colliders.d.ts.map +1 -0
  277. package/lib/typescript/src/collision2d/debug.d.ts +67 -0
  278. package/lib/typescript/src/collision2d/debug.d.ts.map +1 -0
  279. package/lib/typescript/src/collision2d/filters.d.ts +28 -0
  280. package/lib/typescript/src/collision2d/filters.d.ts.map +1 -0
  281. package/lib/typescript/src/collision2d/index.d.ts +17 -0
  282. package/lib/typescript/src/collision2d/index.d.ts.map +1 -0
  283. package/lib/typescript/src/collision2d/intersections.d.ts +24 -0
  284. package/lib/typescript/src/collision2d/intersections.d.ts.map +1 -0
  285. package/lib/typescript/src/collision2d/manifolds.d.ts +39 -0
  286. package/lib/typescript/src/collision2d/manifolds.d.ts.map +1 -0
  287. package/lib/typescript/src/collision2d/segments.d.ts +24 -0
  288. package/lib/typescript/src/collision2d/segments.d.ts.map +1 -0
  289. package/lib/typescript/src/collision2d/spatialHash.d.ts +50 -0
  290. package/lib/typescript/src/collision2d/spatialHash.d.ts.map +1 -0
  291. package/lib/typescript/src/collision2d/sweeps.d.ts +60 -0
  292. package/lib/typescript/src/collision2d/sweeps.d.ts.map +1 -0
  293. package/lib/typescript/src/collision2d.d.ts +7 -0
  294. package/lib/typescript/src/collision2d.d.ts.map +1 -0
  295. package/lib/typescript/src/core/input/buttonPad.d.ts +59 -0
  296. package/lib/typescript/src/core/input/buttonPad.d.ts.map +1 -0
  297. package/lib/typescript/src/core/input/createInputBuffer.d.ts +1 -1
  298. package/lib/typescript/src/core/input/createInputBuffer.d.ts.map +1 -1
  299. package/lib/typescript/src/core/input/types.d.ts +7 -0
  300. package/lib/typescript/src/core/input/types.d.ts.map +1 -1
  301. package/lib/typescript/src/core/session/createGameSession.d.ts +4 -2
  302. package/lib/typescript/src/core/session/createGameSession.d.ts.map +1 -1
  303. package/lib/typescript/src/core/session/deepFreeze.d.ts +21 -1
  304. package/lib/typescript/src/core/session/deepFreeze.d.ts.map +1 -1
  305. package/lib/typescript/src/core/session/diagnostics.d.ts +2 -0
  306. package/lib/typescript/src/core/session/diagnostics.d.ts.map +1 -1
  307. package/lib/typescript/src/core/session/types.d.ts +69 -4
  308. package/lib/typescript/src/core/session/types.d.ts.map +1 -1
  309. package/lib/typescript/src/definition/defineGame.d.ts +12 -1
  310. package/lib/typescript/src/definition/defineGame.d.ts.map +1 -1
  311. package/lib/typescript/src/definition/types.d.ts +10 -1
  312. package/lib/typescript/src/definition/types.d.ts.map +1 -1
  313. package/lib/typescript/src/events/defineGameEvents.d.ts +26 -0
  314. package/lib/typescript/src/events/defineGameEvents.d.ts.map +1 -0
  315. package/lib/typescript/src/events/errors.d.ts +12 -0
  316. package/lib/typescript/src/events/errors.d.ts.map +1 -0
  317. package/lib/typescript/src/events/index.d.ts +6 -0
  318. package/lib/typescript/src/events/index.d.ts.map +1 -0
  319. package/lib/typescript/src/events/payload.d.ts +21 -0
  320. package/lib/typescript/src/events/payload.d.ts.map +1 -0
  321. package/lib/typescript/src/events/seed.d.ts +3 -0
  322. package/lib/typescript/src/events/seed.d.ts.map +1 -0
  323. package/lib/typescript/src/events/types.d.ts +42 -0
  324. package/lib/typescript/src/events/types.d.ts.map +1 -0
  325. package/lib/typescript/src/events.d.ts +12 -0
  326. package/lib/typescript/src/events.d.ts.map +1 -0
  327. package/lib/typescript/src/geometry/helpers.d.ts +35 -0
  328. package/lib/typescript/src/geometry/helpers.d.ts.map +1 -0
  329. package/lib/typescript/src/geometry/index.d.ts +4 -0
  330. package/lib/typescript/src/geometry/index.d.ts.map +1 -0
  331. package/lib/typescript/src/geometry/types.d.ts +49 -0
  332. package/lib/typescript/src/geometry/types.d.ts.map +1 -1
  333. package/lib/typescript/src/geometry/validation.d.ts +44 -0
  334. package/lib/typescript/src/geometry/validation.d.ts.map +1 -0
  335. package/lib/typescript/src/geometry.d.ts +7 -0
  336. package/lib/typescript/src/geometry.d.ts.map +1 -0
  337. package/lib/typescript/src/haptics/createGameHaptics.d.ts +3 -0
  338. package/lib/typescript/src/haptics/createGameHaptics.d.ts.map +1 -0
  339. package/lib/typescript/src/haptics/errors.d.ts +6 -0
  340. package/lib/typescript/src/haptics/errors.d.ts.map +1 -0
  341. package/lib/typescript/src/haptics/index.d.ts +4 -0
  342. package/lib/typescript/src/haptics/index.d.ts.map +1 -0
  343. package/lib/typescript/src/haptics/resolver.d.ts +22 -0
  344. package/lib/typescript/src/haptics/resolver.d.ts.map +1 -0
  345. package/lib/typescript/src/haptics/types.d.ts +37 -0
  346. package/lib/typescript/src/haptics/types.d.ts.map +1 -0
  347. package/lib/typescript/src/haptics.d.ts +8 -0
  348. package/lib/typescript/src/haptics.d.ts.map +1 -0
  349. package/lib/typescript/src/index.d.ts +10 -2
  350. package/lib/typescript/src/index.d.ts.map +1 -1
  351. package/lib/typescript/src/particles/createParticleSystem.d.ts +5 -0
  352. package/lib/typescript/src/particles/createParticleSystem.d.ts.map +1 -0
  353. package/lib/typescript/src/particles/defineParticleEffect.d.ts +3 -0
  354. package/lib/typescript/src/particles/defineParticleEffect.d.ts.map +1 -0
  355. package/lib/typescript/src/particles/errors.d.ts +5 -0
  356. package/lib/typescript/src/particles/errors.d.ts.map +1 -0
  357. package/lib/typescript/src/particles/index.d.ts +6 -0
  358. package/lib/typescript/src/particles/index.d.ts.map +1 -0
  359. package/lib/typescript/src/particles/sampling.d.ts +54 -0
  360. package/lib/typescript/src/particles/sampling.d.ts.map +1 -0
  361. package/lib/typescript/src/particles/types.d.ts +292 -0
  362. package/lib/typescript/src/particles/types.d.ts.map +1 -0
  363. package/lib/typescript/src/particles.d.ts +2 -0
  364. package/lib/typescript/src/particles.d.ts.map +1 -0
  365. package/lib/typescript/src/react/GameButtonPad.d.ts +84 -0
  366. package/lib/typescript/src/react/GameButtonPad.d.ts.map +1 -0
  367. package/lib/typescript/src/react/GamePointerInput.d.ts.map +1 -1
  368. package/lib/typescript/src/react/GameView.d.ts +27 -1
  369. package/lib/typescript/src/react/GameView.d.ts.map +1 -1
  370. package/lib/typescript/src/react/assets/createGameAssetStore.d.ts.map +1 -1
  371. package/lib/typescript/src/react/assets/useGameAssets.d.ts.map +1 -1
  372. package/lib/typescript/src/react/bindAppLifecycle.d.ts +7 -1
  373. package/lib/typescript/src/react/bindAppLifecycle.d.ts.map +1 -1
  374. package/lib/typescript/src/react/bindGameSession.d.ts.map +1 -1
  375. package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts +24 -0
  376. package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts.map +1 -0
  377. package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts +44 -0
  378. package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts.map +1 -0
  379. package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts +22 -0
  380. package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts.map +1 -0
  381. package/lib/typescript/src/react/cleanupList.d.ts +21 -0
  382. package/lib/typescript/src/react/cleanupList.d.ts.map +1 -0
  383. package/lib/typescript/src/react/instrumentation.d.ts +33 -3
  384. package/lib/typescript/src/react/instrumentation.d.ts.map +1 -1
  385. package/lib/typescript/src/react/lifecycleSource.d.ts +21 -0
  386. package/lib/typescript/src/react/lifecycleSource.d.ts.map +1 -0
  387. package/lib/typescript/src/react/particles/ParticleView.d.ts +65 -0
  388. package/lib/typescript/src/react/particles/ParticleView.d.ts.map +1 -0
  389. package/lib/typescript/src/react/particles/culling.d.ts +70 -0
  390. package/lib/typescript/src/react/particles/culling.d.ts.map +1 -0
  391. package/lib/typescript/src/react/particles/index.d.ts +7 -0
  392. package/lib/typescript/src/react/particles/index.d.ts.map +1 -0
  393. package/lib/typescript/src/react/particles/spriteXForm.d.ts +28 -0
  394. package/lib/typescript/src/react/particles/spriteXForm.d.ts.map +1 -0
  395. package/lib/typescript/src/react/particles/useParticlePresentation.d.ts +48 -0
  396. package/lib/typescript/src/react/particles/useParticlePresentation.d.ts.map +1 -0
  397. package/lib/typescript/src/react/pointerBinding.d.ts +38 -8
  398. package/lib/typescript/src/react/pointerBinding.d.ts.map +1 -1
  399. package/lib/typescript/src/react/pointerCamera.d.ts +20 -0
  400. package/lib/typescript/src/react/pointerCamera.d.ts.map +1 -0
  401. package/lib/typescript/src/react/pointerCoalescer.d.ts +17 -1
  402. package/lib/typescript/src/react/pointerCoalescer.d.ts.map +1 -1
  403. package/lib/typescript/src/react/sprites/GameSprite.d.ts +3 -10
  404. package/lib/typescript/src/react/sprites/GameSprite.d.ts.map +1 -1
  405. package/lib/typescript/src/react/sprites/GameWorld2D.d.ts +51 -6
  406. package/lib/typescript/src/react/sprites/GameWorld2D.d.ts.map +1 -1
  407. package/lib/typescript/src/react/sprites/Sprite.d.ts.map +1 -1
  408. package/lib/typescript/src/react/sprites/SpriteBatch.d.ts +22 -1
  409. package/lib/typescript/src/react/sprites/SpriteBatch.d.ts.map +1 -1
  410. package/lib/typescript/src/react/sprites/batchVisibility.d.ts +28 -0
  411. package/lib/typescript/src/react/sprites/batchVisibility.d.ts.map +1 -0
  412. package/lib/typescript/src/react/sprites/spriteSelection.d.ts +58 -0
  413. package/lib/typescript/src/react/sprites/spriteSelection.d.ts.map +1 -0
  414. package/lib/typescript/src/react/sprites/spriteTransform.d.ts +27 -12
  415. package/lib/typescript/src/react/sprites/spriteTransform.d.ts.map +1 -1
  416. package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts +69 -0
  417. package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts.map +1 -0
  418. package/lib/typescript/src/react/tilemap/tilePresentation.d.ts +217 -0
  419. package/lib/typescript/src/react/tilemap/tilePresentation.d.ts.map +1 -0
  420. package/lib/typescript/src/react/useGameSession.d.ts +48 -0
  421. package/lib/typescript/src/react/useGameSession.d.ts.map +1 -0
  422. package/lib/typescript/src/react/useGameSessionStatus.d.ts +23 -0
  423. package/lib/typescript/src/react/useGameSessionStatus.d.ts.map +1 -0
  424. package/lib/typescript/src/react/viewportBinding.d.ts +5 -1
  425. package/lib/typescript/src/react/viewportBinding.d.ts.map +1 -1
  426. package/lib/typescript/src/react.d.ts +18 -1
  427. package/lib/typescript/src/react.d.ts.map +1 -1
  428. package/lib/typescript/src/scene/defineScene.d.ts +22 -2
  429. package/lib/typescript/src/scene/defineScene.d.ts.map +1 -1
  430. package/lib/typescript/src/scene/types.d.ts +24 -4
  431. package/lib/typescript/src/scene/types.d.ts.map +1 -1
  432. package/lib/typescript/src/sprites/sampleSpriteClip.d.ts +26 -2
  433. package/lib/typescript/src/sprites/sampleSpriteClip.d.ts.map +1 -1
  434. package/lib/typescript/src/sprites/spriteAnimationState.d.ts +6 -7
  435. package/lib/typescript/src/sprites/spriteAnimationState.d.ts.map +1 -1
  436. package/lib/typescript/src/sprites.d.ts +10 -0
  437. package/lib/typescript/src/sprites.d.ts.map +1 -0
  438. package/lib/typescript/src/storage/adapters/asyncStorage.d.ts +18 -0
  439. package/lib/typescript/src/storage/adapters/asyncStorage.d.ts.map +1 -0
  440. package/lib/typescript/src/storage/adapters/memory.d.ts +16 -0
  441. package/lib/typescript/src/storage/adapters/memory.d.ts.map +1 -0
  442. package/lib/typescript/src/storage/errors.d.ts +37 -0
  443. package/lib/typescript/src/storage/errors.d.ts.map +1 -0
  444. package/lib/typescript/src/storage/index.d.ts +11 -0
  445. package/lib/typescript/src/storage/index.d.ts.map +1 -0
  446. package/lib/typescript/src/storage/schema.d.ts +22 -0
  447. package/lib/typescript/src/storage/schema.d.ts.map +1 -0
  448. package/lib/typescript/src/storage/serialization.d.ts +23 -0
  449. package/lib/typescript/src/storage/serialization.d.ts.map +1 -0
  450. package/lib/typescript/src/storage/store.d.ts +3 -0
  451. package/lib/typescript/src/storage/store.d.ts.map +1 -0
  452. package/lib/typescript/src/storage/types.d.ts +69 -0
  453. package/lib/typescript/src/storage/types.d.ts.map +1 -0
  454. package/lib/typescript/src/storage/validation.d.ts +24 -0
  455. package/lib/typescript/src/storage/validation.d.ts.map +1 -0
  456. package/lib/typescript/src/storage.d.ts +2 -0
  457. package/lib/typescript/src/storage.d.ts.map +1 -0
  458. package/lib/typescript/src/testing.d.ts +1 -0
  459. package/lib/typescript/src/testing.d.ts.map +1 -1
  460. package/lib/typescript/src/tilemap/chunkStats.d.ts +14 -0
  461. package/lib/typescript/src/tilemap/chunkStats.d.ts.map +1 -0
  462. package/lib/typescript/src/tilemap/definitions.d.ts +38 -0
  463. package/lib/typescript/src/tilemap/definitions.d.ts.map +1 -0
  464. package/lib/typescript/src/tilemap/errors.d.ts +7 -0
  465. package/lib/typescript/src/tilemap/errors.d.ts.map +1 -0
  466. package/lib/typescript/src/tilemap/index.d.ts +8 -0
  467. package/lib/typescript/src/tilemap/index.d.ts.map +1 -0
  468. package/lib/typescript/src/tilemap/movement.d.ts +52 -0
  469. package/lib/typescript/src/tilemap/movement.d.ts.map +1 -0
  470. package/lib/typescript/src/tilemap/queries.d.ts +21 -0
  471. package/lib/typescript/src/tilemap/queries.d.ts.map +1 -0
  472. package/lib/typescript/src/tilemap/tiledAdapter.d.ts +32 -0
  473. package/lib/typescript/src/tilemap/tiledAdapter.d.ts.map +1 -0
  474. package/lib/typescript/src/tilemap/types.d.ts +127 -0
  475. package/lib/typescript/src/tilemap/types.d.ts.map +1 -0
  476. package/lib/typescript/src/tilemap.d.ts +2 -0
  477. package/lib/typescript/src/tilemap.d.ts.map +1 -0
  478. package/package.json +100 -10
  479. package/src/assets/defineAssets.ts +31 -5
  480. package/src/assets.ts +27 -0
  481. package/src/audio/createGameAudio.ts +749 -0
  482. package/src/audio/errors.ts +12 -0
  483. package/src/audio/index.ts +9 -0
  484. package/src/audio/resolver.ts +74 -0
  485. package/src/audio/types.ts +47 -0
  486. package/src/audio.ts +13 -0
  487. package/src/camera2d/bounds.ts +69 -0
  488. package/src/camera2d/extents.ts +25 -0
  489. package/src/camera2d/follow.ts +165 -0
  490. package/src/camera2d/index.ts +29 -0
  491. package/src/camera2d/interpolation.ts +113 -0
  492. package/src/camera2d/shake.ts +90 -0
  493. package/src/camera2d/transform.ts +148 -0
  494. package/src/camera2d/types.ts +73 -0
  495. package/src/camera2d/validation.ts +126 -0
  496. package/src/camera2d/visibility.ts +181 -0
  497. package/src/camera2d.ts +6 -0
  498. package/src/collision2d/colliders.ts +292 -0
  499. package/src/collision2d/debug.ts +133 -0
  500. package/src/collision2d/filters.ts +48 -0
  501. package/src/collision2d/index.ts +74 -0
  502. package/src/collision2d/intersections.ts +82 -0
  503. package/src/collision2d/manifolds.ts +191 -0
  504. package/src/collision2d/segments.ts +176 -0
  505. package/src/collision2d/spatialHash.ts +211 -0
  506. package/src/collision2d/sweeps.ts +269 -0
  507. package/src/collision2d.ts +6 -0
  508. package/src/core/input/buttonPad.ts +235 -0
  509. package/src/core/input/createInputBuffer.ts +95 -14
  510. package/src/core/input/types.ts +7 -0
  511. package/src/core/session/createGameSession.ts +480 -53
  512. package/src/core/session/deepFreeze.ts +104 -3
  513. package/src/core/session/diagnostics.ts +2 -0
  514. package/src/core/session/types.ts +77 -4
  515. package/src/definition/defineGame.ts +61 -3
  516. package/src/definition/types.ts +10 -0
  517. package/src/events/defineGameEvents.ts +46 -0
  518. package/src/events/errors.ts +14 -0
  519. package/src/events/index.ts +14 -0
  520. package/src/events/payload.ts +234 -0
  521. package/src/events/seed.ts +33 -0
  522. package/src/events/types.ts +53 -0
  523. package/src/events.ts +20 -0
  524. package/src/geometry/helpers.ts +136 -0
  525. package/src/geometry/index.ts +14 -0
  526. package/src/geometry/types.ts +54 -0
  527. package/src/geometry/validation.ts +131 -0
  528. package/src/geometry.ts +6 -0
  529. package/src/haptics/createGameHaptics.ts +214 -0
  530. package/src/haptics/errors.ts +12 -0
  531. package/src/haptics/index.ts +3 -0
  532. package/src/haptics/resolver.ts +58 -0
  533. package/src/haptics/types.ts +48 -0
  534. package/src/haptics.ts +7 -0
  535. package/src/index.ts +37 -1
  536. package/src/particles/createParticleSystem.ts +505 -0
  537. package/src/particles/defineParticleEffect.ts +164 -0
  538. package/src/particles/errors.ts +6 -0
  539. package/src/particles/index.ts +5 -0
  540. package/src/particles/sampling.ts +141 -0
  541. package/src/particles/types.ts +327 -0
  542. package/src/particles.ts +1 -0
  543. package/src/react/GameButtonPad.tsx +436 -0
  544. package/src/react/GamePointerInput.tsx +73 -8
  545. package/src/react/GameView.tsx +160 -61
  546. package/src/react/assets/createGameAssetStore.ts +140 -107
  547. package/src/react/assets/useGameAssets.ts +63 -31
  548. package/src/react/bindAppLifecycle.ts +55 -19
  549. package/src/react/bindGameSession.ts +12 -2
  550. package/src/react/camera2d/GameLayer2D.tsx +43 -0
  551. package/src/react/camera2d/defineGameCamera2D.ts +59 -0
  552. package/src/react/camera2d/usePresentedCameraBinding.ts +185 -0
  553. package/src/react/cleanupList.ts +48 -0
  554. package/src/react/instrumentation.ts +31 -3
  555. package/src/react/lifecycleSource.ts +58 -0
  556. package/src/react/particles/ParticleView.tsx +358 -0
  557. package/src/react/particles/culling.ts +145 -0
  558. package/src/react/particles/index.ts +6 -0
  559. package/src/react/particles/spriteXForm.ts +72 -0
  560. package/src/react/particles/useParticlePresentation.ts +261 -0
  561. package/src/react/pointerBinding.ts +74 -16
  562. package/src/react/pointerCamera.ts +33 -0
  563. package/src/react/pointerCoalescer.ts +36 -10
  564. package/src/react/sprites/GameSprite.tsx +4 -33
  565. package/src/react/sprites/GameWorld2D.tsx +113 -7
  566. package/src/react/sprites/Sprite.tsx +51 -72
  567. package/src/react/sprites/SpriteBatch.tsx +74 -22
  568. package/src/react/sprites/batchVisibility.ts +76 -0
  569. package/src/react/sprites/spriteSelection.ts +133 -0
  570. package/src/react/sprites/spriteTransform.ts +46 -26
  571. package/src/react/tilemap/TileMapLayer2D.tsx +274 -0
  572. package/src/react/tilemap/tilePresentation.ts +491 -0
  573. package/src/react/useGameSession.ts +90 -0
  574. package/src/react/useGameSessionStatus.ts +53 -0
  575. package/src/react/viewportBinding.ts +5 -1
  576. package/src/react.ts +18 -1
  577. package/src/scene/defineScene.ts +47 -5
  578. package/src/scene/types.ts +37 -3
  579. package/src/sprites/sampleSpriteClip.ts +82 -14
  580. package/src/sprites/spriteAnimationState.ts +29 -25
  581. package/src/sprites.ts +22 -0
  582. package/src/storage/adapters/asyncStorage.ts +77 -0
  583. package/src/storage/adapters/memory.ts +58 -0
  584. package/src/storage/errors.ts +88 -0
  585. package/src/storage/index.ts +18 -0
  586. package/src/storage/schema.ts +147 -0
  587. package/src/storage/serialization.ts +248 -0
  588. package/src/storage/store.ts +380 -0
  589. package/src/storage/types.ts +76 -0
  590. package/src/storage/validation.ts +102 -0
  591. package/src/storage.ts +1 -0
  592. package/src/testing.ts +4 -0
  593. package/src/tilemap/chunkStats.ts +24 -0
  594. package/src/tilemap/definitions.ts +327 -0
  595. package/src/tilemap/errors.ts +11 -0
  596. package/src/tilemap/index.ts +10 -0
  597. package/src/tilemap/movement.ts +368 -0
  598. package/src/tilemap/queries.ts +136 -0
  599. package/src/tilemap/tiledAdapter.ts +165 -0
  600. package/src/tilemap/types.ts +119 -0
  601. package/src/tilemap.ts +1 -0
@@ -4,7 +4,47 @@ import { createAnimationFrameDriver } from "../frameDriver.js";
4
4
  import { createInputBuffer } from "../input/createInputBuffer.js";
5
5
  import { GameSessionDisposedError, GameSessionLifecycleError } from "./types.js";
6
6
  import { createDeepFreeze } from "./deepFreeze.js";
7
+ import { GameEventError } from "../../events/errors.js";
8
+ import { cloneAndValidatePayload } from "../../events/payload.js";
7
9
  const DEFAULT_FIXED_STEP_MS = 1000 / 60;
10
+
11
+ /**
12
+ * A captured failure: an explicit boolean record so an arbitrary thrown
13
+ * value (including `undefined`) is never confused with "no failure".
14
+ */
15
+
16
+ const NO_FAILURE = {
17
+ failed: false
18
+ };
19
+
20
+ /** Run a closure and capture its thrown value, whatever its type. */
21
+ function captureFailure(run) {
22
+ try {
23
+ run();
24
+ return NO_FAILURE;
25
+ } catch (error) {
26
+ return {
27
+ failed: true,
28
+ value: error
29
+ };
30
+ }
31
+ }
32
+
33
+ /** Surface a captured failure, throwing `undefined` if that was the value. */
34
+ function throwFailure(record) {
35
+ if (record.failed) {
36
+ throw record.value;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Immutably compose two failures into one lossless error: an AggregateError
42
+ * with a stable message and ordered `errors`. Neither thrown value is
43
+ * mutated or wrapped away.
44
+ */
45
+ function composeFailures(first, second) {
46
+ return new AggregateError([first, second], 'Lifecycle operation failed with multiple errors');
47
+ }
8
48
  const DEFAULT_MAX_CATCH_UP_STEPS = 5;
9
49
  function now() {
10
50
  return typeof performance !== 'undefined' ? performance.now() : 0;
@@ -47,6 +87,34 @@ export function createGameSessionWithDriver(definition, options) {
47
87
  if (!(maxFrameDeltaMs > 0) || !Number.isFinite(maxFrameDeltaMs)) {
48
88
  throw new RangeError('maxFrameDeltaMs must be a finite positive number');
49
89
  }
90
+
91
+ // T13: event definitions and per-scene emit declarations.
92
+ const eventDefs = definition.events;
93
+ const hasEvents = eventDefs !== undefined && eventDefs !== null;
94
+ const allowedEventNames = hasEvents ? new Set(Object.keys(eventDefs)) : new Set();
95
+ const allowedEmitsByScene = new Map();
96
+ // Validate per-scene emits are declared events and that the scene's `events` reference matches the game's.
97
+ for (const [name, scene] of Object.entries(definition.scenes)) {
98
+ const erased = scene;
99
+ const emits = erased.emits ?? [];
100
+ for (const ev of emits) {
101
+ if (!allowedEventNames.has(ev)) {
102
+ throw new Error(`Scene "${name}" declares unknown event "${ev}"`);
103
+ }
104
+ }
105
+ allowedEmitsByScene.set(name, new Set(emits));
106
+ // Runtime identity check for the branded event definitions.
107
+ if (erased.events !== undefined) {
108
+ if (!hasEvents) {
109
+ throw new Error(`Scene "${name}" is bound to events but the game has no events map`);
110
+ }
111
+ if (erased.events !== eventDefs) {
112
+ throw new Error(`Scene "${name}" is bound to a different events object than the game. Pass the same defineGameEvents() result to both defineScene({ events }) and defineGame({ events }).`);
113
+ }
114
+ } else if (emits.length > 0) {
115
+ throw new Error(`Scene "${name}" declares emits but is not bound to the game's events. Pass \`events\` to defineScene.`);
116
+ }
117
+ }
50
118
  const erasedSceneMap = {};
51
119
  for (const [name, scene] of Object.entries(definition.scenes)) {
52
120
  const erased = scene;
@@ -74,16 +142,24 @@ export function createGameSessionWithDriver(definition, options) {
74
142
  try {
75
143
  initialSnapshot = deepFreeze(initialDefinition.snapshot({
76
144
  state: initialState
77
- }));
145
+ }), {
146
+ scene: initialSceneName
147
+ });
78
148
  } catch (error) {
79
- initialDefinition.dispose?.(initialState);
149
+ // GS-SCENE-02: the cleanup failure must not replace the snapshot
150
+ // failure — compose losslessly in a stable order instead.
151
+ const cleanupFailure = captureFailure(() => initialDefinition.dispose?.(initialState));
152
+ if (cleanupFailure.failed) {
153
+ throw composeFailures(error, cleanupFailure.value);
154
+ }
80
155
  throw error;
81
156
  }
82
157
  let activeScene = {
83
158
  name: initialSceneName,
84
159
  definition: initialDefinition,
85
160
  state: initialState,
86
- sceneTick: 0
161
+ sceneTick: 0,
162
+ disposalAttempted: false
87
163
  };
88
164
  let sceneName = initialSceneName;
89
165
  let currentSnapshot = initialSnapshot;
@@ -95,6 +171,12 @@ export function createGameSessionWithDriver(definition, options) {
95
171
  let accumulatorMs = 0;
96
172
  let tick = 0;
97
173
  let pendingTransition;
174
+ /**
175
+ * Set when an outgoing scene disposal throws: the retained scene is
176
+ * explicitly non-resumable — `start()` refuses to update it until a
177
+ * transition or restart replaces it. Cleared on every successful install.
178
+ */
179
+ let sceneFaulted = false;
98
180
  let hardCutPending = false;
99
181
  let publishedThisCallback = false;
100
182
  let updateInProgress = false;
@@ -110,13 +192,138 @@ export function createGameSessionWithDriver(definition, options) {
110
192
  stepMs: fixedStepMs
111
193
  });
112
194
  const listeners = new Set();
195
+ const statusListeners = new Set();
196
+ let deliveringStatus = false;
197
+ const queuedStatuses = [];
198
+ let releaseStatusListeners = false;
199
+
200
+ // T13: per-session event listeners.
201
+ const eventListeners = new Map();
202
+
203
+ // Centralized, non-recursive error sink for game event listeners.
204
+ // A failing reporter must not pause the session or escape into the fixed-step loop.
205
+ const reportGameEventListenerError = (name, error) => {
206
+ try {
207
+ console.error(`Game event listener for "${name}" threw:`, error);
208
+ } catch {
209
+ // Swallow reporter failures — the session must remain running and siblings must still run.
210
+ }
211
+ };
212
+ const deliverEvents = envelopes => {
213
+ // Snapshot per tick: a listener added during this tick's delivery receives
214
+ // only later ticks, and a removal prevents only future ticks.
215
+ const snapshots = new Map();
216
+ for (const env of envelopes) {
217
+ if (!snapshots.has(env.name)) {
218
+ const set = eventListeners.get(env.name);
219
+ snapshots.set(env.name, set ? [...set] : []);
220
+ }
221
+ }
222
+ for (const envelope of envelopes) {
223
+ const snapshot = snapshots.get(envelope.name) ?? [];
224
+ for (const listener of snapshot) {
225
+ let result;
226
+ try {
227
+ result = listener(envelope);
228
+ } catch (error) {
229
+ reportGameEventListenerError(envelope.name, error);
230
+ continue;
231
+ }
232
+ if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
233
+ result.catch(error => {
234
+ reportGameEventListenerError(envelope.name, error);
235
+ });
236
+ }
237
+ }
238
+ }
239
+ };
240
+ const NO_EVENT_EMITTER = Object.freeze({
241
+ emit() {
242
+ throw new GameEventError('This game has no events or this scene does not emit any events');
243
+ }
244
+ });
245
+
246
+ // Deliver one transition pass to a snapshot. A throwing listener never
247
+ // aborts the pass: the remaining snapshot listeners still run, and the
248
+ // first failure is rethrown once every queued transition has been
249
+ // delivered (the transition itself is already authoritative).
250
+ const deliverStatusPass = current => {
251
+ let firstFailure = NO_FAILURE;
252
+ for (const listener of [...statusListeners]) {
253
+ try {
254
+ listener(current);
255
+ } catch (error) {
256
+ if (!firstFailure.failed) {
257
+ firstFailure = {
258
+ failed: true,
259
+ value: error
260
+ };
261
+ }
262
+ }
263
+ }
264
+ return firstFailure;
265
+ };
266
+ const notifyStatus = () => {
267
+ if (deliveringStatus) {
268
+ // Re-entrant transition from inside a listener: queue it so every
269
+ // listener observes complete states, never a half-applied transition.
270
+ queuedStatuses.push(status);
271
+ return;
272
+ }
273
+ deliveringStatus = true;
274
+ let firstFailure = NO_FAILURE;
275
+ try {
276
+ const passFailure = deliverStatusPass(status);
277
+ if (!firstFailure.failed) {
278
+ firstFailure = passFailure;
279
+ }
280
+ while (queuedStatuses.length > 0) {
281
+ const queued = queuedStatuses.shift();
282
+ const queuedFailure = deliverStatusPass(queued);
283
+ if (!firstFailure.failed) {
284
+ firstFailure = queuedFailure;
285
+ }
286
+ }
287
+ } finally {
288
+ deliveringStatus = false;
289
+ if (releaseStatusListeners) {
290
+ releaseStatusListeners = false;
291
+ statusListeners.clear();
292
+ diagnostics?.onStatusListenerCount?.(statusListeners.size);
293
+ }
294
+ }
295
+ throwFailure(firstFailure);
296
+ };
297
+
298
+ // Status becomes authoritative only after the command's scheduling and
299
+ // input side effects are safe; listeners then observe the committed value.
300
+ const setStatus = next => {
301
+ if (status === next) {
302
+ return;
303
+ }
304
+ status = next;
305
+ notifyStatus();
306
+ };
307
+
308
+ // Error-path pause: complete the transition and notify, but never let a
309
+ // status-listener failure mask the original error that caused the pause.
310
+ const pauseAndRethrow = original => {
311
+ try {
312
+ pauseInternal();
313
+ } catch {
314
+ // The original error is the actionable one; listeners were notified.
315
+ }
316
+ throw original;
317
+ };
113
318
  const assertLive = () => {
114
319
  if (status === 'disposed') {
115
320
  throw new GameSessionDisposedError();
116
321
  }
117
322
  };
118
323
  const isDisposed = () => status === 'disposed';
119
- const inputBuffer = createInputBuffer(definition.input, assertLive);
324
+ // T10.4: gameplay input is rejected at the shared boundary while paused
325
+ // (before the queue and before the accepted-input diagnostics).
326
+ const inputBuffer = createInputBuffer(definition.input, assertLive, () => status === 'paused');
120
327
 
121
328
  // Simulation commits are the only presentation updates (T5): a commit
122
329
  // happens on the initial baseline, after every presentation callback that
@@ -152,7 +359,6 @@ export function createGameSessionWithDriver(definition, options) {
152
359
  if (status !== 'running') {
153
360
  return;
154
361
  }
155
- status = 'paused';
156
362
  generation += 1;
157
363
  if (frameHandle !== undefined) {
158
364
  options.frameDriver.cancelFrame(frameHandle);
@@ -161,16 +367,29 @@ export function createGameSessionWithDriver(definition, options) {
161
367
  previousTimestampMs = undefined;
162
368
  accumulatorMs = 0;
163
369
  inputBuffer.reset();
370
+ setStatus('paused');
164
371
  };
165
372
  const commitOrPause = () => {
166
373
  try {
167
374
  commit();
168
375
  } catch (error) {
169
- pauseInternal();
170
- throw error;
376
+ pauseAndRethrow(error);
171
377
  }
172
378
  };
173
379
 
380
+ /**
381
+ * Dispose one scene instance exactly once. The attempt flag is set before
382
+ * user cleanup runs, so a throwing disposer is never retried for this
383
+ * instance — by transition retries or by session disposal.
384
+ */
385
+ const disposeSceneInstance = (scene, state) => {
386
+ if (scene.disposalAttempted) {
387
+ return;
388
+ }
389
+ scene.disposalAttempted = true;
390
+ scene.definition.dispose?.(state);
391
+ };
392
+
174
393
  /**
175
394
  * Transition to `targetName` following the deterministic ordering:
176
395
  * prepare the target -> dispose the outgoing scene exactly once -> advance
@@ -198,7 +417,9 @@ export function createGameSessionWithDriver(definition, options) {
198
417
  targetCreated = true;
199
418
  targetSnapshot = deepFreeze(targetDefinition.snapshot({
200
419
  state: targetState
201
- }));
420
+ }), {
421
+ scene: targetName
422
+ });
202
423
  } catch (error) {
203
424
  if (targetCreated) {
204
425
  try {
@@ -210,15 +431,17 @@ export function createGameSessionWithDriver(definition, options) {
210
431
  throw error;
211
432
  }
212
433
  try {
213
- activeScene.definition.dispose?.(outgoingFinalState);
434
+ disposeSceneInstance(activeScene, outgoingFinalState);
214
435
  } catch (error) {
215
- // The outgoing scene's dispose already ran (possibly partially) and is
216
- // not retried; the old scene remains active with that honest caveat.
436
+ // The outgoing scene's dispose already ran (possibly partially) and
437
+ // is never retried; the old scene remains active but explicitly
438
+ // faulted. The prepared target is still cleaned up, and a failing
439
+ // target cleanup composes losslessly with the outgoing failure.
440
+ sceneFaulted = true;
217
441
  if (targetCreated) {
218
- try {
219
- targetDefinition.dispose?.(targetState);
220
- } catch {
221
- // Best effort.
442
+ const targetFailure = captureFailure(() => targetDefinition.dispose?.(targetState));
443
+ if (targetFailure.failed) {
444
+ throw composeFailures(error, targetFailure.value);
222
445
  }
223
446
  }
224
447
  throw error;
@@ -230,8 +453,10 @@ export function createGameSessionWithDriver(definition, options) {
230
453
  name: targetName,
231
454
  definition: targetDefinition,
232
455
  state: targetState,
233
- sceneTick: 0
456
+ sceneTick: 0,
457
+ disposalAttempted: false
234
458
  };
459
+ sceneFaulted = false;
235
460
  sceneName = targetName;
236
461
  currentSnapshot = targetSnapshot;
237
462
  previousSnapshot = targetSnapshot;
@@ -290,6 +515,30 @@ export function createGameSessionWithDriver(definition, options) {
290
515
  }
291
516
  });
292
517
  };
518
+
519
+ // One scheduler-failure policy for successor scheduling (GS-SESSION-02).
520
+ // The initial `start()` path keeps its own restore-previous-status policy
521
+ // because no running state was ever published there. On failure: clear the
522
+ // handle, invalidate the generation so late callbacks are no-ops,
523
+ // neutralize input via the pause path, and publish paused while preserving
524
+ // the scheduling error — a throwing status listener composes with it
525
+ // instead of masking it. Never retries automatically.
526
+ const failScheduling = error => {
527
+ frameHandle = undefined;
528
+ try {
529
+ pauseInternal();
530
+ } catch (statusError) {
531
+ throw composeFailures(error, statusError);
532
+ }
533
+ throw error;
534
+ };
535
+ const scheduleSuccessor = activeGeneration => {
536
+ try {
537
+ schedule(activeGeneration);
538
+ } catch (error) {
539
+ failScheduling(error);
540
+ }
541
+ };
293
542
  const schedule = activeGeneration => {
294
543
  frameHandle = options.frameDriver.requestFrame(timestampMs => {
295
544
  if (status !== 'running' || generation !== activeGeneration) {
@@ -301,16 +550,17 @@ export function createGameSessionWithDriver(definition, options) {
301
550
  diagnostics.onDisplayCallback();
302
551
  }
303
552
 
304
- // A pending external transition commits at the next fixed-step boundary
305
- // without advancing simulation tick/time.
553
+ // A pending external transition commits at the next frame-driver
554
+ // callback — a zero-step callback may carry it — without advancing
555
+ // simulation tick/time. The target scene first updates at the next
556
+ // due fixed step; accumulated timing debt is preserved.
306
557
  if (pendingTransition !== undefined) {
307
558
  const pending = pendingTransition;
308
559
  pendingTransition = undefined;
309
560
  try {
310
561
  commitTransition(pending, activeScene.state, undefined);
311
562
  } catch (error) {
312
- pauseInternal();
313
- throw error;
563
+ pauseAndRethrow(error);
314
564
  }
315
565
  commitOrPause();
316
566
  if (status !== 'running' || generation !== activeGeneration) {
@@ -323,7 +573,7 @@ export function createGameSessionWithDriver(definition, options) {
323
573
  commitOrPause();
324
574
  }
325
575
  if (status === 'running' && generation === activeGeneration) {
326
- schedule(activeGeneration);
576
+ scheduleSuccessor(activeGeneration);
327
577
  }
328
578
  return;
329
579
  }
@@ -338,6 +588,39 @@ export function createGameSessionWithDriver(definition, options) {
338
588
  stepsRan = true;
339
589
  const nextTick = tick + 1;
340
590
  const nextSceneTick = activeScene.sceneTick + 1;
591
+ const sceneNameForTick = activeScene.name;
592
+ const allowedEmits = allowedEmitsByScene.get(sceneNameForTick) ?? new Set();
593
+
594
+ // T13: per-tick transactional emitter.
595
+ let stagedThisTick = [];
596
+ let ordinal = 0;
597
+ let emitterInvalidated = false;
598
+ const emitter = hasEvents ? {
599
+ emit(name, payload) {
600
+ if (emitterInvalidated) {
601
+ throw new GameEventError('Game event emitter is only valid during its owning scene update');
602
+ }
603
+ if (!allowedEventNames.has(name)) {
604
+ throw new GameEventError(`Event "${name}" is not declared by this game`);
605
+ }
606
+ if (!allowedEmits.has(name)) {
607
+ throw new GameEventError(`Scene "${sceneNameForTick}" cannot emit event "${name}" (missing from its emits list)`);
608
+ }
609
+ const cloned = cloneAndValidatePayload(payload, name, 'payload');
610
+ const envelope = Object.freeze({
611
+ name,
612
+ payload: cloned,
613
+ tick: nextTick,
614
+ scene: sceneNameForTick,
615
+ sceneTick: nextSceneTick,
616
+ ordinal: ordinal++
617
+ });
618
+ stagedThisTick.push(envelope);
619
+ }
620
+ } : NO_EVENT_EMITTER;
621
+ if (hasEvents) {
622
+ Object.freeze(emitter);
623
+ }
341
624
  const sampleStart = diagnostics === undefined ? 0 : now();
342
625
  const input = inputBuffer.sample();
343
626
  if (diagnostics !== undefined) {
@@ -347,16 +630,30 @@ export function createGameSessionWithDriver(definition, options) {
347
630
  activeUpdateScope = scope;
348
631
  updateInProgress = true;
349
632
  const updateStart = diagnostics === undefined ? 0 : now();
350
- const nextState = freezeObject(activeScene.definition.update({
351
- state: activeScene.state,
352
- input,
353
- transition: makeTransitionController(scope),
354
- tick: nextTick,
355
- sceneTick: nextSceneTick,
356
- deltaSeconds: fixedStepMs / 1000,
357
- elapsedSeconds: nextTick * fixedStepMs / 1000,
358
- sceneElapsedSeconds: nextSceneTick * fixedStepMs / 1000
359
- }));
633
+ let nextState;
634
+ try {
635
+ nextState = freezeObject(
636
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
637
+ activeScene.definition.update({
638
+ state: activeScene.state,
639
+ input,
640
+ transition: makeTransitionController(scope),
641
+ events: emitter,
642
+ tick: nextTick,
643
+ sceneTick: nextSceneTick,
644
+ deltaSeconds: fixedStepMs / 1000,
645
+ elapsedSeconds: nextTick * fixedStepMs / 1000,
646
+ sceneElapsedSeconds: nextSceneTick * fixedStepMs / 1000
647
+ }));
648
+ } catch (error) {
649
+ // Update failed: discard staged events, invalidate emitter.
650
+ stagedThisTick = [];
651
+ emitterInvalidated = true;
652
+ activeUpdateScope = undefined;
653
+ updateInProgress = false;
654
+ throw error;
655
+ }
656
+ emitterInvalidated = true;
360
657
  activeUpdateScope = undefined;
361
658
  updateInProgress = false;
362
659
  if (diagnostics !== undefined) {
@@ -370,7 +667,17 @@ export function createGameSessionWithDriver(definition, options) {
370
667
  // A transition requested during update commits after this update
371
668
  // completes successfully. The update consumed one fixed step; the
372
669
  // hard-cut frame publishes at the end of this presentation callback.
373
- commitTransition(intent, nextState, nextTick, true);
670
+ try {
671
+ commitTransition(intent, nextState, nextTick, true);
672
+ } catch (error) {
673
+ // Transition preparation/disposal failed: discard staged events.
674
+ stagedThisTick = [];
675
+ throw error;
676
+ }
677
+ // Successful transition tick: staged events commit with source tick/scene.
678
+ if (stagedThisTick.length > 0) {
679
+ deliverEvents(stagedThisTick);
680
+ }
374
681
  accumulatorMs = Math.max(0, accumulatorMs - fixedStepMs);
375
682
  // F4: the transition consumed one fixed step; report it as such
376
683
  // and never as a zero-step callback (zero-step fires only when
@@ -381,14 +688,29 @@ export function createGameSessionWithDriver(definition, options) {
381
688
  break;
382
689
  }
383
690
  const snapshotStart = diagnostics === undefined ? 0 : now();
384
- const rawSnapshot = activeScene.definition.snapshot({
385
- state: nextState
386
- });
691
+ let rawSnapshot;
692
+ try {
693
+ rawSnapshot = activeScene.definition.snapshot({
694
+ state: nextState
695
+ });
696
+ } catch (error) {
697
+ stagedThisTick = [];
698
+ throw error;
699
+ }
387
700
  if (diagnostics !== undefined) {
388
701
  diagnostics.onSnapshot(now() - snapshotStart);
389
702
  }
390
703
  const freezeStart = diagnostics === undefined ? 0 : now();
391
- const nextSnapshot = deepFreeze(rawSnapshot);
704
+ let nextSnapshot;
705
+ try {
706
+ nextSnapshot = deepFreeze(rawSnapshot, {
707
+ scene: sceneNameForTick,
708
+ tick: nextTick
709
+ });
710
+ } catch (error) {
711
+ stagedThisTick = [];
712
+ throw error;
713
+ }
392
714
  if (diagnostics !== undefined) {
393
715
  diagnostics.onDeepFreeze(now() - freezeStart);
394
716
  }
@@ -400,6 +722,10 @@ export function createGameSessionWithDriver(definition, options) {
400
722
  previousSnapshot = currentSnapshot;
401
723
  currentSnapshot = nextSnapshot;
402
724
  tick = nextTick;
725
+ // Successful tick: deliver staged events in tick order, then emission order.
726
+ if (stagedThisTick.length > 0) {
727
+ deliverEvents(stagedThisTick);
728
+ }
403
729
  accumulatorMs = Math.max(0, accumulatorMs - fixedStepMs);
404
730
  catchUpSteps += 1;
405
731
  if (diagnostics !== undefined) {
@@ -412,8 +738,7 @@ export function createGameSessionWithDriver(definition, options) {
412
738
  } catch (error) {
413
739
  activeUpdateScope = undefined;
414
740
  updateInProgress = false;
415
- pauseInternal();
416
- throw error;
741
+ pauseAndRethrow(error);
417
742
  }
418
743
  if (catchUpSteps === maxCatchUpSteps && accumulatorMs >= fixedStepMs) {
419
744
  const droppedSteps = Math.floor(accumulatorMs / fixedStepMs);
@@ -435,7 +760,7 @@ export function createGameSessionWithDriver(definition, options) {
435
760
  commitOrPause();
436
761
  }
437
762
  if (status === 'running' && generation === activeGeneration) {
438
- schedule(activeGeneration);
763
+ scheduleSuccessor(activeGeneration);
439
764
  }
440
765
  });
441
766
  };
@@ -453,21 +778,41 @@ export function createGameSessionWithDriver(definition, options) {
453
778
  if (status === 'running') {
454
779
  return;
455
780
  }
456
- status = 'running';
781
+ if (sceneFaulted) {
782
+ throw new GameSessionLifecycleError('The active scene failed disposal and is explicitly non-resumable: ' + 'replace it with setScene() or restartScene() before start(), or dispose the session.');
783
+ }
784
+ const previous = status;
457
785
  previousTimestampMs = undefined;
458
786
  accumulatorMs = 0;
459
787
  const activeGeneration = ++generation;
460
- schedule(activeGeneration);
788
+ try {
789
+ schedule(activeGeneration);
790
+ } catch (error) {
791
+ // Do not publish `running` when scheduling could not be established.
792
+ status = previous;
793
+ throw error;
794
+ }
795
+ setStatus('running');
461
796
  },
462
797
  pause() {
463
798
  assertLive();
464
- pauseInternal();
465
- if (pendingTransition !== undefined) {
466
- const pending = pendingTransition;
467
- pendingTransition = undefined;
468
- commitTransition(pending, activeScene.state, undefined);
469
- commitOrPause();
799
+ const pauseFailure = captureFailure(() => pauseInternal());
800
+ // The command must complete: a pending external transition is resolved
801
+ // at the pause boundary even when a status listener threw, so a later
802
+ // start() can never commit a stale transition on an unexpected frame.
803
+ const transitionFailure = captureFailure(() => {
804
+ if (pendingTransition !== undefined) {
805
+ const pending = pendingTransition;
806
+ pendingTransition = undefined;
807
+ commitTransition(pending, activeScene.state, undefined);
808
+ commitOrPause();
809
+ }
810
+ });
811
+ if (pauseFailure.failed && transitionFailure.failed) {
812
+ throw composeFailures(pauseFailure.value, transitionFailure.value);
470
813
  }
814
+ throwFailure(pauseFailure);
815
+ throwFailure(transitionFailure);
471
816
  },
472
817
  setScene(name) {
473
818
  assertLive();
@@ -518,14 +863,29 @@ export function createGameSessionWithDriver(definition, options) {
518
863
  options.frameDriver.cancelFrame(frameHandle);
519
864
  frameHandle = undefined;
520
865
  }
521
- status = 'disposed';
522
866
  generation += 1;
523
867
  previousTimestampMs = undefined;
524
868
  accumulatorMs = 0;
525
869
  pendingTransition = undefined;
526
870
  inputBuffer.reset();
527
- listeners.clear();
528
- activeScene.definition.dispose?.(activeScene.state);
871
+ eventListeners.clear();
872
+ // Terminal cleanup is unconditional: `disposed` is delivered to the
873
+ // full listener snapshot (including the re-entrant queued pass), then
874
+ // every listener and the scene are released exactly once even when a
875
+ // status listener throws. The release flag is set BEFORE the
876
+ // notification so notifyStatus's finally performs the clear; for the
877
+ // re-entrant path the clear happens after the queued pass drains.
878
+ releaseStatusListeners = true;
879
+ const notificationFailure = captureFailure(() => setStatus('disposed'));
880
+ const cleanupFailure = captureFailure(() => {
881
+ listeners.clear();
882
+ disposeSceneInstance(activeScene, activeScene.state);
883
+ });
884
+ if (notificationFailure.failed && cleanupFailure.failed) {
885
+ throw composeFailures(notificationFailure.value, cleanupFailure.value);
886
+ }
887
+ throwFailure(notificationFailure);
888
+ throwFailure(cleanupFailure);
529
889
  },
530
890
  getRenderFrame() {
531
891
  const alpha = commitFrame.hardCut ? 0 : Math.max(0, Math.min(accumulatorMs / fixedStepMs, 1 - Number.EPSILON));
@@ -547,6 +907,50 @@ export function createGameSessionWithDriver(definition, options) {
547
907
  listeners.delete(listener);
548
908
  }
549
909
  });
910
+ },
911
+ addStatusListener(listener) {
912
+ assertLive();
913
+ statusListeners.add(listener);
914
+ diagnostics?.onStatusListenerCount?.(statusListeners.size);
915
+ let removed = false;
916
+ return Object.freeze({
917
+ remove() {
918
+ if (removed) {
919
+ return;
920
+ }
921
+ removed = true;
922
+ statusListeners.delete(listener);
923
+ diagnostics?.onStatusListenerCount?.(statusListeners.size);
924
+ }
925
+ });
926
+ },
927
+ addGameEventListener: (name, listener) => {
928
+ assertLive();
929
+ if (!hasEvents || !allowedEventNames.has(name)) {
930
+ throw new GameEventError(`Event "${String(name)}" is not declared by this game`);
931
+ }
932
+ let set = eventListeners.get(name);
933
+ if (set === undefined) {
934
+ set = new Set();
935
+ eventListeners.set(name, set);
936
+ }
937
+ set.add(listener);
938
+ let removed = false;
939
+ return Object.freeze({
940
+ remove() {
941
+ if (removed) {
942
+ return;
943
+ }
944
+ removed = true;
945
+ const existing = eventListeners.get(name);
946
+ if (existing) {
947
+ existing.delete(listener);
948
+ if (existing.size === 0) {
949
+ eventListeners.delete(name);
950
+ }
951
+ }
952
+ }
953
+ });
550
954
  }
551
955
  };
552
956
  return Object.freeze(session);