@colyseus/core 0.15.45 → 0.16.0-alpha.21

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 (103) hide show
  1. package/README.md +5 -5
  2. package/build/IPC.js.map +1 -1
  3. package/build/IPC.mjs.map +1 -1
  4. package/build/MatchMaker.d.ts +20 -15
  5. package/build/MatchMaker.js +63 -78
  6. package/build/MatchMaker.js.map +2 -2
  7. package/build/MatchMaker.mjs +62 -75
  8. package/build/MatchMaker.mjs.map +2 -2
  9. package/build/Protocol.d.ts +3 -9
  10. package/build/Protocol.js +35 -95
  11. package/build/Protocol.js.map +2 -2
  12. package/build/Protocol.mjs +34 -92
  13. package/build/Protocol.mjs.map +2 -2
  14. package/build/Room.d.ts +22 -27
  15. package/build/Room.js +55 -67
  16. package/build/Room.js.map +3 -3
  17. package/build/Room.mjs +56 -68
  18. package/build/Room.mjs.map +2 -2
  19. package/build/Server.d.ts +0 -1
  20. package/build/Server.js +4 -4
  21. package/build/Server.js.map +2 -2
  22. package/build/Server.mjs +3 -3
  23. package/build/Server.mjs.map +2 -2
  24. package/build/Transport.d.ts +37 -17
  25. package/build/Transport.js.map +2 -2
  26. package/build/Transport.mjs.map +2 -2
  27. package/build/index.d.ts +3 -3
  28. package/build/index.js +3 -3
  29. package/build/index.js.map +2 -2
  30. package/build/index.mjs +3 -3
  31. package/build/index.mjs.map +2 -2
  32. package/build/matchmaker/Lobby.d.ts +2 -2
  33. package/build/matchmaker/Lobby.js.map +2 -2
  34. package/build/matchmaker/Lobby.mjs.map +2 -2
  35. package/build/matchmaker/RegisteredHandler.d.ts +4 -5
  36. package/build/matchmaker/RegisteredHandler.js +4 -3
  37. package/build/matchmaker/RegisteredHandler.js.map +2 -2
  38. package/build/matchmaker/RegisteredHandler.mjs +4 -3
  39. package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
  40. package/build/matchmaker/controller.d.ts +1 -2
  41. package/build/matchmaker/driver/RoomData.d.ts +3 -3
  42. package/build/matchmaker/driver/RoomData.js +3 -3
  43. package/build/matchmaker/driver/RoomData.js.map +2 -2
  44. package/build/matchmaker/driver/RoomData.mjs +2 -2
  45. package/build/matchmaker/driver/RoomData.mjs.map +2 -2
  46. package/build/matchmaker/driver/api.d.ts +104 -0
  47. package/build/matchmaker/driver/api.js +29 -0
  48. package/build/matchmaker/driver/api.js.map +7 -0
  49. package/build/matchmaker/driver/api.mjs +6 -0
  50. package/build/matchmaker/driver/api.mjs.map +7 -0
  51. package/build/matchmaker/driver/index.d.ts +7 -7
  52. package/build/matchmaker/driver/index.js +1 -1
  53. package/build/matchmaker/driver/index.js.map +2 -2
  54. package/build/matchmaker/driver/index.mjs +2 -2
  55. package/build/matchmaker/driver/index.mjs.map +2 -2
  56. package/build/matchmaker/driver/interfaces.d.ts +7 -11
  57. package/build/matchmaker/driver/interfaces.js.map +1 -1
  58. package/build/matchmaker/driver/local/LocalDriver.d.ts +13 -0
  59. package/build/matchmaker/driver/local/LocalDriver.js +65 -0
  60. package/build/matchmaker/driver/local/LocalDriver.js.map +7 -0
  61. package/build/matchmaker/driver/local/LocalDriver.mjs +42 -0
  62. package/build/matchmaker/driver/local/LocalDriver.mjs.map +7 -0
  63. package/build/matchmaker/driver/local/Query.d.ts +9 -0
  64. package/build/matchmaker/driver/local/Query.js +78 -0
  65. package/build/matchmaker/driver/local/Query.js.map +7 -0
  66. package/build/matchmaker/driver/local/Query.mjs +55 -0
  67. package/build/matchmaker/driver/local/Query.mjs.map +7 -0
  68. package/build/matchmaker/driver/local/RoomData.d.ts +19 -0
  69. package/build/matchmaker/driver/local/RoomData.js +79 -0
  70. package/build/matchmaker/driver/local/RoomData.js.map +7 -0
  71. package/build/matchmaker/driver/local/RoomData.mjs +56 -0
  72. package/build/matchmaker/driver/local/RoomData.mjs.map +7 -0
  73. package/build/presence/LocalPresence.d.ts +6 -1
  74. package/build/presence/LocalPresence.js +61 -0
  75. package/build/presence/LocalPresence.js.map +3 -3
  76. package/build/presence/LocalPresence.mjs +61 -0
  77. package/build/presence/LocalPresence.mjs.map +3 -3
  78. package/build/presence/Presence.d.ts +6 -0
  79. package/build/presence/Presence.js.map +1 -1
  80. package/build/rooms/LobbyRoom.d.ts +4 -4
  81. package/build/rooms/LobbyRoom.js.map +2 -2
  82. package/build/rooms/LobbyRoom.mjs.map +2 -2
  83. package/build/rooms/RelayRoom.js +2 -3
  84. package/build/rooms/RelayRoom.js.map +2 -2
  85. package/build/rooms/RelayRoom.mjs +3 -4
  86. package/build/rooms/RelayRoom.mjs.map +2 -2
  87. package/build/serializer/SchemaSerializer.d.ts +14 -7
  88. package/build/serializer/SchemaSerializer.js +66 -38
  89. package/build/serializer/SchemaSerializer.js.map +2 -2
  90. package/build/serializer/SchemaSerializer.mjs +67 -39
  91. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  92. package/build/serializer/Serializer.d.ts +2 -2
  93. package/build/serializer/Serializer.js.map +1 -1
  94. package/build/utils/DevMode.js +1 -1
  95. package/build/utils/DevMode.js.map +2 -2
  96. package/build/utils/DevMode.mjs +2 -2
  97. package/build/utils/DevMode.mjs.map +2 -2
  98. package/build/utils/Utils.d.ts +2 -9
  99. package/build/utils/Utils.js +7 -4
  100. package/build/utils/Utils.js.map +2 -2
  101. package/build/utils/Utils.mjs +5 -3
  102. package/build/utils/Utils.mjs.map +2 -2
  103. package/package.json +5 -6
package/README.md CHANGED
@@ -7,19 +7,19 @@
7
7
  <a href="https://npmjs.com/package/colyseus">
8
8
  <img src="https://img.shields.io/npm/dm/colyseus.svg?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAHdElNRQfjAgETESWYxR33AAAAtElEQVQoz4WQMQrCQBRE38Z0QoTcwF4Qg1h4BO0sxGOk80iCtViksrIQRRBTewWxMI1mbELYjYu+4rPMDPtn12ChMT3gavb4US5Jym0tcBIta3oDHv4Gwmr7nC4QAxBrCdzM2q6XqUnm9m9r59h7Rc0n2pFv24k4ttGMUXW+sGELTJjSr7QDKuqLS6UKFChVWWuFkZw9Z2AAvAirKT+JTlppIRnd6XgaP4goefI2Shj++OnjB3tBmHYK8z9zAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTAyLTAxVDE4OjE3OjM3KzAxOjAwGQQixQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wMi0wMVQxODoxNzozNyswMTowMGhZmnkAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC">
9
9
  </a>
10
- <a href="https://github.com/colyseus/colyseus/discussions" title="Discuss on Forum">
11
- <img src="https://img.shields.io/badge/discuss-on%20forum-brightgreen.svg?style=for-the-badge&colorB=0069b8&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAHdElNRQfjAgETDROxCNUzAAABB0lEQVQoz4WRvyvEARjGP193CnWRH+dHQmGwKZtFGcSmxHAL400GN95ktIpV2dzlLzDJgsGgGNRdDAzoQueS/PgY3HXHyT3T+/Y87/s89UANBKXBdoZo5J6L4K1K5ZxHfnjnlQUf3bKvkgy57a0r9hS3cXfMO1kWJMza++tj3Ac7/LY343x1NA9cNmYMwnSS/SP8JVFuSJmr44iFqvtmpjhmhBCrOOazCesq6H4P3bPBjFoIBydOk2bUA17I080Es+wSZ51B4DIA2zgjSpYcEe44Js01G0XjRcCU+y4ZMrDeLmfc9EnVd5M/o0VMeu6nJZxWJivLmhyw1WHTvrr2b4+2OFqra+ALwouTMDcqmjMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDItMDFUMTg6MTM6MTkrMDE6MDAC9f6fAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTAyLTAxVDE4OjEzOjE5KzAxOjAwc6hGIwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAASUVORK5CYII=" alt="Discussion forum" />
12
- </a>
13
10
  <a href="http://chat.colyseus.io">
14
11
  <img src="https://img.shields.io/discord/525739117951320081.svg?style=for-the-badge&colorB=7581dc&logo=discord&logoColor=white">
15
12
  </a>
13
+ <a href="https://github.com/colyseus/colyseus/discussions" title="Discuss on Forum">
14
+ <img src="https://img.shields.io/badge/discuss-on%20forum-brightgreen.svg?style=for-the-badge&colorB=0069b8&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAHdElNRQfjAgETDROxCNUzAAABB0lEQVQoz4WRvyvEARjGP193CnWRH+dHQmGwKZtFGcSmxHAL400GN95ktIpV2dzlLzDJgsGgGNRdDAzoQueS/PgY3HXHyT3T+/Y87/s89UANBKXBdoZo5J6L4K1K5ZxHfnjnlQUf3bKvkgy57a0r9hS3cXfMO1kWJMza++tj3Ac7/LY343x1NA9cNmYMwnSS/SP8JVFuSJmr44iFqvtmpjhmhBCrOOazCesq6H4P3bPBjFoIBydOk2bUA17I080Es+wSZ51B4DIA2zgjSpYcEe44Js01G0XjRcCU+y4ZMrDeLmfc9EnVd5M/o0VMeu6nJZxWJivLmhyw1WHTvrr2b4+2OFqra+ALwouTMDcqmjMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDItMDFUMTg6MTM6MTkrMDE6MDAC9f6fAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTAyLTAxVDE4OjEzOjE5KzAxOjAwc6hGIwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAASUVORK5CYII=" alt="Discussion forum" />
15
+ </a>
16
16
  <h3>
17
17
  Multiplayer Framework for Node.js. <br /><a href="https://docs.colyseus.io/">View documentation</a>
18
18
  </h3>
19
19
  </div>
20
20
 
21
- Colyseus is an Authoritative Multiplayer Framework for Node.js, with clients
22
- available for the Web, Unity3d, Defold, Haxe, and Cocos. ([See official clients](#%EF%B8%8F-official-client-integration))
21
+ Colyseus is an Authoritative Multiplayer Framework for Node.js, with SDKs
22
+ available for the Web, Unity, Defold, Haxe, Cocos and Construct3. ([See official SDKs](https://docs.colyseus.io/client/))
23
23
 
24
24
  The project focuses on providing synchronizable data structures for realtime and
25
25
  turn-based games, matchmaking, and ease of usage both on the server-side and
package/build/IPC.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/IPC.ts"],
4
- "sourcesContent": ["import { debugAndPrintError } from './Debug';\nimport { Presence } from './presence/Presence';\nimport { IpcProtocol } from './Protocol';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils';\n\nexport async function requestFromIPC<T>(\n presence: Presence,\n publishToChannel: string,\n method: string,\n args: any[],\n rejectionTimeout: number = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let unsubscribeTimeout: NodeJS.Timer;\n\n const requestId = generateId();\n const channel = `ipc:${requestId}`;\n\n const unsubscribe = () => {\n presence.unsubscribe(channel);\n clearTimeout(unsubscribeTimeout);\n };\n\n presence.subscribe(channel, (message) => {\n const [code, data] = message;\n if (code === IpcProtocol.SUCCESS) {\n resolve(data);\n\n } else if (code === IpcProtocol.ERROR) {\n let error: any = data;\n\n // parse error message + code\n try { error = JSON.parse(data) } catch (e) {}\n\n // turn string message into Error instance\n if (typeof(error) === \"string\") {\n error = new Error(error);\n }\n\n reject(error);\n }\n unsubscribe();\n });\n\n presence.publish(publishToChannel, [method, requestId, args]);\n\n unsubscribeTimeout = setTimeout(() => {\n unsubscribe();\n reject(new Error(\"ipc_timeout\"));\n }, rejectionTimeout);\n });\n}\n\nexport async function subscribeIPC(\n presence: Presence,\n processId: string,\n channel: string,\n replyCallback: (method: string, args: any[]) => any,\n) {\n await presence.subscribe(channel, (message) => {\n const [method, requestId, args] = message;\n\n const reply = (code, data) => {\n presence.publish(`ipc:${requestId}`, [code, data]);\n };\n\n // reply with method result\n let response: any;\n try {\n response = replyCallback(method, args);\n\n } catch (e) {\n debugAndPrintError(e);\n const error = (typeof(e.code) !== \"undefined\")\n ? { code: e.code, message: e.message }\n : e.message;\n return reply(IpcProtocol.ERROR, JSON.stringify(error));\n }\n\n if (!(response instanceof Promise)) {\n return reply(IpcProtocol.SUCCESS, response);\n }\n\n response.\n then((result) => reply(IpcProtocol.SUCCESS, result)).\n catch((e) => {\n // user might have called `reject()` without arguments.\n const err = e && e.message || e;\n reply(IpcProtocol.ERROR, err);\n });\n });\n}\n"],
4
+ "sourcesContent": ["import { debugAndPrintError } from './Debug';\nimport { Presence } from './presence/Presence';\nimport { IpcProtocol } from './Protocol';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils';\n\nexport async function requestFromIPC<T>(\n presence: Presence,\n publishToChannel: string,\n method: string,\n args: any[],\n rejectionTimeout: number = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let unsubscribeTimeout: NodeJS.Timeout;\n\n const requestId = generateId();\n const channel = `ipc:${requestId}`;\n\n const unsubscribe = () => {\n presence.unsubscribe(channel);\n clearTimeout(unsubscribeTimeout);\n };\n\n presence.subscribe(channel, (message) => {\n const [code, data] = message;\n if (code === IpcProtocol.SUCCESS) {\n resolve(data);\n\n } else if (code === IpcProtocol.ERROR) {\n let error: any = data;\n\n // parse error message + code\n try { error = JSON.parse(data) } catch (e) {}\n\n // turn string message into Error instance\n if (typeof(error) === \"string\") {\n error = new Error(error);\n }\n\n reject(error);\n }\n unsubscribe();\n });\n\n presence.publish(publishToChannel, [method, requestId, args]);\n\n unsubscribeTimeout = setTimeout(() => {\n unsubscribe();\n reject(new Error(\"ipc_timeout\"));\n }, rejectionTimeout);\n });\n}\n\nexport async function subscribeIPC(\n presence: Presence,\n processId: string,\n channel: string,\n replyCallback: (method: string, args: any[]) => any,\n) {\n await presence.subscribe(channel, (message) => {\n const [method, requestId, args] = message;\n\n const reply = (code, data) => {\n presence.publish(`ipc:${requestId}`, [code, data]);\n };\n\n // reply with method result\n let response: any;\n try {\n response = replyCallback(method, args);\n\n } catch (e) {\n debugAndPrintError(e);\n const error = (typeof(e.code) !== \"undefined\")\n ? { code: e.code, message: e.message }\n : e.message;\n return reply(IpcProtocol.ERROR, JSON.stringify(error));\n }\n\n if (!(response instanceof Promise)) {\n return reply(IpcProtocol.SUCCESS, response);\n }\n\n response.\n then((result) => reply(IpcProtocol.SUCCESS, result)).\n catch((e) => {\n // user might have called `reject()` without arguments.\n const err = e && e.message || e;\n reply(IpcProtocol.ERROR, err);\n });\n });\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAmC;AAEnC,sBAA4B;AAC5B,mBAAsD;AAEtD,eAAsB,eACpB,UACA,kBACA,QACA,MACA,mBAA2B,wCACf;AACZ,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,QAAI;AAEJ,UAAM,gBAAY,yBAAW;AAC7B,UAAM,UAAU,OAAO;AAEvB,UAAM,cAAc,MAAM;AACxB,eAAS,YAAY,OAAO;AAC5B,mBAAa,kBAAkB;AAAA,IACjC;AAEA,aAAS,UAAU,SAAS,CAAC,YAAY;AACvC,YAAM,CAAC,MAAM,IAAI,IAAI;AACrB,UAAI,SAAS,4BAAY,SAAS;AAChC,gBAAQ,IAAI;AAAA,MAEd,WAAW,SAAS,4BAAY,OAAO;AACrC,YAAI,QAAa;AAGjB,YAAI;AAAE,kBAAQ,KAAK,MAAM,IAAI;AAAA,QAAE,SAAS,GAAP;AAAA,QAAW;AAG5C,YAAI,OAAO,UAAW,UAAU;AAC9B,kBAAQ,IAAI,MAAM,KAAK;AAAA,QACzB;AAEA,eAAO,KAAK;AAAA,MACd;AACA,kBAAY;AAAA,IACd,CAAC;AAED,aAAS,QAAQ,kBAAkB,CAAC,QAAQ,WAAW,IAAI,CAAC;AAE5D,yBAAqB,WAAW,MAAM;AACpC,kBAAY;AACZ,aAAO,IAAI,MAAM,aAAa,CAAC;AAAA,IACjC,GAAG,gBAAgB;AAAA,EACrB,CAAC;AACH;AAEA,eAAsB,aACpB,UACA,WACA,SACA,eACA;AACA,QAAM,SAAS,UAAU,SAAS,CAAC,YAAY;AAC7C,UAAM,CAAC,QAAQ,WAAW,IAAI,IAAI;AAElC,UAAM,QAAQ,CAAC,MAAM,SAAS;AAC5B,eAAS,QAAQ,OAAO,aAAa,CAAC,MAAM,IAAI,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,cAAc,QAAQ,IAAI;AAAA,IAEvC,SAAS,GAAP;AACA,2CAAmB,CAAC;AACpB,YAAM,QAAS,OAAO,EAAE,SAAU,cAC9B,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,IACnC,EAAE;AACN,aAAO,MAAM,4BAAY,OAAO,KAAK,UAAU,KAAK,CAAC;AAAA,IACvD;AAEA,QAAI,EAAE,oBAAoB,UAAU;AAClC,aAAO,MAAM,4BAAY,SAAS,QAAQ;AAAA,IAC5C;AAEA,aACE,KAAK,CAAC,WAAW,MAAM,4BAAY,SAAS,MAAM,CAAC,EACnD,MAAM,CAAC,MAAM;AAEX,YAAM,MAAM,KAAK,EAAE,WAAW;AAC9B,YAAM,4BAAY,OAAO,GAAG;AAAA,IAC9B,CAAC;AAAA,EACL,CAAC;AACH;",
6
6
  "names": []
7
7
  }
package/build/IPC.mjs.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/IPC.ts"],
4
- "sourcesContent": ["import { debugAndPrintError } from './Debug';\nimport { Presence } from './presence/Presence';\nimport { IpcProtocol } from './Protocol';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils';\n\nexport async function requestFromIPC<T>(\n presence: Presence,\n publishToChannel: string,\n method: string,\n args: any[],\n rejectionTimeout: number = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let unsubscribeTimeout: NodeJS.Timer;\n\n const requestId = generateId();\n const channel = `ipc:${requestId}`;\n\n const unsubscribe = () => {\n presence.unsubscribe(channel);\n clearTimeout(unsubscribeTimeout);\n };\n\n presence.subscribe(channel, (message) => {\n const [code, data] = message;\n if (code === IpcProtocol.SUCCESS) {\n resolve(data);\n\n } else if (code === IpcProtocol.ERROR) {\n let error: any = data;\n\n // parse error message + code\n try { error = JSON.parse(data) } catch (e) {}\n\n // turn string message into Error instance\n if (typeof(error) === \"string\") {\n error = new Error(error);\n }\n\n reject(error);\n }\n unsubscribe();\n });\n\n presence.publish(publishToChannel, [method, requestId, args]);\n\n unsubscribeTimeout = setTimeout(() => {\n unsubscribe();\n reject(new Error(\"ipc_timeout\"));\n }, rejectionTimeout);\n });\n}\n\nexport async function subscribeIPC(\n presence: Presence,\n processId: string,\n channel: string,\n replyCallback: (method: string, args: any[]) => any,\n) {\n await presence.subscribe(channel, (message) => {\n const [method, requestId, args] = message;\n\n const reply = (code, data) => {\n presence.publish(`ipc:${requestId}`, [code, data]);\n };\n\n // reply with method result\n let response: any;\n try {\n response = replyCallback(method, args);\n\n } catch (e) {\n debugAndPrintError(e);\n const error = (typeof(e.code) !== \"undefined\")\n ? { code: e.code, message: e.message }\n : e.message;\n return reply(IpcProtocol.ERROR, JSON.stringify(error));\n }\n\n if (!(response instanceof Promise)) {\n return reply(IpcProtocol.SUCCESS, response);\n }\n\n response.\n then((result) => reply(IpcProtocol.SUCCESS, result)).\n catch((e) => {\n // user might have called `reject()` without arguments.\n const err = e && e.message || e;\n reply(IpcProtocol.ERROR, err);\n });\n });\n}\n"],
4
+ "sourcesContent": ["import { debugAndPrintError } from './Debug';\nimport { Presence } from './presence/Presence';\nimport { IpcProtocol } from './Protocol';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils';\n\nexport async function requestFromIPC<T>(\n presence: Presence,\n publishToChannel: string,\n method: string,\n args: any[],\n rejectionTimeout: number = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let unsubscribeTimeout: NodeJS.Timeout;\n\n const requestId = generateId();\n const channel = `ipc:${requestId}`;\n\n const unsubscribe = () => {\n presence.unsubscribe(channel);\n clearTimeout(unsubscribeTimeout);\n };\n\n presence.subscribe(channel, (message) => {\n const [code, data] = message;\n if (code === IpcProtocol.SUCCESS) {\n resolve(data);\n\n } else if (code === IpcProtocol.ERROR) {\n let error: any = data;\n\n // parse error message + code\n try { error = JSON.parse(data) } catch (e) {}\n\n // turn string message into Error instance\n if (typeof(error) === \"string\") {\n error = new Error(error);\n }\n\n reject(error);\n }\n unsubscribe();\n });\n\n presence.publish(publishToChannel, [method, requestId, args]);\n\n unsubscribeTimeout = setTimeout(() => {\n unsubscribe();\n reject(new Error(\"ipc_timeout\"));\n }, rejectionTimeout);\n });\n}\n\nexport async function subscribeIPC(\n presence: Presence,\n processId: string,\n channel: string,\n replyCallback: (method: string, args: any[]) => any,\n) {\n await presence.subscribe(channel, (message) => {\n const [method, requestId, args] = message;\n\n const reply = (code, data) => {\n presence.publish(`ipc:${requestId}`, [code, data]);\n };\n\n // reply with method result\n let response: any;\n try {\n response = replyCallback(method, args);\n\n } catch (e) {\n debugAndPrintError(e);\n const error = (typeof(e.code) !== \"undefined\")\n ? { code: e.code, message: e.message }\n : e.message;\n return reply(IpcProtocol.ERROR, JSON.stringify(error));\n }\n\n if (!(response instanceof Promise)) {\n return reply(IpcProtocol.SUCCESS, response);\n }\n\n response.\n then((result) => reply(IpcProtocol.SUCCESS, result)).\n catch((e) => {\n // user might have called `reject()` without arguments.\n const err = e && e.message || e;\n reply(IpcProtocol.ERROR, err);\n });\n });\n}\n"],
5
5
  "mappings": "AAAA,SAAS,0BAA0B;AAEnC,SAAS,mBAAmB;AAC5B,SAAS,YAAY,iCAAiC;AAEtD,eAAsB,eACpB,UACA,kBACA,QACA,MACA,mBAA2B,2BACf;AACZ,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,QAAI;AAEJ,UAAM,YAAY,WAAW;AAC7B,UAAM,UAAU,OAAO;AAEvB,UAAM,cAAc,MAAM;AACxB,eAAS,YAAY,OAAO;AAC5B,mBAAa,kBAAkB;AAAA,IACjC;AAEA,aAAS,UAAU,SAAS,CAAC,YAAY;AACvC,YAAM,CAAC,MAAM,IAAI,IAAI;AACrB,UAAI,SAAS,YAAY,SAAS;AAChC,gBAAQ,IAAI;AAAA,MAEd,WAAW,SAAS,YAAY,OAAO;AACrC,YAAI,QAAa;AAGjB,YAAI;AAAE,kBAAQ,KAAK,MAAM,IAAI;AAAA,QAAE,SAAS,GAAP;AAAA,QAAW;AAG5C,YAAI,OAAO,UAAW,UAAU;AAC9B,kBAAQ,IAAI,MAAM,KAAK;AAAA,QACzB;AAEA,eAAO,KAAK;AAAA,MACd;AACA,kBAAY;AAAA,IACd,CAAC;AAED,aAAS,QAAQ,kBAAkB,CAAC,QAAQ,WAAW,IAAI,CAAC;AAE5D,yBAAqB,WAAW,MAAM;AACpC,kBAAY;AACZ,aAAO,IAAI,MAAM,aAAa,CAAC;AAAA,IACjC,GAAG,gBAAgB;AAAA,EACrB,CAAC;AACH;AAEA,eAAsB,aACpB,UACA,WACA,SACA,eACA;AACA,QAAM,SAAS,UAAU,SAAS,CAAC,YAAY;AAC7C,UAAM,CAAC,QAAQ,WAAW,IAAI,IAAI;AAElC,UAAM,QAAQ,CAAC,MAAM,SAAS;AAC5B,eAAS,QAAQ,OAAO,aAAa,CAAC,MAAM,IAAI,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,cAAc,QAAQ,IAAI;AAAA,IAEvC,SAAS,GAAP;AACA,yBAAmB,CAAC;AACpB,YAAM,QAAS,OAAO,EAAE,SAAU,cAC9B,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,IACnC,EAAE;AACN,aAAO,MAAM,YAAY,OAAO,KAAK,UAAU,KAAK,CAAC;AAAA,IACvD;AAEA,QAAI,EAAE,oBAAoB,UAAU;AAClC,aAAO,MAAM,YAAY,SAAS,QAAQ;AAAA,IAC5C;AAEA,aACE,KAAK,CAAC,WAAW,MAAM,YAAY,SAAS,MAAM,CAAC,EACnD,MAAM,CAAC,MAAM;AAEX,YAAM,MAAM,KAAK,EAAE,WAAW;AAC9B,YAAM,YAAY,OAAO,GAAG;AAAA,IAC9B,CAAC;AAAA,EACL,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@ import { Deferred } from './utils/Utils';
2
2
  import { RegisteredHandler } from './matchmaker/RegisteredHandler';
3
3
  import { Room } from './Room';
4
4
  import { Presence } from './presence/Presence';
5
- import { IRoomListingData, RoomListingData, MatchMakerDriver } from './matchmaker/driver';
5
+ import { IRoomCache, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver';
6
6
  import controller from './matchmaker/controller';
7
7
  import * as stats from "./Stats";
8
8
  import { Type } from './utils/types';
@@ -15,7 +15,7 @@ export type AuthOptions = {
15
15
  export type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;
16
16
  export interface SeatReservation {
17
17
  sessionId: string;
18
- room: RoomListingData;
18
+ room: IRoomCache;
19
19
  devMode?: boolean;
20
20
  }
21
21
  export declare let publicAddress: string;
@@ -23,8 +23,6 @@ export declare let processId: string;
23
23
  export declare let presence: Presence;
24
24
  export declare let driver: MatchMakerDriver;
25
25
  export declare let selectProcessIdToCreateRoom: SelectProcessIdCallback;
26
- export declare function setHealthChecksEnabled(value: boolean): void;
27
- export declare let isGracefullyShuttingDown: boolean;
28
26
  export declare let onReady: Deferred;
29
27
  export declare enum MatchMakerState {
30
28
  INITIALIZING = 0,
@@ -61,7 +59,7 @@ export declare function join(roomName: string, clientOptions?: ClientOptions, au
61
59
  * Join a room by id and return seat reservation
62
60
  */
63
61
  export declare function reconnect(roomId: string, clientOptions?: ClientOptions): Promise<{
64
- room: RoomListingData<any>;
62
+ room: import("./matchmaker/driver/api").RoomCache<any>;
65
63
  sessionId: any;
66
64
  }>;
67
65
  /**
@@ -71,22 +69,22 @@ export declare function reconnect(roomId: string, clientOptions?: ClientOptions)
71
69
  * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)
72
70
  * @param authOptions - Optional authentication token
73
71
  *
74
- * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `RoomListingData`.
72
+ * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.
75
73
  */
76
74
  export declare function joinById(roomId: string, clientOptions?: ClientOptions, authOptions?: AuthOptions): Promise<SeatReservation>;
77
75
  /**
78
76
  * Perform a query for all cached rooms
79
77
  */
80
- export declare function query(conditions?: Partial<IRoomListingData>): Promise<RoomListingData<any>[]>;
78
+ export declare function query(conditions?: Partial<IRoomCache>, sortOptions?: SortOptions): Promise<IRoomCache[]>;
81
79
  /**
82
80
  * Find for a public and unlocked room available.
83
81
  *
84
82
  * @param roomName - The Id of the specific room.
85
83
  * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).
86
84
  *
87
- * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.
85
+ * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.
88
86
  */
89
- export declare function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData>;
87
+ export declare function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions, fallbackCreateRoom?: () => Promise<IRoomCache>): Promise<IRoomCache>;
90
88
  /**
91
89
  * Call a method or return a property on a remote room.
92
90
  *
@@ -108,11 +106,19 @@ export declare function getRoomClass(roomName: string): Type<Room>;
108
106
  * @param roomName - The identifier you defined on `gameServer.define()`
109
107
  * @param clientOptions - Options for `onCreate`
110
108
  *
111
- * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.
109
+ * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.
112
110
  */
113
- export declare function createRoom(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData>;
114
- export declare function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<RoomListingData>;
115
- export declare function getRoomById(roomId: string): Room<any, any>;
111
+ export declare function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache>;
112
+ export declare function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache>;
113
+ /**
114
+ * Get room data by roomId.
115
+ * This method does not return the actual room instance, use `getLocalRoomById` for that.
116
+ */
117
+ export declare function getRoomById(roomId: string): Promise<import("./matchmaker/driver/api").RoomCache<any>>;
118
+ /**
119
+ * Get local room instance by roomId. (Can return "undefined" if the room is not available on this process)
120
+ */
121
+ export declare function getLocalRoomById(roomId: string): Room<any, any, any, any>;
116
122
  /**
117
123
  * Disconnects every client on every room in the current process.
118
124
  */
@@ -121,8 +127,7 @@ export declare function gracefullyShutdown(): Promise<any>;
121
127
  /**
122
128
  * Reserve a seat for a client in a room
123
129
  */
124
- export declare function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any): Promise<SeatReservation>;
125
- export declare function cleanupStaleRooms(roomName: string): Promise<void>;
130
+ export declare function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any): Promise<SeatReservation>;
126
131
  /**
127
132
  * Perform health check on all processes
128
133
  */
@@ -25,7 +25,6 @@ var MatchMaker_exports = {};
25
25
  __export(MatchMaker_exports, {
26
26
  MatchMakerState: () => MatchMakerState,
27
27
  accept: () => accept,
28
- cleanupStaleRooms: () => cleanupStaleRooms,
29
28
  controller: () => import_controller.default,
30
29
  create: () => create,
31
30
  createRoom: () => createRoom,
@@ -34,6 +33,7 @@ __export(MatchMaker_exports, {
34
33
  driver: () => driver,
35
34
  findOneRoomAvailable: () => findOneRoomAvailable,
36
35
  getHandler: () => getHandler,
36
+ getLocalRoomById: () => getLocalRoomById,
37
37
  getRoomById: () => getRoomById,
38
38
  getRoomClass: () => getRoomClass,
39
39
  gracefullyShutdown: () => gracefullyShutdown,
@@ -41,7 +41,6 @@ __export(MatchMaker_exports, {
41
41
  hasHandler: () => hasHandler,
42
42
  healthCheckAllProcesses: () => healthCheckAllProcesses,
43
43
  healthCheckProcessId: () => healthCheckProcessId,
44
- isGracefullyShuttingDown: () => isGracefullyShuttingDown,
45
44
  join: () => join,
46
45
  joinById: () => joinById,
47
46
  joinOrCreate: () => joinOrCreate,
@@ -55,7 +54,6 @@ __export(MatchMaker_exports, {
55
54
  removeRoomType: () => removeRoomType,
56
55
  reserveSeatFor: () => reserveSeatFor,
57
56
  selectProcessIdToCreateRoom: () => selectProcessIdToCreateRoom,
58
- setHealthChecksEnabled: () => setHealthChecksEnabled,
59
57
  setup: () => setup,
60
58
  state: () => state,
61
59
  stats: () => stats
@@ -71,11 +69,12 @@ var import_LocalPresence = require("./presence/LocalPresence");
71
69
  var import_Debug = require("./Debug");
72
70
  var import_SeatReservationError = require("./errors/SeatReservationError");
73
71
  var import_ServerError = require("./errors/ServerError");
74
- var import_driver = require("./matchmaker/driver");
72
+ var import_LocalDriver = require("./matchmaker/driver/local/LocalDriver");
75
73
  var import_controller = __toESM(require("./matchmaker/controller"));
76
74
  var stats = __toESM(require("./Stats"));
77
75
  var import_Logger = require("./Logger");
78
76
  var import_discovery = require("./discovery");
77
+ var import_api = require("./matchmaker/driver/api");
79
78
  const handlers = {};
80
79
  const rooms = {};
81
80
  let publicAddress;
@@ -83,11 +82,6 @@ let processId;
83
82
  let presence;
84
83
  let driver;
85
84
  let selectProcessIdToCreateRoom;
86
- let enableHealthChecks = true;
87
- function setHealthChecksEnabled(value) {
88
- enableHealthChecks = value;
89
- }
90
- let isGracefullyShuttingDown;
91
85
  let onReady = new import_Utils.Deferred();
92
86
  var MatchMakerState = /* @__PURE__ */ ((MatchMakerState2) => {
93
87
  MatchMakerState2[MatchMakerState2["INITIALIZING"] = 0] = "INITIALIZING";
@@ -100,10 +94,9 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
100
94
  if (onReady === void 0) {
101
95
  onReady = new import_Utils.Deferred();
102
96
  }
103
- isGracefullyShuttingDown = false;
104
97
  state = 0 /* INITIALIZING */;
105
98
  presence = _presence || new import_LocalPresence.LocalPresence();
106
- driver = _driver || new import_driver.LocalDriver();
99
+ driver = _driver || new import_LocalDriver.LocalDriver();
107
100
  publicAddress = _publicAddress;
108
101
  stats.reset(false);
109
102
  if (import_DevMode.isDevMode) {
@@ -126,9 +119,7 @@ async function accept() {
126
119
  return handleCreateRoom.apply(void 0, args);
127
120
  }
128
121
  });
129
- if (enableHealthChecks) {
130
- await healthCheckAllProcesses();
131
- }
122
+ await healthCheckAllProcesses();
132
123
  state = 1 /* READY */;
133
124
  await stats.persist();
134
125
  if (import_DevMode.isDevMode) {
@@ -138,10 +129,11 @@ async function accept() {
138
129
  async function joinOrCreate(roomName, clientOptions = {}, authOptions) {
139
130
  return await (0, import_Utils.retry)(async () => {
140
131
  const authData = await callOnAuth(roomName, authOptions);
141
- let room = await findOneRoomAvailable(roomName, clientOptions);
142
- if (!room) {
143
- room = await createRoom(roomName, clientOptions);
144
- }
132
+ const room = await findOneRoomAvailable(
133
+ roomName,
134
+ clientOptions,
135
+ () => createRoom(roomName, clientOptions)
136
+ );
145
137
  return await reserveSeatFor(room, clientOptions, authData);
146
138
  }, 5, [import_SeatReservationError.SeatReservationError]);
147
139
  }
@@ -194,22 +186,26 @@ async function joinById(roomId, clientOptions = {}, authOptions) {
194
186
  const authData = await callOnAuth(room.name, authOptions);
195
187
  return reserveSeatFor(room, clientOptions, authData);
196
188
  }
197
- async function query(conditions = {}) {
198
- return await driver.find(conditions);
189
+ async function query(conditions = {}, sortOptions) {
190
+ return await driver.query(conditions, sortOptions);
199
191
  }
200
- async function findOneRoomAvailable(roomName, clientOptions) {
201
- return await awaitRoomAvailable(roomName, async () => {
202
- const handler = getHandler(roomName);
203
- const roomQuery = driver.findOne({
192
+ async function findOneRoomAvailable(roomName, clientOptions, fallbackCreateRoom) {
193
+ const handler = getHandler(roomName);
194
+ const filterOptions = handler.getFilterOptions(clientOptions);
195
+ const concurrencyKey = (0, import_api.getLockId)(filterOptions);
196
+ const mayCreateRoom = fallbackCreateRoom !== void 0;
197
+ return await concurrentLock(handler, concurrencyKey, mayCreateRoom, async (roomId) => {
198
+ let room = roomId ? await driver.findOne({ roomId }) : await driver.findOne({
204
199
  locked: false,
205
200
  name: roomName,
206
201
  private: false,
207
- ...handler.getFilterOptions(clientOptions)
208
- });
209
- if (handler.sortOptions) {
210
- roomQuery.sort(handler.sortOptions);
202
+ ...filterOptions
203
+ }, handler.sortOptions);
204
+ if (!room && fallbackCreateRoom) {
205
+ room = await fallbackCreateRoom();
206
+ presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);
211
207
  }
212
- return await roomQuery;
208
+ return room;
213
209
  });
214
210
  }
215
211
  async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Utils.REMOTE_ROOM_SHORT_TIMEOUT) {
@@ -232,23 +228,17 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
232
228
  }
233
229
  }
234
230
  function defineRoomType(roomName, klass, defaultOptions) {
235
- const registeredHandler = new import_RegisteredHandler.RegisteredHandler(klass, defaultOptions);
231
+ const registeredHandler = new import_RegisteredHandler.RegisteredHandler(roomName, klass, defaultOptions);
236
232
  handlers[roomName] = registeredHandler;
237
233
  if (klass.prototype["onAuth"] !== import_Room.Room.prototype["onAuth"]) {
238
234
  if (klass["onAuth"] !== import_Room.Room["onAuth"]) {
239
235
  import_Logger.logger.info(`\u274C "${roomName}"'s onAuth() defined at the instance level will be ignored.`);
240
236
  }
241
237
  }
242
- if (!import_DevMode.isDevMode) {
243
- cleanupStaleRooms(roomName);
244
- }
245
238
  return registeredHandler;
246
239
  }
247
240
  function removeRoomType(roomName) {
248
241
  delete handlers[roomName];
249
- if (!import_DevMode.isDevMode) {
250
- cleanupStaleRooms(roomName);
251
- }
252
242
  }
253
243
  function hasHandler(roomName) {
254
244
  import_Logger.logger.warn("hasHandler() is deprecated. Use getHandler() instead.");
@@ -283,9 +273,7 @@ async function createRoom(roomName, clientOptions) {
283
273
  } catch (e) {
284
274
  if (e.message === "ipc_timeout") {
285
275
  (0, import_Debug.debugAndPrintError)(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);
286
- if (enableHealthChecks) {
287
- await stats.excludeProcess(selectedProcessId);
288
- }
276
+ await stats.excludeProcess(selectedProcessId);
289
277
  room = await handleCreateRoom(roomName, clientOptions);
290
278
  } else {
291
279
  throw e;
@@ -309,6 +297,9 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
309
297
  } else {
310
298
  room.roomId = (0, import_Utils.generateId)();
311
299
  }
300
+ if (room.state) {
301
+ room.setState(room.state);
302
+ }
312
303
  room.roomName = roomName;
313
304
  room.presence = presence;
314
305
  const additionalListingData = handler.getFilterOptions(clientOptions);
@@ -355,6 +346,9 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
355
346
  return room.listing;
356
347
  }
357
348
  function getRoomById(roomId) {
349
+ return driver.findOne({ roomId });
350
+ }
351
+ function getLocalRoomById(roomId) {
358
352
  return rooms[roomId];
359
353
  }
360
354
  function disconnectAll(closeCode) {
@@ -370,10 +364,9 @@ function disconnectAll(closeCode) {
370
364
  return promises;
371
365
  }
372
366
  async function gracefullyShutdown() {
373
- if (isGracefullyShuttingDown) {
367
+ if (state === 2 /* SHUTTING_DOWN */) {
374
368
  return Promise.reject("already_shutting_down");
375
369
  }
376
- isGracefullyShuttingDown = true;
377
370
  state = 2 /* SHUTTING_DOWN */;
378
371
  onReady = void 0;
379
372
  (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
@@ -405,7 +398,7 @@ async function reserveSeatFor(room, options, authData) {
405
398
  );
406
399
  } catch (e) {
407
400
  (0, import_Debug.debugMatchMaking)(e);
408
- if (e.message === "ipc_timeout" && !(enableHealthChecks && await healthCheckProcessId(room.processId))) {
401
+ if (e.message === "ipc_timeout" && !await healthCheckProcessId(room.processId)) {
409
402
  throw new import_SeatReservationError.SeatReservationError(`process ${room.processId} is not available.`);
410
403
  } else {
411
404
  successfulSeatReservation = false;
@@ -424,9 +417,6 @@ function callOnAuth(roomName, authOptions) {
424
417
  const roomClass = getRoomClass(roomName);
425
418
  return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== import_Room.Room["onAuth"] ? roomClass["onAuth"](authOptions.token, authOptions.request) : void 0;
426
419
  }
427
- async function cleanupStaleRooms(roomName) {
428
- await presence.del(getHandlerConcurrencyKey(roomName));
429
- }
430
420
  async function healthCheckAllProcesses() {
431
421
  const allStats = await stats.fetchAll();
432
422
  if (allStats.length > 0) {
@@ -467,13 +457,7 @@ function healthCheckProcessId(processId2) {
467
457
  return _healthCheckByProcessId[processId2];
468
458
  }
469
459
  async function removeRoomsByProcessId(processId2) {
470
- if (typeof driver.cleanup === "function") {
471
- await driver.cleanup(processId2);
472
- } else {
473
- const cachedRooms = await driver.find({ processId: processId2 }, { _id: 1 });
474
- import_Logger.logger.debug("> Removing stale rooms by processId:", processId2, `(${cachedRooms.length} rooms found)`);
475
- cachedRooms.forEach((room) => room.remove());
476
- }
460
+ await driver.cleanup(processId2);
477
461
  }
478
462
  async function createRoomReferences(room, init = false) {
479
463
  rooms[room.roomId] = room;
@@ -489,29 +473,34 @@ async function createRoomReferences(room, init = false) {
489
473
  }
490
474
  return true;
491
475
  }
492
- async function awaitRoomAvailable(roomToJoin, callback) {
476
+ async function concurrentLock(handler, concurrencyKey, mayCreateRoom, callback) {
493
477
  return new Promise(async (resolve, reject) => {
494
- const concurrencyKey = getHandlerConcurrencyKey(roomToJoin);
495
- const concurrency = await presence.incr(concurrencyKey) - 1;
496
- const concurrencyTimeout = Math.min(concurrency * 100, 500);
497
- if (concurrency > 0) {
498
- (0, import_Debug.debugMatchMaking)(
499
- "receiving %d concurrent requests for joining '%s' (waiting %d ms)",
500
- concurrency,
501
- roomToJoin,
502
- concurrencyTimeout
503
- );
504
- }
505
- setTimeout(async () => {
478
+ const hkey = getConcurrencyHashKey(handler.name);
479
+ const concurrency = await presence.hincrby(hkey, concurrencyKey, 1) - 1;
480
+ const fulfill = async (roomId) => {
506
481
  try {
507
- const result = await callback();
508
- resolve(result);
482
+ resolve(await callback(roomId));
509
483
  } catch (e) {
510
484
  reject(e);
511
485
  } finally {
512
- await presence.decr(concurrencyKey);
486
+ await presence.hincrby(hkey, concurrencyKey, -1);
513
487
  }
514
- }, concurrencyTimeout);
488
+ };
489
+ if (concurrency > 0) {
490
+ (0, import_Debug.debugMatchMaking)(
491
+ "receiving %d concurrent requests for joining '%s' (%s)",
492
+ concurrency,
493
+ handler.name,
494
+ concurrencyKey
495
+ );
496
+ const [_, roomId] = await presence.brpop(
497
+ `l:${handler.name}:${concurrencyKey}`,
498
+ import_Utils.MAX_CONCURRENT_JOIN_WAIT_TIME
499
+ );
500
+ return await fulfill(roomId);
501
+ } else {
502
+ return await fulfill();
503
+ }
515
504
  });
516
505
  }
517
506
  function onClientJoinRoom(room, client) {
@@ -536,9 +525,8 @@ function onVisibilityChange(room, isInvisible) {
536
525
  handlers[room.roomName].emit("visibility-change", room, isInvisible);
537
526
  }
538
527
  async function disposeRoom(roomName, room) {
539
- (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, isGracefullyShuttingDown);
540
- room.listing.remove();
541
- if (!isGracefullyShuttingDown) {
528
+ (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, state === 2 /* SHUTTING_DOWN */);
529
+ if (state !== 2 /* SHUTTING_DOWN */) {
542
530
  stats.local.roomCount--;
543
531
  stats.persist();
544
532
  if (import_DevMode.isDevMode) {
@@ -546,15 +534,14 @@ async function disposeRoom(roomName, room) {
546
534
  }
547
535
  }
548
536
  handlers[roomName].emit("dispose", room);
549
- presence.del(getHandlerConcurrencyKey(roomName));
550
537
  presence.unsubscribe(getRoomChannel(room.roomId));
551
538
  delete rooms[room.roomId];
552
539
  }
553
540
  function getRoomChannel(roomId) {
554
541
  return `$${roomId}`;
555
542
  }
556
- function getHandlerConcurrencyKey(name) {
557
- return `c:${name}`;
543
+ function getConcurrencyHashKey(roomName) {
544
+ return `ch:${roomName}`;
558
545
  }
559
546
  function getProcessChannel(id = processId) {
560
547
  return `p:${id}`;
@@ -563,7 +550,6 @@ function getProcessChannel(id = processId) {
563
550
  0 && (module.exports = {
564
551
  MatchMakerState,
565
552
  accept,
566
- cleanupStaleRooms,
567
553
  controller,
568
554
  create,
569
555
  createRoom,
@@ -572,6 +558,7 @@ function getProcessChannel(id = processId) {
572
558
  driver,
573
559
  findOneRoomAvailable,
574
560
  getHandler,
561
+ getLocalRoomById,
575
562
  getRoomById,
576
563
  getRoomClass,
577
564
  gracefullyShutdown,
@@ -579,7 +566,6 @@ function getProcessChannel(id = processId) {
579
566
  hasHandler,
580
567
  healthCheckAllProcesses,
581
568
  healthCheckProcessId,
582
- isGracefullyShuttingDown,
583
569
  join,
584
570
  joinById,
585
571
  joinOrCreate,
@@ -593,7 +579,6 @@ function getProcessChannel(id = processId) {
593
579
  removeRoomType,
594
580
  reserveSeatFor,
595
581
  selectProcessIdToCreateRoom,
596
- setHealthChecksEnabled,
597
582
  setup,
598
583
  state,
599
584
  stats