@colyseus/core 0.15.45 → 0.16.0-alpha.23

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 +22 -16
  5. package/build/MatchMaker.js +71 -81
  6. package/build/MatchMaker.js.map +2 -2
  7. package/build/MatchMaker.mjs +70 -78
  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,23 @@ 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
- * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`).
83
+ * @param filterOptions - Filter options.
84
+ * @param sortOptions - Sorting options.
86
85
  *
87
- * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.
86
+ * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.
88
87
  */
89
- export declare function findOneRoomAvailable(roomName: string, clientOptions: ClientOptions): Promise<RoomListingData>;
88
+ export declare function findOneRoomAvailable(roomName: string, filterOptions: ClientOptions, additionalSortOptions?: SortOptions): Promise<import("./matchmaker/driver/api").RoomCache<any>>;
90
89
  /**
91
90
  * Call a method or return a property on a remote room.
92
91
  *
@@ -108,11 +107,19 @@ export declare function getRoomClass(roomName: string): Type<Room>;
108
107
  * @param roomName - The identifier you defined on `gameServer.define()`
109
108
  * @param clientOptions - Options for `onCreate`
110
109
  *
111
- * @returns Promise<RoomListingData> - A promise contaning an object which includes room metadata and configurations.
110
+ * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.
112
111
  */
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>;
112
+ export declare function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache>;
113
+ export declare function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache>;
114
+ /**
115
+ * Get room data by roomId.
116
+ * This method does not return the actual room instance, use `getLocalRoomById` for that.
117
+ */
118
+ export declare function getRoomById(roomId: string): Promise<import("./matchmaker/driver/api").RoomCache<any>>;
119
+ /**
120
+ * Get local room instance by roomId. (Can return "undefined" if the room is not available on this process)
121
+ */
122
+ export declare function getLocalRoomById(roomId: string): Room<any, any, any, any>;
116
123
  /**
117
124
  * Disconnects every client on every room in the current process.
118
125
  */
@@ -121,8 +128,7 @@ export declare function gracefullyShutdown(): Promise<any>;
121
128
  /**
122
129
  * Reserve a seat for a client in a room
123
130
  */
124
- export declare function reserveSeatFor(room: RoomListingData, options: ClientOptions, authData?: any): Promise<SeatReservation>;
125
- export declare function cleanupStaleRooms(roomName: string): Promise<void>;
131
+ export declare function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any): Promise<SeatReservation>;
126
132
  /**
127
133
  * Perform health check on all processes
128
134
  */
@@ -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) {
@@ -140,7 +131,19 @@ async function joinOrCreate(roomName, clientOptions = {}, authOptions) {
140
131
  const authData = await callOnAuth(roomName, authOptions);
141
132
  let room = await findOneRoomAvailable(roomName, clientOptions);
142
133
  if (!room) {
143
- room = await createRoom(roomName, clientOptions);
134
+ const handler = getHandler(roomName);
135
+ const filterOptions = handler.getFilterOptions(clientOptions);
136
+ const concurrencyKey = (0, import_api.getLockId)(filterOptions);
137
+ await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId) => {
138
+ if (roomId) {
139
+ room = await driver.findOne({ roomId });
140
+ }
141
+ if (!room) {
142
+ room = await createRoom(roomName, clientOptions);
143
+ presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);
144
+ }
145
+ return room;
146
+ });
144
147
  }
145
148
  return await reserveSeatFor(room, clientOptions, authData);
146
149
  }, 5, [import_SeatReservationError.SeatReservationError]);
@@ -194,23 +197,21 @@ async function joinById(roomId, clientOptions = {}, authOptions) {
194
197
  const authData = await callOnAuth(room.name, authOptions);
195
198
  return reserveSeatFor(room, clientOptions, authData);
196
199
  }
197
- async function query(conditions = {}) {
198
- return await driver.find(conditions);
199
- }
200
- async function findOneRoomAvailable(roomName, clientOptions) {
201
- return await awaitRoomAvailable(roomName, async () => {
202
- const handler = getHandler(roomName);
203
- const roomQuery = driver.findOne({
204
- locked: false,
205
- name: roomName,
206
- private: false,
207
- ...handler.getFilterOptions(clientOptions)
208
- });
209
- if (handler.sortOptions) {
210
- roomQuery.sort(handler.sortOptions);
211
- }
212
- return await roomQuery;
213
- });
200
+ async function query(conditions = {}, sortOptions) {
201
+ return await driver.query(conditions, sortOptions);
202
+ }
203
+ async function findOneRoomAvailable(roomName, filterOptions, additionalSortOptions) {
204
+ const handler = getHandler(roomName);
205
+ const sortOptions = Object.assign({}, handler.sortOptions ?? {});
206
+ if (additionalSortOptions) {
207
+ Object.assign(sortOptions, additionalSortOptions);
208
+ }
209
+ return await driver.findOne({
210
+ locked: false,
211
+ name: roomName,
212
+ private: false,
213
+ ...handler.getFilterOptions(filterOptions)
214
+ }, sortOptions);
214
215
  }
215
216
  async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Utils.REMOTE_ROOM_SHORT_TIMEOUT) {
216
217
  const room = rooms[roomId];
@@ -232,23 +233,17 @@ async function remoteRoomCall(roomId, method, args, rejectionTimeout = import_Ut
232
233
  }
233
234
  }
234
235
  function defineRoomType(roomName, klass, defaultOptions) {
235
- const registeredHandler = new import_RegisteredHandler.RegisteredHandler(klass, defaultOptions);
236
+ const registeredHandler = new import_RegisteredHandler.RegisteredHandler(roomName, klass, defaultOptions);
236
237
  handlers[roomName] = registeredHandler;
237
238
  if (klass.prototype["onAuth"] !== import_Room.Room.prototype["onAuth"]) {
238
239
  if (klass["onAuth"] !== import_Room.Room["onAuth"]) {
239
240
  import_Logger.logger.info(`\u274C "${roomName}"'s onAuth() defined at the instance level will be ignored.`);
240
241
  }
241
242
  }
242
- if (!import_DevMode.isDevMode) {
243
- cleanupStaleRooms(roomName);
244
- }
245
243
  return registeredHandler;
246
244
  }
247
245
  function removeRoomType(roomName) {
248
246
  delete handlers[roomName];
249
- if (!import_DevMode.isDevMode) {
250
- cleanupStaleRooms(roomName);
251
- }
252
247
  }
253
248
  function hasHandler(roomName) {
254
249
  import_Logger.logger.warn("hasHandler() is deprecated. Use getHandler() instead.");
@@ -283,9 +278,7 @@ async function createRoom(roomName, clientOptions) {
283
278
  } catch (e) {
284
279
  if (e.message === "ipc_timeout") {
285
280
  (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
- }
281
+ await stats.excludeProcess(selectedProcessId);
289
282
  room = await handleCreateRoom(roomName, clientOptions);
290
283
  } else {
291
284
  throw e;
@@ -309,6 +302,9 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
309
302
  } else {
310
303
  room.roomId = (0, import_Utils.generateId)();
311
304
  }
305
+ if (room.state) {
306
+ room.setState(room.state);
307
+ }
312
308
  room.roomName = roomName;
313
309
  room.presence = presence;
314
310
  const additionalListingData = handler.getFilterOptions(clientOptions);
@@ -355,6 +351,9 @@ async function handleCreateRoom(roomName, clientOptions, restoringRoomId) {
355
351
  return room.listing;
356
352
  }
357
353
  function getRoomById(roomId) {
354
+ return driver.findOne({ roomId });
355
+ }
356
+ function getLocalRoomById(roomId) {
358
357
  return rooms[roomId];
359
358
  }
360
359
  function disconnectAll(closeCode) {
@@ -370,10 +369,9 @@ function disconnectAll(closeCode) {
370
369
  return promises;
371
370
  }
372
371
  async function gracefullyShutdown() {
373
- if (isGracefullyShuttingDown) {
372
+ if (state === 2 /* SHUTTING_DOWN */) {
374
373
  return Promise.reject("already_shutting_down");
375
374
  }
376
- isGracefullyShuttingDown = true;
377
375
  state = 2 /* SHUTTING_DOWN */;
378
376
  onReady = void 0;
379
377
  (0, import_Debug.debugMatchMaking)(`${processId} is shutting down!`);
@@ -405,7 +403,7 @@ async function reserveSeatFor(room, options, authData) {
405
403
  );
406
404
  } catch (e) {
407
405
  (0, import_Debug.debugMatchMaking)(e);
408
- if (e.message === "ipc_timeout" && !(enableHealthChecks && await healthCheckProcessId(room.processId))) {
406
+ if (e.message === "ipc_timeout" && !await healthCheckProcessId(room.processId)) {
409
407
  throw new import_SeatReservationError.SeatReservationError(`process ${room.processId} is not available.`);
410
408
  } else {
411
409
  successfulSeatReservation = false;
@@ -424,9 +422,6 @@ function callOnAuth(roomName, authOptions) {
424
422
  const roomClass = getRoomClass(roomName);
425
423
  return roomClass && roomClass["onAuth"] && roomClass["onAuth"] !== import_Room.Room["onAuth"] ? roomClass["onAuth"](authOptions.token, authOptions.request) : void 0;
426
424
  }
427
- async function cleanupStaleRooms(roomName) {
428
- await presence.del(getHandlerConcurrencyKey(roomName));
429
- }
430
425
  async function healthCheckAllProcesses() {
431
426
  const allStats = await stats.fetchAll();
432
427
  if (allStats.length > 0) {
@@ -467,13 +462,7 @@ function healthCheckProcessId(processId2) {
467
462
  return _healthCheckByProcessId[processId2];
468
463
  }
469
464
  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
- }
465
+ await driver.cleanup(processId2);
477
466
  }
478
467
  async function createRoomReferences(room, init = false) {
479
468
  rooms[room.roomId] = room;
@@ -489,29 +478,34 @@ async function createRoomReferences(room, init = false) {
489
478
  }
490
479
  return true;
491
480
  }
492
- async function awaitRoomAvailable(roomToJoin, callback) {
481
+ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback) {
493
482
  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 () => {
483
+ const hkey = getConcurrencyHashKey(handler.name);
484
+ const concurrency = await presence.hincrby(hkey, concurrencyKey, 1) - 1;
485
+ const fulfill = async (roomId) => {
506
486
  try {
507
- const result = await callback();
508
- resolve(result);
487
+ resolve(await callback(roomId));
509
488
  } catch (e) {
510
489
  reject(e);
511
490
  } finally {
512
- await presence.decr(concurrencyKey);
491
+ await presence.hincrby(hkey, concurrencyKey, -1);
513
492
  }
514
- }, concurrencyTimeout);
493
+ };
494
+ if (concurrency > 0) {
495
+ (0, import_Debug.debugMatchMaking)(
496
+ "receiving %d concurrent requests for creating '%s' (%s)",
497
+ concurrency,
498
+ handler.name,
499
+ concurrencyKey
500
+ );
501
+ const result = await presence.brpop(
502
+ `l:${handler.name}:${concurrencyKey}`,
503
+ import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME
504
+ );
505
+ return await fulfill(result && result[1]);
506
+ } else {
507
+ return await fulfill();
508
+ }
515
509
  });
516
510
  }
517
511
  function onClientJoinRoom(room, client) {
@@ -536,9 +530,8 @@ function onVisibilityChange(room, isInvisible) {
536
530
  handlers[room.roomName].emit("visibility-change", room, isInvisible);
537
531
  }
538
532
  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) {
533
+ (0, import_Debug.debugMatchMaking)("disposing '%s' (%s) on processId '%s' (graceful shutdown: %s)", roomName, room.roomId, processId, state === 2 /* SHUTTING_DOWN */);
534
+ if (state !== 2 /* SHUTTING_DOWN */) {
542
535
  stats.local.roomCount--;
543
536
  stats.persist();
544
537
  if (import_DevMode.isDevMode) {
@@ -546,15 +539,14 @@ async function disposeRoom(roomName, room) {
546
539
  }
547
540
  }
548
541
  handlers[roomName].emit("dispose", room);
549
- presence.del(getHandlerConcurrencyKey(roomName));
550
542
  presence.unsubscribe(getRoomChannel(room.roomId));
551
543
  delete rooms[room.roomId];
552
544
  }
553
545
  function getRoomChannel(roomId) {
554
546
  return `$${roomId}`;
555
547
  }
556
- function getHandlerConcurrencyKey(name) {
557
- return `c:${name}`;
548
+ function getConcurrencyHashKey(roomName) {
549
+ return `ch:${roomName}`;
558
550
  }
559
551
  function getProcessChannel(id = processId) {
560
552
  return `p:${id}`;
@@ -563,7 +555,6 @@ function getProcessChannel(id = processId) {
563
555
  0 && (module.exports = {
564
556
  MatchMakerState,
565
557
  accept,
566
- cleanupStaleRooms,
567
558
  controller,
568
559
  create,
569
560
  createRoom,
@@ -572,6 +563,7 @@ function getProcessChannel(id = processId) {
572
563
  driver,
573
564
  findOneRoomAvailable,
574
565
  getHandler,
566
+ getLocalRoomById,
575
567
  getRoomById,
576
568
  getRoomClass,
577
569
  gracefullyShutdown,
@@ -579,7 +571,6 @@ function getProcessChannel(id = processId) {
579
571
  hasHandler,
580
572
  healthCheckAllProcesses,
581
573
  healthCheckProcessId,
582
- isGracefullyShuttingDown,
583
574
  join,
584
575
  joinById,
585
576
  joinOrCreate,
@@ -593,7 +584,6 @@ function getProcessChannel(id = processId) {
593
584
  removeRoomType,
594
585
  reserveSeatFor,
595
586
  selectProcessIdToCreateRoom,
596
- setHealthChecksEnabled,
597
587
  setup,
598
588
  state,
599
589
  stats