@colyseus/core 0.16.19 → 0.16.20

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.
package/build/IPC.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  import { Presence } from './presence/Presence.js';
2
2
  export declare function requestFromIPC<T>(presence: Presence, publishToChannel: string, method: string, args: any[], rejectionTimeout?: number): Promise<T>;
3
- export declare function subscribeIPC(presence: Presence, processId: string, channel: string, replyCallback: (method: string, args: any[]) => any): Promise<void>;
3
+ export declare function subscribeIPC(presence: Presence, channel: string, replyCallback: (method: string, args: any[]) => any): Promise<void>;
4
+ /**
5
+ * Wait for a room creation notification via presence publish/subscribe
6
+ */
7
+ export declare function subscribeWithTimeout(presence: Presence, channel: string, timeout: number): Promise<string>;
package/build/IPC.js CHANGED
@@ -18,14 +18,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var IPC_exports = {};
19
19
  __export(IPC_exports, {
20
20
  requestFromIPC: () => requestFromIPC,
21
- subscribeIPC: () => subscribeIPC
21
+ subscribeIPC: () => subscribeIPC,
22
+ subscribeWithTimeout: () => subscribeWithTimeout
22
23
  });
23
24
  module.exports = __toCommonJS(IPC_exports);
24
25
  var import_Debug = require("./Debug.js");
25
26
  var import_Protocol = require("./Protocol.js");
26
27
  var import_Utils = require("./utils/Utils.js");
27
28
  async function requestFromIPC(presence, publishToChannel, method, args, rejectionTimeout = import_Utils.REMOTE_ROOM_SHORT_TIMEOUT) {
28
- return new Promise((resolve, reject) => {
29
+ return new Promise(async (resolve, reject) => {
29
30
  let unsubscribeTimeout;
30
31
  const requestId = (0, import_Utils.generateId)();
31
32
  const channel = `ipc:${requestId}`;
@@ -33,7 +34,7 @@ async function requestFromIPC(presence, publishToChannel, method, args, rejectio
33
34
  presence.unsubscribe(channel);
34
35
  clearTimeout(unsubscribeTimeout);
35
36
  };
36
- presence.subscribe(channel, (message) => {
37
+ await presence.subscribe(channel, (message) => {
37
38
  const [code, data] = message;
38
39
  if (code === import_Protocol.IpcProtocol.SUCCESS) {
39
40
  resolve(data);
@@ -57,7 +58,7 @@ async function requestFromIPC(presence, publishToChannel, method, args, rejectio
57
58
  }, rejectionTimeout);
58
59
  });
59
60
  }
60
- async function subscribeIPC(presence, processId, channel, replyCallback) {
61
+ async function subscribeIPC(presence, channel, replyCallback) {
61
62
  await presence.subscribe(channel, (message) => {
62
63
  const [method, requestId, args] = message;
63
64
  const reply = (code, data) => {
@@ -80,8 +81,31 @@ async function subscribeIPC(presence, processId, channel, replyCallback) {
80
81
  });
81
82
  });
82
83
  }
84
+ function subscribeWithTimeout(presence, channel, timeout) {
85
+ return new Promise((resolve, reject) => {
86
+ let timeoutHandle;
87
+ let resolved = false;
88
+ const unsubscribe = () => {
89
+ presence.unsubscribe(channel);
90
+ clearTimeout(timeoutHandle);
91
+ };
92
+ presence.subscribe(channel, (roomId) => {
93
+ if (resolved) return;
94
+ resolved = true;
95
+ unsubscribe();
96
+ resolve(roomId);
97
+ });
98
+ timeoutHandle = setTimeout(() => {
99
+ if (resolved) return;
100
+ resolved = true;
101
+ unsubscribe();
102
+ reject(new Error("timeout"));
103
+ }, timeout);
104
+ });
105
+ }
83
106
  // Annotate the CommonJS export names for ESM import in node:
84
107
  0 && (module.exports = {
85
108
  requestFromIPC,
86
- subscribeIPC
109
+ subscribeIPC,
110
+ subscribeWithTimeout
87
111
  });
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.js';\nimport { Presence } from './presence/Presence.js';\nimport { IpcProtocol } from './Protocol.js';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\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
- "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,SAAS;AAEhC,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,GAAG;AAAA,QAAC;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,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,cAAc,QAAQ,IAAI;AAAA,IAEvC,SAAS,GAAG;AACV,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;",
4
+ "sourcesContent": ["import { debugAndPrintError } from './Debug.js';\nimport { Presence } from './presence/Presence.js';\nimport { IpcProtocol } from './Protocol.js';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\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>(async (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 await presence.subscribe(channel, (message: [IpcProtocol, any]) => {\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 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\n/**\n * Wait for a room creation notification via presence publish/subscribe\n */\nexport function subscribeWithTimeout(\n presence: Presence,\n channel: string,\n timeout: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let timeoutHandle: NodeJS.Timeout;\n let resolved = false;\n\n const unsubscribe = () => {\n presence.unsubscribe(channel);\n clearTimeout(timeoutHandle);\n };\n\n presence.subscribe(channel, (roomId: string) => {\n if (resolved) return;\n resolved = true;\n unsubscribe();\n resolve(roomId);\n });\n\n timeoutHandle = setTimeout(() => {\n if (resolved) return;\n resolved = true;\n unsubscribe();\n reject(new Error(\"timeout\"));\n }, timeout);\n });\n}"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;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,OAAO,SAAS,WAAW;AAC/C,QAAI;AAEJ,UAAM,gBAAY,yBAAW;AAC7B,UAAM,UAAU,OAAO,SAAS;AAEhC,UAAM,cAAc,MAAM;AACxB,eAAS,YAAY,OAAO;AAC5B,mBAAa,kBAAkB;AAAA,IACjC;AAEA,UAAM,SAAS,UAAU,SAAS,CAAC,YAAgC;AACjE,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,GAAG;AAAA,QAAC;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,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,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,cAAc,QAAQ,IAAI;AAAA,IAEvC,SAAS,GAAG;AACV,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;AAKO,SAAS,qBACd,UACA,SACA,SACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI;AACJ,QAAI,WAAW;AAEf,UAAM,cAAc,MAAM;AACxB,eAAS,YAAY,OAAO;AAC5B,mBAAa,aAAa;AAAA,IAC5B;AAEA,aAAS,UAAU,SAAS,CAAC,WAAmB;AAC9C,UAAI,SAAU;AACd,iBAAW;AACX,kBAAY;AACZ,cAAQ,MAAM;AAAA,IAChB,CAAC;AAED,oBAAgB,WAAW,MAAM;AAC/B,UAAI,SAAU;AACd,iBAAW;AACX,kBAAY;AACZ,aAAO,IAAI,MAAM,SAAS,CAAC;AAAA,IAC7B,GAAG,OAAO;AAAA,EACZ,CAAC;AACH;",
6
6
  "names": []
7
7
  }
package/build/IPC.mjs CHANGED
@@ -3,7 +3,7 @@ import { debugAndPrintError } from "./Debug.mjs";
3
3
  import { IpcProtocol } from "./Protocol.mjs";
4
4
  import { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from "./utils/Utils.mjs";
5
5
  async function requestFromIPC(presence, publishToChannel, method, args, rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT) {
6
- return new Promise((resolve, reject) => {
6
+ return new Promise(async (resolve, reject) => {
7
7
  let unsubscribeTimeout;
8
8
  const requestId = generateId();
9
9
  const channel = `ipc:${requestId}`;
@@ -11,7 +11,7 @@ async function requestFromIPC(presence, publishToChannel, method, args, rejectio
11
11
  presence.unsubscribe(channel);
12
12
  clearTimeout(unsubscribeTimeout);
13
13
  };
14
- presence.subscribe(channel, (message) => {
14
+ await presence.subscribe(channel, (message) => {
15
15
  const [code, data] = message;
16
16
  if (code === IpcProtocol.SUCCESS) {
17
17
  resolve(data);
@@ -35,7 +35,7 @@ async function requestFromIPC(presence, publishToChannel, method, args, rejectio
35
35
  }, rejectionTimeout);
36
36
  });
37
37
  }
38
- async function subscribeIPC(presence, processId, channel, replyCallback) {
38
+ async function subscribeIPC(presence, channel, replyCallback) {
39
39
  await presence.subscribe(channel, (message) => {
40
40
  const [method, requestId, args] = message;
41
41
  const reply = (code, data) => {
@@ -58,7 +58,30 @@ async function subscribeIPC(presence, processId, channel, replyCallback) {
58
58
  });
59
59
  });
60
60
  }
61
+ function subscribeWithTimeout(presence, channel, timeout) {
62
+ return new Promise((resolve, reject) => {
63
+ let timeoutHandle;
64
+ let resolved = false;
65
+ const unsubscribe = () => {
66
+ presence.unsubscribe(channel);
67
+ clearTimeout(timeoutHandle);
68
+ };
69
+ presence.subscribe(channel, (roomId) => {
70
+ if (resolved) return;
71
+ resolved = true;
72
+ unsubscribe();
73
+ resolve(roomId);
74
+ });
75
+ timeoutHandle = setTimeout(() => {
76
+ if (resolved) return;
77
+ resolved = true;
78
+ unsubscribe();
79
+ reject(new Error("timeout"));
80
+ }, timeout);
81
+ });
82
+ }
61
83
  export {
62
84
  requestFromIPC,
63
- subscribeIPC
85
+ subscribeIPC,
86
+ subscribeWithTimeout
64
87
  };
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.js';\nimport { Presence } from './presence/Presence.js';\nimport { IpcProtocol } from './Protocol.js';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\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
- "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,SAAS;AAEhC,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,GAAG;AAAA,QAAC;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,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,cAAc,QAAQ,IAAI;AAAA,IAEvC,SAAS,GAAG;AACV,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;",
4
+ "sourcesContent": ["import { debugAndPrintError } from './Debug.js';\nimport { Presence } from './presence/Presence.js';\nimport { IpcProtocol } from './Protocol.js';\nimport { generateId, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\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>(async (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 await presence.subscribe(channel, (message: [IpcProtocol, any]) => {\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 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\n/**\n * Wait for a room creation notification via presence publish/subscribe\n */\nexport function subscribeWithTimeout(\n presence: Presence,\n channel: string,\n timeout: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let timeoutHandle: NodeJS.Timeout;\n let resolved = false;\n\n const unsubscribe = () => {\n presence.unsubscribe(channel);\n clearTimeout(timeoutHandle);\n };\n\n presence.subscribe(channel, (roomId: string) => {\n if (resolved) return;\n resolved = true;\n unsubscribe();\n resolve(roomId);\n });\n\n timeoutHandle = setTimeout(() => {\n if (resolved) return;\n resolved = true;\n unsubscribe();\n reject(new Error(\"timeout\"));\n }, timeout);\n });\n}"],
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,OAAO,SAAS,WAAW;AAC/C,QAAI;AAEJ,UAAM,YAAY,WAAW;AAC7B,UAAM,UAAU,OAAO,SAAS;AAEhC,UAAM,cAAc,MAAM;AACxB,eAAS,YAAY,OAAO;AAC5B,mBAAa,kBAAkB;AAAA,IACjC;AAEA,UAAM,SAAS,UAAU,SAAS,CAAC,YAAgC;AACjE,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,GAAG;AAAA,QAAC;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,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,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,cAAc,QAAQ,IAAI;AAAA,IAEvC,SAAS,GAAG;AACV,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;AAKO,SAAS,qBACd,UACA,SACA,SACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI;AACJ,QAAI,WAAW;AAEf,UAAM,cAAc,MAAM;AACxB,eAAS,YAAY,OAAO;AAC5B,mBAAa,aAAa;AAAA,IAC5B;AAEA,aAAS,UAAU,SAAS,CAAC,WAAmB;AAC9C,UAAI,SAAU;AACd,iBAAW;AACX,kBAAY;AACZ,cAAQ,MAAM;AAAA,IAChB,CAAC;AAED,oBAAgB,WAAW,MAAM;AAC/B,UAAI,SAAU;AACd,iBAAW;AACX,kBAAY;AACZ,aAAO,IAAI,MAAM,SAAS,CAAC;AAAA,IAC7B,GAAG,OAAO;AAAA,EACZ,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -123,7 +123,7 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
123
123
  }
124
124
  async function accept() {
125
125
  await onReady;
126
- await (0, import_IPC.subscribeIPC)(presence, processId, getProcessChannel(), (method, args) => {
126
+ await (0, import_IPC.subscribeIPC)(presence, getProcessChannel(), (method, args) => {
127
127
  if (method === "healthcheck") {
128
128
  return true;
129
129
  } else {
@@ -152,13 +152,12 @@ async function joinOrCreate(roomName, clientOptions = {}, authContext) {
152
152
  if (roomId) {
153
153
  room = await driver.findOne({ roomId });
154
154
  }
155
- if (!room) {
155
+ if (!room || room.locked) {
156
156
  room = await findOneRoomAvailable(roomName, clientOptions);
157
157
  }
158
158
  if (!room) {
159
159
  room = await createRoom(roomName, clientOptions);
160
- presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);
161
- presence.expire(`l:${handler.name}:${concurrencyKey}`, import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);
160
+ presence.publish(`concurrent:${handler.name}:${concurrencyKey}`, room.roomId);
162
161
  }
163
162
  return room;
164
163
  });
@@ -524,7 +523,6 @@ async function createRoomReferences(room, init = false) {
524
523
  if (init) {
525
524
  await (0, import_IPC.subscribeIPC)(
526
525
  presence,
527
- processId,
528
526
  getRoomChannel(room.roomId),
529
527
  (method, args) => {
530
528
  return !args && typeof room[method] !== "function" ? room[method] : room[method].apply(room, args);
@@ -550,7 +548,7 @@ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback)
550
548
  } catch (e) {
551
549
  reject(e);
552
550
  } finally {
553
- await presence.hincrby(hkey, concurrencyKey, -1);
551
+ await presence.hincrbyex(hkey, concurrencyKey, -1, import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);
554
552
  }
555
553
  };
556
554
  if (concurrency > 0) {
@@ -560,15 +558,18 @@ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback)
560
558
  handler.name,
561
559
  concurrencyKey
562
560
  );
563
- const result = await presence.brpop(
564
- `l:${handler.name}:${concurrencyKey}`,
565
- import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME + Math.min(concurrency, 3) * 0.2
566
- // add extra milliseconds for each concurrent request
567
- );
568
- return await fulfill(result && result[1]);
569
- } else {
570
- return await fulfill();
561
+ try {
562
+ const roomId = await (0, import_IPC.subscribeWithTimeout)(
563
+ presence,
564
+ `concurrent:${handler.name}:${concurrencyKey}`,
565
+ (import_Utils.MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME + Math.min(concurrency, 3) * 0.2) * 1e3
566
+ // convert to milliseconds
567
+ );
568
+ return await fulfill(roomId);
569
+ } catch (error) {
570
+ }
571
571
  }
572
+ return await fulfill();
572
573
  });
573
574
  }
574
575
  function onClientJoinRoom(room, client) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MatchMaker.ts"],
4
- "sourcesContent": ["import { EventEmitter } from 'events';\nimport { ErrorCode, Protocol } from './Protocol.js';\n\nimport { requestFromIPC, subscribeIPC } from './IPC.js';\n\nimport { Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.js';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.js';\nimport { Room, RoomInternalState } from './Room.js';\n\nimport { LocalPresence } from './presence/LocalPresence.js';\nimport { Presence } from './presence/Presence.js';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.js';\nimport { SeatReservationError } from './errors/SeatReservationError.js';\nimport { ServerError } from './errors/ServerError.js';\n\nimport { IRoomCache, LocalDriver, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver.js';\nimport controller from './matchmaker/controller.js';\nimport * as stats from './Stats.js';\n\nimport { logger } from './Logger.js';\nimport { AuthContext, Client } from './Transport.js';\nimport { Type } from './utils/types.js';\nimport { getHostname } from './discovery/index.js';\nimport { getLockId } from './matchmaker/driver/api.js';\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: IRoomCache;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\nconst events = new EventEmitter();\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\n/**\n * Whether health checks are enabled or not. (default: true)\n *\n * Health checks are automatically performed on theses scenarios:\n * - At startup, to check for leftover/invalid processId's\n * - When a remote room creation request times out\n * - When a remote seat reservation request times out\n */\nlet enableHealthChecks: boolean = true;\nexport function setHealthChecksEnabled(value: boolean) {\n enableHealthChecks = value;\n}\n\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n if (enableHealthChecks) {\n await healthCheckAllProcesses();\n\n /*\n * persist processId every 1 minute\n *\n * FIXME: this is a workaround in case this `processId` gets excluded\n * (`stats.excludeProcess()`) by mistake due to health-check failure\n */\n stats.setAutoPersistInterval();\n }\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n let room: IRoomCache = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n const handler = getHandler(roomName);\n const filterOptions = handler.getFilterOptions(clientOptions);\n const concurrencyKey = getLockId(filterOptions);\n\n //\n // Prevent multiple rooms of same filter from being created concurrently\n //\n await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId?: string) => {\n if (roomId) {\n room = await driver.findOne({ roomId })\n }\n\n if (!room) {\n room = await findOneRoomAvailable(roomName, clientOptions);\n }\n\n if (!room) {\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n room = await createRoom(roomName, clientOptions);\n presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);\n presence.expire(`l:${handler.name}:${concurrencyKey}`, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\n }\n\n return room;\n });\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authContext - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, clientOptions, authContext);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomCache> = {}, sortOptions?: SortOptions) {\n return await driver.query(conditions, sortOptions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param filterOptions - Filter options.\n * @param sortOptions - Sorting options.\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(\n roomName: string,\n filterOptions: ClientOptions,\n additionalSortOptions?: SortOptions,\n) {\n const handler = getHandler(roomName);\n const sortOptions = Object.assign({}, handler.sortOptions ?? {});\n\n if (additionalSortOptions) {\n Object.assign(sortOptions, additionalSortOptions);\n }\n\n return await driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(filterOptions),\n }, sortOptions);\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(roomName, klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: IRoomCache;\n if (selectedProcessId === undefined) {\n\n if (isDevMode && processId === undefined) {\n //\n // WORKAROUND: wait for processId to be available\n // TODO: Remove this check on 1.0\n //\n // - This is a workaround when using matchMaker.createRoom() before the processId is available.\n // - We need to use top-level await to retrieve processId\n //\n await onReady;\n return createRoom(roomName, clientOptions);\n\n } else {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n }\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n if (enableHealthChecks) {\n await stats.excludeProcess(selectedProcessId);\n }\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n //\n // Initialize .state (if set).\n //\n // Define getters and setters for:\n // - autoDispose\n // - patchRate\n //\n room['__init']();\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n\n //\n // emit \"no active rooms\" event when there are no more rooms in this process\n // (used during graceful shutdown)\n //\n if (stats.local.roomCount <= 0) {\n events.emit('no-active-rooms');\n }\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n\n // persist room data only if match-making is enabled\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n await room.listing.save();\n }\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\n/**\n * Get room data by roomId.\n * This method does not return the actual room instance, use `getLocalRoomById` for that.\n */\nexport function getRoomById(roomId: string) {\n return driver.findOne({ roomId });\n}\n\n/**\n * Get local room instance by roomId. (Can return \"undefined\" if the room is not available on this process)\n */\nexport function getLocalRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nasync function lockAndDisposeAll(): Promise<any> {\n // remove processId from room count key\n // (stops accepting new rooms on this process)\n await stats.excludeProcess(processId);\n\n // clear auto-persisting stats interval\n if (enableHealthChecks) {\n stats.clearAutoPersistInterval();\n }\n\n const noActiveRooms = new Deferred();\n if (stats.local.roomCount <= 0) {\n // no active rooms to dispose\n noActiveRooms.resolve();\n\n } else {\n // wait for all rooms to be disposed\n // TODO: set generous timeout in case\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n // - lock all local rooms to prevent new joins\n // - trigger `onBeforeShutdown()` on each room\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n room.lock();\n room.onBeforeShutdown();\n }\n\n await noActiveRooms;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n // - lock existing rooms\n // - stop accepting new rooms on this process\n // - wait for all rooms to be disposed\n await lockAndDisposeAll();\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // make sure rooms are removed from cache\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n // make sure all rooms are disposed\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any) {\n const sessionId: string = authData?.sessionId || generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (\n e.message === \"ipc_timeout\" &&\n !(\n enableHealthChecks &&\n await healthCheckProcessId(room.processId)\n )\n ) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nasync function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {\n const roomClass = getRoomClass(roomName);\n if (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth']) {\n const result = await roomClass['onAuth'](authContext.token, clientOptions, authContext)\n if (!result) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n return result;\n }\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n\n const activeProcessChannels = (typeof(presence.channels) === \"function\") // TODO: remove this check on 0.17\n ? (await presence.channels(\"p:*\")).map(c => c.substring(2))\n : [];\n\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => (\n stat.processId !== processId && // skip current process\n !activeProcessChannels.includes(stat.processId) // skip if channel is still listening\n ))\n .map(stat => healthCheckProcessId(stat.processId))\n );\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n await driver.cleanup(processId);\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\n/**\n * Used only during `joinOrCreate` to handle concurrent requests for creating a room.\n */\nasync function concurrentJoinOrCreateRoomLock(\n handler: RegisteredHandler,\n concurrencyKey: string,\n callback: (roomId?: string) => Promise<IRoomCache>\n): Promise<IRoomCache> {\n return new Promise(async (resolve, reject) => {\n const hkey = getConcurrencyHashKey(handler.name);\n const concurrency = await presence.hincrbyex(\n hkey,\n concurrencyKey,\n 1, // increment by 1\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2 // expire in 2x the time of MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME\n ) - 1; // do not consider the current request\n\n const fulfill = async (roomId?: string) => {\n try {\n resolve(await callback(roomId));\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.hincrby(hkey, concurrencyKey, -1);\n }\n };\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent joinOrCreate for \\'%s\\' (%s)',\n concurrency, handler.name, concurrencyKey\n );\n\n const result = await presence.brpop(\n `l:${handler.name}:${concurrencyKey}`,\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2) // add extra milliseconds for each concurrent request\n );\n\n return await fulfill(result && result[1]);\n\n } else {\n return await fulfill();\n }\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, state === MatchMakerState.SHUTTING_DOWN);\n\n //\n // FIXME: this call should not be necessary.\n //\n // there's an unidentified edge case using LocalDriver where Room._dispose()\n // doesn't seem to be called [?], but \"disposeRoom\" is, leaving the matchmaker\n // in a broken state. (repeated ipc_timeout's for seat reservation on\n // non-existing rooms)\n //\n room.listing.remove();\n stats.local.roomCount--;\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getConcurrencyHashKey(roomName: string) {\n // concurrency hash\n return `ch:${roomName}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EventEmitter } from 'events';\nimport { ErrorCode, Protocol } from './Protocol.js';\n\nimport { requestFromIPC, subscribeIPC, subscribeWithTimeout } from './IPC.js';\n\nimport { Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.js';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.js';\nimport { Room, RoomInternalState } from './Room.js';\n\nimport { LocalPresence } from './presence/LocalPresence.js';\nimport { Presence } from './presence/Presence.js';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.js';\nimport { SeatReservationError } from './errors/SeatReservationError.js';\nimport { ServerError } from './errors/ServerError.js';\n\nimport { IRoomCache, LocalDriver, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver.js';\nimport controller from './matchmaker/controller.js';\nimport * as stats from './Stats.js';\n\nimport { logger } from './Logger.js';\nimport { AuthContext, Client } from './Transport.js';\nimport { Type } from './utils/types.js';\nimport { getHostname } from './discovery/index.js';\nimport { getLockId } from './matchmaker/driver/api.js';\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: IRoomCache;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\nconst events = new EventEmitter();\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\n/**\n * Whether health checks are enabled or not. (default: true)\n *\n * Health checks are automatically performed on theses scenarios:\n * - At startup, to check for leftover/invalid processId's\n * - When a remote room creation request times out\n * - When a remote seat reservation request times out\n */\nlet enableHealthChecks: boolean = true;\nexport function setHealthChecksEnabled(value: boolean) {\n enableHealthChecks = value;\n}\n\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n if (enableHealthChecks) {\n await healthCheckAllProcesses();\n\n /*\n * persist processId every 1 minute\n *\n * FIXME: this is a workaround in case this `processId` gets excluded\n * (`stats.excludeProcess()`) by mistake due to health-check failure\n */\n stats.setAutoPersistInterval();\n }\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n let room: IRoomCache = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n const handler = getHandler(roomName);\n const filterOptions = handler.getFilterOptions(clientOptions);\n const concurrencyKey = getLockId(filterOptions);\n\n //\n // Prevent multiple rooms of same filter from being created concurrently\n //\n await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId?: string) => {\n if (roomId) {\n room = await driver.findOne({ roomId })\n }\n\n // If the room is not found or is already locked, try to find a new one\n if (!room || room.locked) {\n room = await findOneRoomAvailable(roomName, clientOptions);\n }\n\n if (!room) {\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n room = await createRoom(roomName, clientOptions);\n\n // Notify waiting concurrent requests about the new room\n presence.publish(`concurrent:${handler.name}:${concurrencyKey}`, room.roomId);\n }\n\n return room;\n });\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authContext - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, clientOptions, authContext);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomCache> = {}, sortOptions?: SortOptions) {\n return await driver.query(conditions, sortOptions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param filterOptions - Filter options.\n * @param sortOptions - Sorting options.\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(\n roomName: string,\n filterOptions: ClientOptions,\n additionalSortOptions?: SortOptions,\n) {\n const handler = getHandler(roomName);\n const sortOptions = Object.assign({}, handler.sortOptions ?? {});\n\n if (additionalSortOptions) {\n Object.assign(sortOptions, additionalSortOptions);\n }\n\n return await driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(filterOptions),\n }, sortOptions);\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(roomName, klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: IRoomCache;\n if (selectedProcessId === undefined) {\n\n if (isDevMode && processId === undefined) {\n //\n // WORKAROUND: wait for processId to be available\n // TODO: Remove this check on 1.0\n //\n // - This is a workaround when using matchMaker.createRoom() before the processId is available.\n // - We need to use top-level await to retrieve processId\n //\n await onReady;\n return createRoom(roomName, clientOptions);\n\n } else {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n }\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n if (enableHealthChecks) {\n await stats.excludeProcess(selectedProcessId);\n }\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n //\n // Initialize .state (if set).\n //\n // Define getters and setters for:\n // - autoDispose\n // - patchRate\n //\n room['__init']();\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n\n //\n // emit \"no active rooms\" event when there are no more rooms in this process\n // (used during graceful shutdown)\n //\n if (stats.local.roomCount <= 0) {\n events.emit('no-active-rooms');\n }\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n\n // persist room data only if match-making is enabled\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n await room.listing.save();\n }\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\n/**\n * Get room data by roomId.\n * This method does not return the actual room instance, use `getLocalRoomById` for that.\n */\nexport function getRoomById(roomId: string) {\n return driver.findOne({ roomId });\n}\n\n/**\n * Get local room instance by roomId. (Can return \"undefined\" if the room is not available on this process)\n */\nexport function getLocalRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nasync function lockAndDisposeAll(): Promise<any> {\n // remove processId from room count key\n // (stops accepting new rooms on this process)\n await stats.excludeProcess(processId);\n\n // clear auto-persisting stats interval\n if (enableHealthChecks) {\n stats.clearAutoPersistInterval();\n }\n\n const noActiveRooms = new Deferred();\n if (stats.local.roomCount <= 0) {\n // no active rooms to dispose\n noActiveRooms.resolve();\n\n } else {\n // wait for all rooms to be disposed\n // TODO: set generous timeout in case\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n // - lock all local rooms to prevent new joins\n // - trigger `onBeforeShutdown()` on each room\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n room.lock();\n room.onBeforeShutdown();\n }\n\n await noActiveRooms;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n // - lock existing rooms\n // - stop accepting new rooms on this process\n // - wait for all rooms to be disposed\n await lockAndDisposeAll();\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // make sure rooms are removed from cache\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n // make sure all rooms are disposed\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any) {\n const sessionId: string = authData?.sessionId || generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (\n e.message === \"ipc_timeout\" &&\n !(\n enableHealthChecks &&\n await healthCheckProcessId(room.processId)\n )\n ) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nasync function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {\n const roomClass = getRoomClass(roomName);\n if (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth']) {\n const result = await roomClass['onAuth'](authContext.token, clientOptions, authContext)\n if (!result) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n return result;\n }\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n\n const activeProcessChannels = (typeof(presence.channels) === \"function\") // TODO: remove this check on 0.17\n ? (await presence.channels(\"p:*\")).map(c => c.substring(2))\n : [];\n\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => (\n stat.processId !== processId && // skip current process\n !activeProcessChannels.includes(stat.processId) // skip if channel is still listening\n ))\n .map(stat => healthCheckProcessId(stat.processId))\n );\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n await driver.cleanup(processId);\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\n/**\n * Used only during `joinOrCreate` to handle concurrent requests for creating a room.\n */\nasync function concurrentJoinOrCreateRoomLock(\n handler: RegisteredHandler,\n concurrencyKey: string,\n callback: (roomId?: string) => Promise<IRoomCache>\n): Promise<IRoomCache> {\n return new Promise(async (resolve, reject) => {\n const hkey = getConcurrencyHashKey(handler.name);\n const concurrency = await presence.hincrbyex(\n hkey,\n concurrencyKey,\n 1, // increment by 1\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2 // expire in 2x the time of MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME\n ) - 1; // do not consider the current request\n\n const fulfill = async (roomId?: string) => {\n try {\n resolve(await callback(roomId));\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.hincrbyex(hkey, concurrencyKey, -1, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\n }\n };\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent joinOrCreate for \\'%s\\' (%s)',\n concurrency, handler.name, concurrencyKey\n );\n\n try {\n const roomId = await subscribeWithTimeout(\n presence,\n `concurrent:${handler.name}:${concurrencyKey}`,\n (MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2)) * 1000 // convert to milliseconds\n );\n\n return await fulfill(roomId);\n } catch (error) {\n // Ignore ipc_timeout error\n }\n }\n\n return await fulfill();\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, state === MatchMakerState.SHUTTING_DOWN);\n\n //\n // FIXME: this call should not be necessary.\n //\n // there's an unidentified edge case using LocalDriver where Room._dispose()\n // doesn't seem to be called [?], but \"disposeRoom\" is, leaving the matchmaker\n // in a broken state. (repeated ipc_timeout's for seat reservation on\n // non-existing rooms)\n //\n room.listing.remove();\n stats.local.roomCount--;\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getConcurrencyHashKey(roomName: string) {\n // concurrency hash\n return `ch:${roomName}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["controller", "MatchMakerState", "processId"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  // packages/core/src/MatchMaker.ts
2
2
  import { EventEmitter } from "events";
3
3
  import { ErrorCode, Protocol } from "./Protocol.mjs";
4
- import { requestFromIPC, subscribeIPC } from "./IPC.mjs";
4
+ import { requestFromIPC, subscribeIPC, subscribeWithTimeout } from "./IPC.mjs";
5
5
  import { Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT } from "./utils/Utils.mjs";
6
6
  import { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from "./utils/DevMode.mjs";
7
7
  import { RegisteredHandler } from "./matchmaker/RegisteredHandler.mjs";
@@ -58,7 +58,7 @@ async function setup(_presence, _driver, _publicAddress, _selectProcessIdToCreat
58
58
  }
59
59
  async function accept() {
60
60
  await onReady;
61
- await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {
61
+ await subscribeIPC(presence, getProcessChannel(), (method, args) => {
62
62
  if (method === "healthcheck") {
63
63
  return true;
64
64
  } else {
@@ -87,13 +87,12 @@ async function joinOrCreate(roomName, clientOptions = {}, authContext) {
87
87
  if (roomId) {
88
88
  room = await driver.findOne({ roomId });
89
89
  }
90
- if (!room) {
90
+ if (!room || room.locked) {
91
91
  room = await findOneRoomAvailable(roomName, clientOptions);
92
92
  }
93
93
  if (!room) {
94
94
  room = await createRoom(roomName, clientOptions);
95
- presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);
96
- presence.expire(`l:${handler.name}:${concurrencyKey}`, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);
95
+ presence.publish(`concurrent:${handler.name}:${concurrencyKey}`, room.roomId);
97
96
  }
98
97
  return room;
99
98
  });
@@ -459,7 +458,6 @@ async function createRoomReferences(room, init = false) {
459
458
  if (init) {
460
459
  await subscribeIPC(
461
460
  presence,
462
- processId,
463
461
  getRoomChannel(room.roomId),
464
462
  (method, args) => {
465
463
  return !args && typeof room[method] !== "function" ? room[method] : room[method].apply(room, args);
@@ -485,7 +483,7 @@ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback)
485
483
  } catch (e) {
486
484
  reject(e);
487
485
  } finally {
488
- await presence.hincrby(hkey, concurrencyKey, -1);
486
+ await presence.hincrbyex(hkey, concurrencyKey, -1, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);
489
487
  }
490
488
  };
491
489
  if (concurrency > 0) {
@@ -495,15 +493,18 @@ async function concurrentJoinOrCreateRoomLock(handler, concurrencyKey, callback)
495
493
  handler.name,
496
494
  concurrencyKey
497
495
  );
498
- const result = await presence.brpop(
499
- `l:${handler.name}:${concurrencyKey}`,
500
- MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME + Math.min(concurrency, 3) * 0.2
501
- // add extra milliseconds for each concurrent request
502
- );
503
- return await fulfill(result && result[1]);
504
- } else {
505
- return await fulfill();
496
+ try {
497
+ const roomId = await subscribeWithTimeout(
498
+ presence,
499
+ `concurrent:${handler.name}:${concurrencyKey}`,
500
+ (MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME + Math.min(concurrency, 3) * 0.2) * 1e3
501
+ // convert to milliseconds
502
+ );
503
+ return await fulfill(roomId);
504
+ } catch (error) {
505
+ }
506
506
  }
507
+ return await fulfill();
507
508
  });
508
509
  }
509
510
  function onClientJoinRoom(room, client) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MatchMaker.ts"],
4
- "sourcesContent": ["import { EventEmitter } from 'events';\nimport { ErrorCode, Protocol } from './Protocol.js';\n\nimport { requestFromIPC, subscribeIPC } from './IPC.js';\n\nimport { Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.js';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.js';\nimport { Room, RoomInternalState } from './Room.js';\n\nimport { LocalPresence } from './presence/LocalPresence.js';\nimport { Presence } from './presence/Presence.js';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.js';\nimport { SeatReservationError } from './errors/SeatReservationError.js';\nimport { ServerError } from './errors/ServerError.js';\n\nimport { IRoomCache, LocalDriver, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver.js';\nimport controller from './matchmaker/controller.js';\nimport * as stats from './Stats.js';\n\nimport { logger } from './Logger.js';\nimport { AuthContext, Client } from './Transport.js';\nimport { Type } from './utils/types.js';\nimport { getHostname } from './discovery/index.js';\nimport { getLockId } from './matchmaker/driver/api.js';\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: IRoomCache;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\nconst events = new EventEmitter();\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\n/**\n * Whether health checks are enabled or not. (default: true)\n *\n * Health checks are automatically performed on theses scenarios:\n * - At startup, to check for leftover/invalid processId's\n * - When a remote room creation request times out\n * - When a remote seat reservation request times out\n */\nlet enableHealthChecks: boolean = true;\nexport function setHealthChecksEnabled(value: boolean) {\n enableHealthChecks = value;\n}\n\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, processId, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n if (enableHealthChecks) {\n await healthCheckAllProcesses();\n\n /*\n * persist processId every 1 minute\n *\n * FIXME: this is a workaround in case this `processId` gets excluded\n * (`stats.excludeProcess()`) by mistake due to health-check failure\n */\n stats.setAutoPersistInterval();\n }\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n let room: IRoomCache = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n const handler = getHandler(roomName);\n const filterOptions = handler.getFilterOptions(clientOptions);\n const concurrencyKey = getLockId(filterOptions);\n\n //\n // Prevent multiple rooms of same filter from being created concurrently\n //\n await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId?: string) => {\n if (roomId) {\n room = await driver.findOne({ roomId })\n }\n\n if (!room) {\n room = await findOneRoomAvailable(roomName, clientOptions);\n }\n\n if (!room) {\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n room = await createRoom(roomName, clientOptions);\n presence.lpush(`l:${handler.name}:${concurrencyKey}`, room.roomId);\n presence.expire(`l:${handler.name}:${concurrencyKey}`, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\n }\n\n return room;\n });\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authContext - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, clientOptions, authContext);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomCache> = {}, sortOptions?: SortOptions) {\n return await driver.query(conditions, sortOptions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param filterOptions - Filter options.\n * @param sortOptions - Sorting options.\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(\n roomName: string,\n filterOptions: ClientOptions,\n additionalSortOptions?: SortOptions,\n) {\n const handler = getHandler(roomName);\n const sortOptions = Object.assign({}, handler.sortOptions ?? {});\n\n if (additionalSortOptions) {\n Object.assign(sortOptions, additionalSortOptions);\n }\n\n return await driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(filterOptions),\n }, sortOptions);\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(roomName, klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: IRoomCache;\n if (selectedProcessId === undefined) {\n\n if (isDevMode && processId === undefined) {\n //\n // WORKAROUND: wait for processId to be available\n // TODO: Remove this check on 1.0\n //\n // - This is a workaround when using matchMaker.createRoom() before the processId is available.\n // - We need to use top-level await to retrieve processId\n //\n await onReady;\n return createRoom(roomName, clientOptions);\n\n } else {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n }\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n if (enableHealthChecks) {\n await stats.excludeProcess(selectedProcessId);\n }\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n //\n // Initialize .state (if set).\n //\n // Define getters and setters for:\n // - autoDispose\n // - patchRate\n //\n room['__init']();\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n\n //\n // emit \"no active rooms\" event when there are no more rooms in this process\n // (used during graceful shutdown)\n //\n if (stats.local.roomCount <= 0) {\n events.emit('no-active-rooms');\n }\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n\n // persist room data only if match-making is enabled\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n await room.listing.save();\n }\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\n/**\n * Get room data by roomId.\n * This method does not return the actual room instance, use `getLocalRoomById` for that.\n */\nexport function getRoomById(roomId: string) {\n return driver.findOne({ roomId });\n}\n\n/**\n * Get local room instance by roomId. (Can return \"undefined\" if the room is not available on this process)\n */\nexport function getLocalRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nasync function lockAndDisposeAll(): Promise<any> {\n // remove processId from room count key\n // (stops accepting new rooms on this process)\n await stats.excludeProcess(processId);\n\n // clear auto-persisting stats interval\n if (enableHealthChecks) {\n stats.clearAutoPersistInterval();\n }\n\n const noActiveRooms = new Deferred();\n if (stats.local.roomCount <= 0) {\n // no active rooms to dispose\n noActiveRooms.resolve();\n\n } else {\n // wait for all rooms to be disposed\n // TODO: set generous timeout in case\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n // - lock all local rooms to prevent new joins\n // - trigger `onBeforeShutdown()` on each room\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n room.lock();\n room.onBeforeShutdown();\n }\n\n await noActiveRooms;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n // - lock existing rooms\n // - stop accepting new rooms on this process\n // - wait for all rooms to be disposed\n await lockAndDisposeAll();\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // make sure rooms are removed from cache\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n // make sure all rooms are disposed\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any) {\n const sessionId: string = authData?.sessionId || generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (\n e.message === \"ipc_timeout\" &&\n !(\n enableHealthChecks &&\n await healthCheckProcessId(room.processId)\n )\n ) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nasync function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {\n const roomClass = getRoomClass(roomName);\n if (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth']) {\n const result = await roomClass['onAuth'](authContext.token, clientOptions, authContext)\n if (!result) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n return result;\n }\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n\n const activeProcessChannels = (typeof(presence.channels) === \"function\") // TODO: remove this check on 0.17\n ? (await presence.channels(\"p:*\")).map(c => c.substring(2))\n : [];\n\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => (\n stat.processId !== processId && // skip current process\n !activeProcessChannels.includes(stat.processId) // skip if channel is still listening\n ))\n .map(stat => healthCheckProcessId(stat.processId))\n );\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n await driver.cleanup(processId);\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n processId,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\n/**\n * Used only during `joinOrCreate` to handle concurrent requests for creating a room.\n */\nasync function concurrentJoinOrCreateRoomLock(\n handler: RegisteredHandler,\n concurrencyKey: string,\n callback: (roomId?: string) => Promise<IRoomCache>\n): Promise<IRoomCache> {\n return new Promise(async (resolve, reject) => {\n const hkey = getConcurrencyHashKey(handler.name);\n const concurrency = await presence.hincrbyex(\n hkey,\n concurrencyKey,\n 1, // increment by 1\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2 // expire in 2x the time of MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME\n ) - 1; // do not consider the current request\n\n const fulfill = async (roomId?: string) => {\n try {\n resolve(await callback(roomId));\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.hincrby(hkey, concurrencyKey, -1);\n }\n };\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent joinOrCreate for \\'%s\\' (%s)',\n concurrency, handler.name, concurrencyKey\n );\n\n const result = await presence.brpop(\n `l:${handler.name}:${concurrencyKey}`,\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2) // add extra milliseconds for each concurrent request\n );\n\n return await fulfill(result && result[1]);\n\n } else {\n return await fulfill();\n }\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, state === MatchMakerState.SHUTTING_DOWN);\n\n //\n // FIXME: this call should not be necessary.\n //\n // there's an unidentified edge case using LocalDriver where Room._dispose()\n // doesn't seem to be called [?], but \"disposeRoom\" is, leaving the matchmaker\n // in a broken state. (repeated ipc_timeout's for seat reservation on\n // non-existing rooms)\n //\n room.listing.remove();\n stats.local.roomCount--;\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getConcurrencyHashKey(roomName: string) {\n // concurrency hash\n return `ch:${roomName}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EventEmitter } from 'events';\nimport { ErrorCode, Protocol } from './Protocol.js';\n\nimport { requestFromIPC, subscribeIPC, subscribeWithTimeout } from './IPC.js';\n\nimport { Deferred, generateId, merge, retry, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME, REMOTE_ROOM_SHORT_TIMEOUT } from './utils/Utils.js';\nimport { isDevMode, cacheRoomHistory, getPreviousProcessId, getRoomRestoreListKey, reloadFromCache } from './utils/DevMode.js';\n\nimport { RegisteredHandler } from './matchmaker/RegisteredHandler.js';\nimport { Room, RoomInternalState } from './Room.js';\n\nimport { LocalPresence } from './presence/LocalPresence.js';\nimport { Presence } from './presence/Presence.js';\n\nimport { debugAndPrintError, debugMatchMaking } from './Debug.js';\nimport { SeatReservationError } from './errors/SeatReservationError.js';\nimport { ServerError } from './errors/ServerError.js';\n\nimport { IRoomCache, LocalDriver, MatchMakerDriver, SortOptions } from './matchmaker/driver/local/LocalDriver.js';\nimport controller from './matchmaker/controller.js';\nimport * as stats from './Stats.js';\n\nimport { logger } from './Logger.js';\nimport { AuthContext, Client } from './Transport.js';\nimport { Type } from './utils/types.js';\nimport { getHostname } from './discovery/index.js';\nimport { getLockId } from './matchmaker/driver/api.js';\n\nexport { controller, stats, type MatchMakerDriver };\n\nexport type ClientOptions = any;\nexport type SelectProcessIdCallback = (roomName: string, clientOptions: ClientOptions) => Promise<string>;\n\nexport interface SeatReservation {\n sessionId: string;\n room: IRoomCache;\n devMode?: boolean;\n}\n\nconst handlers: {[id: string]: RegisteredHandler} = {};\nconst rooms: {[roomId: string]: Room} = {};\nconst events = new EventEmitter();\n\nexport let publicAddress: string;\nexport let processId: string;\nexport let presence: Presence;\nexport let driver: MatchMakerDriver;\nexport let selectProcessIdToCreateRoom: SelectProcessIdCallback;\n\n/**\n * Whether health checks are enabled or not. (default: true)\n *\n * Health checks are automatically performed on theses scenarios:\n * - At startup, to check for leftover/invalid processId's\n * - When a remote room creation request times out\n * - When a remote seat reservation request times out\n */\nlet enableHealthChecks: boolean = true;\nexport function setHealthChecksEnabled(value: boolean) {\n enableHealthChecks = value;\n}\n\nexport let onReady: Deferred = new Deferred(); // onReady needs to be immediately available to @colyseus/auth integration.\n\nexport enum MatchMakerState {\n INITIALIZING,\n READY,\n SHUTTING_DOWN\n}\n\n/**\n * Internal MatchMaker state\n */\nexport let state: MatchMakerState;\n\n/**\n * @private\n */\nexport async function setup(\n _presence?: Presence,\n _driver?: MatchMakerDriver,\n _publicAddress?: string,\n _selectProcessIdToCreateRoom?: SelectProcessIdCallback,\n) {\n if (onReady === undefined) {\n //\n // for testing purposes only: onReady is turned into undefined on shutdown\n // (needs refactoring.)\n //\n onReady = new Deferred();\n }\n\n state = MatchMakerState.INITIALIZING;\n\n presence = _presence || new LocalPresence();\n\n driver = _driver || new LocalDriver();\n publicAddress = _publicAddress;\n\n stats.reset(false);\n\n // devMode: try to retrieve previous processId\n if (isDevMode) { processId = await getPreviousProcessId(await getHostname()); }\n\n // ensure processId is set\n if (!processId) { processId = generateId(); }\n\n /**\n * Define default `assignRoomToProcessId` method.\n * By default, return the process with least amount of rooms created\n */\n selectProcessIdToCreateRoom = _selectProcessIdToCreateRoom || async function () {\n return (await stats.fetchAll())\n .sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]?.processId || processId;\n };\n\n onReady.resolve();\n}\n\n/**\n * - Accept receiving remote room creation requests\n * - Check for leftover/invalid processId's on startup\n * @private\n */\nexport async function accept() {\n await onReady; // make sure \"processId\" is available\n\n /**\n * Process-level subscription\n * - handle remote process healthcheck\n * - handle remote room creation\n */\n await subscribeIPC(presence, getProcessChannel(), (method, args) => {\n if (method === 'healthcheck') {\n // health check for this processId\n return true;\n\n } else {\n // handle room creation\n return handleCreateRoom.apply(undefined, args);\n }\n });\n\n /**\n * Check for leftover/invalid processId's on startup\n */\n if (enableHealthChecks) {\n await healthCheckAllProcesses();\n\n /*\n * persist processId every 1 minute\n *\n * FIXME: this is a workaround in case this `processId` gets excluded\n * (`stats.excludeProcess()`) by mistake due to health-check failure\n */\n stats.setAutoPersistInterval();\n }\n\n state = MatchMakerState.READY;\n\n await stats.persist();\n\n if (isDevMode) {\n await reloadFromCache();\n }\n}\n\n/**\n * Join or create into a room and return seat reservation\n */\nexport async function joinOrCreate(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n let room: IRoomCache = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n const handler = getHandler(roomName);\n const filterOptions = handler.getFilterOptions(clientOptions);\n const concurrencyKey = getLockId(filterOptions);\n\n //\n // Prevent multiple rooms of same filter from being created concurrently\n //\n await concurrentJoinOrCreateRoomLock(handler, concurrencyKey, async (roomId?: string) => {\n if (roomId) {\n room = await driver.findOne({ roomId })\n }\n\n // If the room is not found or is already locked, try to find a new one\n if (!room || room.locked) {\n room = await findOneRoomAvailable(roomName, clientOptions);\n }\n\n if (!room) {\n //\n // TODO [?]\n // should we expose the \"creator\" auth data of the room during `onCreate()`?\n // it would be useful, though it could be accessed via `onJoin()` for now.\n //\n room = await createRoom(roomName, clientOptions);\n\n // Notify waiting concurrent requests about the new room\n presence.publish(`concurrent:${handler.name}:${concurrencyKey}`, room.roomId);\n }\n\n return room;\n });\n }\n\n return await reserveSeatFor(room, clientOptions, authData);\n }, 5, [SeatReservationError]);\n}\n\n/**\n * Create a room and return seat reservation\n */\nexport async function create(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await createRoom(roomName, clientOptions);\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Join a room and return seat reservation\n */\nexport async function join(roomName: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n return await retry<Promise<SeatReservation>>(async () => {\n const authData = await callOnAuth(roomName, clientOptions, authContext);\n const room = await findOneRoomAvailable(roomName, clientOptions);\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_CRITERIA, `no rooms found with provided criteria`);\n }\n\n return reserveSeatFor(room, clientOptions, authData);\n });\n}\n\n/**\n * Join a room by id and return seat reservation\n */\nexport async function reconnect(roomId: string, clientOptions: ClientOptions = {}) {\n const room = await driver.findOne({ roomId });\n if (!room) {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C room \"${roomId}\" has been disposed. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" has been disposed.`);\n }\n\n // check for reconnection\n const reconnectionToken = clientOptions.reconnectionToken;\n if (!reconnectionToken) { throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `'reconnectionToken' must be provided for reconnection.`); }\n\n\n // respond to re-connection!\n const sessionId = await remoteRoomCall(room.roomId, 'checkReconnectionToken', [reconnectionToken]);\n if (sessionId) {\n return { room, sessionId };\n\n } else {\n // TODO: support a \"logLevel\" out of the box?\n if (process.env.NODE_ENV !== 'production') {\n logger.info(`\u274C reconnection token invalid or expired. Did you miss .allowReconnection()?\\n\uD83D\uDC49 https://docs.colyseus.io/server/room/#allowreconnection-client-seconds`);\n }\n throw new ServerError(ErrorCode.MATCHMAKE_EXPIRED, `reconnection token invalid or expired.`);\n }\n}\n\n/**\n * Join a room by id and return client seat reservation. An exception is thrown if a room is not found for roomId.\n *\n * @param roomId - The Id of the specific room instance.\n * @param clientOptions - Options for the client seat reservation (for `onJoin`/`onAuth`)\n * @param authContext - Optional authentication token\n *\n * @returns Promise<SeatReservation> - A promise which contains `sessionId` and `IRoomCache`.\n */\nexport async function joinById(roomId: string, clientOptions: ClientOptions = {}, authContext?: AuthContext) {\n const room = await driver.findOne({ roomId });\n\n if (!room) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" not found`);\n\n } else if (room.locked) {\n throw new ServerError(ErrorCode.MATCHMAKE_INVALID_ROOM_ID, `room \"${roomId}\" is locked`);\n }\n\n const authData = await callOnAuth(room.name, clientOptions, authContext);\n\n return reserveSeatFor(room, clientOptions, authData);\n}\n\n/**\n * Perform a query for all cached rooms\n */\nexport async function query(conditions: Partial<IRoomCache> = {}, sortOptions?: SortOptions) {\n return await driver.query(conditions, sortOptions);\n}\n\n/**\n * Find for a public and unlocked room available.\n *\n * @param roomName - The Id of the specific room.\n * @param filterOptions - Filter options.\n * @param sortOptions - Sorting options.\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function findOneRoomAvailable(\n roomName: string,\n filterOptions: ClientOptions,\n additionalSortOptions?: SortOptions,\n) {\n const handler = getHandler(roomName);\n const sortOptions = Object.assign({}, handler.sortOptions ?? {});\n\n if (additionalSortOptions) {\n Object.assign(sortOptions, additionalSortOptions);\n }\n\n return await driver.findOne({\n locked: false,\n name: roomName,\n private: false,\n ...handler.getFilterOptions(filterOptions),\n }, sortOptions);\n}\n\n/**\n * Call a method or return a property on a remote room.\n *\n * @param roomId - The Id of the specific room instance.\n * @param method - Method or attribute to call or retrive.\n * @param args - Array of arguments for the method\n *\n * @returns Promise<any> - Returned value from the called or retrieved method/attribute.\n */\nexport async function remoteRoomCall<R= any>(\n roomId: string,\n method: string,\n args?: any[],\n rejectionTimeout = REMOTE_ROOM_SHORT_TIMEOUT,\n): Promise<R> {\n const room = rooms[roomId];\n\n if (!room) {\n try {\n return await requestFromIPC<R>(presence, getRoomChannel(roomId), method, args, rejectionTimeout);\n\n } catch (e) {\n\n //\n // the room cache from an unavailable process might've been used here.\n // perform a health-check on the process before proceeding.\n // (this is a broken state when a process wasn't gracefully shut down)\n //\n if (method === '_reserveSeat' && e.message === \"ipc_timeout\") {\n throw e;\n }\n\n // TODO: for 1.0, consider always throwing previous error directly.\n\n const request = `${method}${args && ' with args ' + JSON.stringify(args) || ''}`;\n throw new ServerError(\n ErrorCode.MATCHMAKE_UNHANDLED,\n `remote room (${roomId}) timed out, requesting \"${request}\". (${rejectionTimeout}ms exceeded)`,\n );\n }\n\n } else {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : (await room[method].apply(room, args && JSON.parse(JSON.stringify(args))));\n }\n}\n\nexport function defineRoomType<T extends Type<Room>>(\n roomName: string,\n klass: T,\n defaultOptions?: Parameters<NonNullable<InstanceType<T>['onCreate']>>[0],\n) {\n const registeredHandler = new RegisteredHandler(roomName, klass, defaultOptions);\n\n handlers[roomName] = registeredHandler;\n\n if (klass.prototype['onAuth'] !== Room.prototype['onAuth']) {\n // TODO: soft-deprecate instance level `onAuth` on 0.16\n // logger.warn(\"DEPRECATION WARNING: onAuth() at the instance level will be deprecated soon. Please use static onAuth() instead.\");\n\n if (klass['onAuth'] !== Room['onAuth']) {\n logger.info(`\u274C \"${roomName}\"'s onAuth() defined at the instance level will be ignored.`);\n }\n }\n\n return registeredHandler;\n}\n\nexport function removeRoomType(roomName: string) {\n delete handlers[roomName];\n}\n\n// TODO: legacy; remove me on 1.0\nexport function hasHandler(roomName: string) {\n logger.warn(\"hasHandler() is deprecated. Use getHandler() instead.\");\n return handlers[roomName] !== undefined;\n}\n\nexport function getHandler(roomName: string) {\n const handler = handlers[roomName];\n\n if (!handler) {\n throw new ServerError(ErrorCode.MATCHMAKE_NO_HANDLER, `provided room name \"${roomName}\" not defined`);\n }\n\n return handler;\n}\n\nexport function getRoomClass(roomName: string): Type<Room> {\n return handlers[roomName]?.klass;\n}\n\n\n/**\n * Creates a new room.\n *\n * @param roomName - The identifier you defined on `gameServer.define()`\n * @param clientOptions - Options for `onCreate`\n *\n * @returns Promise<IRoomCache> - A promise contaning an object which includes room metadata and configurations.\n */\nexport async function createRoom(roomName: string, clientOptions: ClientOptions): Promise<IRoomCache> {\n //\n // - select a process to create the room\n // - use local processId if MatchMaker is not ready yet\n //\n const selectedProcessId = (state === MatchMakerState.READY)\n ? await selectProcessIdToCreateRoom(roomName, clientOptions)\n : processId;\n\n let room: IRoomCache;\n if (selectedProcessId === undefined) {\n\n if (isDevMode && processId === undefined) {\n //\n // WORKAROUND: wait for processId to be available\n // TODO: Remove this check on 1.0\n //\n // - This is a workaround when using matchMaker.createRoom() before the processId is available.\n // - We need to use top-level await to retrieve processId\n //\n await onReady;\n return createRoom(roomName, clientOptions);\n\n } else {\n throw new ServerError(ErrorCode.MATCHMAKE_UNHANDLED, `no processId available to create room ${roomName}`);\n }\n\n } else if (selectedProcessId === processId) {\n // create the room on this process!\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // ask other process to create the room!\n try {\n room = await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(selectedProcessId),\n undefined,\n [roomName, clientOptions],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n if (e.message === \"ipc_timeout\") {\n debugAndPrintError(`${e.message}: create room request timed out for ${roomName} on processId ${selectedProcessId}.`);\n\n //\n // clean-up possibly stale process from redis.\n // when a process disconnects ungracefully, it may leave its previous processId under \"roomcount\"\n // if the process is still alive, it will re-add itself shortly after the load-balancer selects it again.\n //\n if (enableHealthChecks) {\n await stats.excludeProcess(selectedProcessId);\n }\n\n // if other process failed to respond, create the room on this process\n room = await handleCreateRoom(roomName, clientOptions);\n\n } else {\n // re-throw intentional exception thrown during remote onCreate()\n throw e;\n }\n }\n }\n\n if (isDevMode) {\n presence.hset(getRoomRestoreListKey(), room.roomId, JSON.stringify({\n \"clientOptions\": clientOptions,\n \"roomName\": roomName,\n \"processId\": processId\n }));\n }\n\n return room;\n}\n\nexport async function handleCreateRoom(roomName: string, clientOptions: ClientOptions, restoringRoomId?: string): Promise<IRoomCache> {\n const handler = getHandler(roomName);\n const room = new handler.klass();\n\n // set room public attributes\n if (restoringRoomId && isDevMode) {\n room.roomId = restoringRoomId;\n\n } else {\n room.roomId = generateId();\n }\n\n //\n // Initialize .state (if set).\n //\n // Define getters and setters for:\n // - autoDispose\n // - patchRate\n //\n room['__init']();\n\n room.roomName = roomName;\n room.presence = presence;\n\n const additionalListingData: any = handler.getFilterOptions(clientOptions);\n\n // assign public host\n if (publicAddress) {\n additionalListingData.publicAddress = publicAddress;\n }\n\n // create a RoomCache reference.\n room.listing = driver.createInstance({\n name: roomName,\n processId,\n ...additionalListingData\n });\n\n if (room.onCreate) {\n try {\n await room.onCreate(merge({}, clientOptions, handler.options));\n\n } catch (e) {\n debugAndPrintError(e);\n throw new ServerError(\n e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n e.message,\n );\n }\n }\n\n room['_internalState'] = RoomInternalState.CREATED;\n\n room.listing.roomId = room.roomId;\n room.listing.maxClients = room.maxClients;\n\n // imediatelly ask client to join the room\n debugMatchMaking('spawning \\'%s\\', roomId: %s, processId: %s', roomName, room.roomId, processId);\n\n // increment amount of rooms this process is handling\n stats.local.roomCount++;\n stats.persist();\n\n room._events.on('lock', lockRoom.bind(this, room));\n room._events.on('unlock', unlockRoom.bind(this, room));\n room._events.on('join', onClientJoinRoom.bind(this, room));\n room._events.on('leave', onClientLeaveRoom.bind(this, room));\n room._events.on('visibility-change', onVisibilityChange.bind(this, room));\n room._events.once('dispose', disposeRoom.bind(this, roomName, room));\n\n // when disconnect()'ing, keep only join/leave events for stat counting\n room._events.once('disconnect', () => {\n room._events.removeAllListeners('lock');\n room._events.removeAllListeners('unlock');\n room._events.removeAllListeners('visibility-change');\n room._events.removeAllListeners('dispose');\n\n //\n // emit \"no active rooms\" event when there are no more rooms in this process\n // (used during graceful shutdown)\n //\n if (stats.local.roomCount <= 0) {\n events.emit('no-active-rooms');\n }\n });\n\n // room always start unlocked\n await createRoomReferences(room, true);\n\n // persist room data only if match-making is enabled\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n await room.listing.save();\n }\n\n handler.emit('create', room);\n\n return room.listing;\n}\n\n/**\n * Get room data by roomId.\n * This method does not return the actual room instance, use `getLocalRoomById` for that.\n */\nexport function getRoomById(roomId: string) {\n return driver.findOne({ roomId });\n}\n\n/**\n * Get local room instance by roomId. (Can return \"undefined\" if the room is not available on this process)\n */\nexport function getLocalRoomById(roomId: string) {\n return rooms[roomId];\n}\n\n/**\n * Disconnects every client on every room in the current process.\n */\nexport function disconnectAll(closeCode?: number) {\n const promises: Array<Promise<any>> = [];\n\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n promises.push(rooms[roomId].disconnect(closeCode));\n }\n\n return promises;\n}\n\nasync function lockAndDisposeAll(): Promise<any> {\n // remove processId from room count key\n // (stops accepting new rooms on this process)\n await stats.excludeProcess(processId);\n\n // clear auto-persisting stats interval\n if (enableHealthChecks) {\n stats.clearAutoPersistInterval();\n }\n\n const noActiveRooms = new Deferred();\n if (stats.local.roomCount <= 0) {\n // no active rooms to dispose\n noActiveRooms.resolve();\n\n } else {\n // wait for all rooms to be disposed\n // TODO: set generous timeout in case\n events.once('no-active-rooms', () => noActiveRooms.resolve());\n }\n\n // - lock all local rooms to prevent new joins\n // - trigger `onBeforeShutdown()` on each room\n for (const roomId in rooms) {\n if (!rooms.hasOwnProperty(roomId)) {\n continue;\n }\n\n const room = rooms[roomId];\n room.lock();\n room.onBeforeShutdown();\n }\n\n await noActiveRooms;\n}\n\nexport async function gracefullyShutdown(): Promise<any> {\n if (state === MatchMakerState.SHUTTING_DOWN) {\n return Promise.reject('already_shutting_down');\n }\n\n debugMatchMaking(`${processId} is shutting down!`);\n\n state = MatchMakerState.SHUTTING_DOWN;\n\n onReady = undefined;\n\n // - lock existing rooms\n // - stop accepting new rooms on this process\n // - wait for all rooms to be disposed\n await lockAndDisposeAll();\n\n if (isDevMode) {\n await cacheRoomHistory(rooms);\n }\n\n // make sure rooms are removed from cache\n await removeRoomsByProcessId(processId);\n\n // unsubscribe from process id channel\n presence.unsubscribe(getProcessChannel());\n\n // make sure all rooms are disposed\n return Promise.all(disconnectAll(\n (isDevMode)\n ? Protocol.WS_CLOSE_DEVMODE_RESTART\n : undefined\n ));\n}\n\n/**\n * Reserve a seat for a client in a room\n */\nexport async function reserveSeatFor(room: IRoomCache, options: ClientOptions, authData?: any) {\n const sessionId: string = authData?.sessionId || generateId();\n\n debugMatchMaking(\n 'reserving seat. sessionId: \\'%s\\', roomId: \\'%s\\', processId: \\'%s\\'',\n sessionId, room.roomId, processId,\n );\n\n let successfulSeatReservation: boolean;\n\n try {\n successfulSeatReservation = await remoteRoomCall(\n room.roomId,\n '_reserveSeat',\n [sessionId, options, authData],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n } catch (e) {\n debugMatchMaking(e);\n\n //\n // the room cache from an unavailable process might've been used here.\n // (this is a broken state when a process wasn't gracefully shut down)\n // perform a health-check on the process before proceeding.\n //\n if (\n e.message === \"ipc_timeout\" &&\n !(\n enableHealthChecks &&\n await healthCheckProcessId(room.processId)\n )\n ) {\n throw new SeatReservationError(`process ${room.processId} is not available.`);\n\n } else {\n successfulSeatReservation = false;\n }\n }\n\n if (!successfulSeatReservation) {\n throw new SeatReservationError(`${room.roomId} is already full.`);\n }\n\n const response: SeatReservation = { room, sessionId };\n\n if (isDevMode) {\n response.devMode = isDevMode;\n }\n\n return response;\n}\n\nasync function callOnAuth(roomName: string, clientOptions?: ClientOptions, authContext?: AuthContext) {\n const roomClass = getRoomClass(roomName);\n if (roomClass && roomClass['onAuth'] && roomClass['onAuth'] !== Room['onAuth']) {\n const result = await roomClass['onAuth'](authContext.token, clientOptions, authContext)\n if (!result) {\n throw new ServerError(ErrorCode.AUTH_FAILED, 'onAuth failed');\n }\n return result;\n }\n}\n\n/**\n * Perform health check on all processes\n */\nexport async function healthCheckAllProcesses() {\n const allStats = await stats.fetchAll();\n\n const activeProcessChannels = (typeof(presence.channels) === \"function\") // TODO: remove this check on 0.17\n ? (await presence.channels(\"p:*\")).map(c => c.substring(2))\n : [];\n\n if (allStats.length > 0) {\n await Promise.all(\n allStats\n .filter(stat => (\n stat.processId !== processId && // skip current process\n !activeProcessChannels.includes(stat.processId) // skip if channel is still listening\n ))\n .map(stat => healthCheckProcessId(stat.processId))\n );\n }\n}\n\n/**\n * Perform health check on a remote process\n * @param processId\n */\nconst _healthCheckByProcessId: { [processId: string]: Promise<any> } = {};\nexport function healthCheckProcessId(processId: string) {\n //\n // re-use the same promise if health-check is already in progress\n // (may occur when _reserveSeat() fails multiple times for the same 'processId')\n //\n if (_healthCheckByProcessId[processId] !== undefined) {\n return _healthCheckByProcessId[processId];\n }\n\n _healthCheckByProcessId[processId] = new Promise<boolean>(async (resolve, reject) => {\n logger.debug(`> Performing health-check against processId: '${processId}'...`);\n\n try {\n const requestTime = Date.now();\n\n await requestFromIPC<IRoomCache>(\n presence,\n getProcessChannel(processId),\n 'healthcheck',\n [],\n REMOTE_ROOM_SHORT_TIMEOUT,\n );\n\n logger.debug(`\u2705 Process '${processId}' successfully responded (${Date.now() - requestTime}ms)`);\n\n // succeeded to respond\n resolve(true)\n\n } catch (e) {\n // process failed to respond - remove it from stats\n logger.debug(`\u274C Process '${processId}' failed to respond. Cleaning it up.`);\n const isProcessExcluded = await stats.excludeProcess(processId);\n\n // clean-up possibly stale room ids\n if (isProcessExcluded && !isDevMode) {\n await removeRoomsByProcessId(processId);\n }\n\n resolve(false);\n } finally {\n delete _healthCheckByProcessId[processId];\n }\n });\n\n return _healthCheckByProcessId[processId];\n}\n\n/**\n * Remove cached rooms by processId\n * @param processId\n */\nasync function removeRoomsByProcessId(processId: string) {\n //\n // clean-up possibly stale room ids\n // (ungraceful shutdowns using Redis can result on stale room ids still on memory.)\n //\n await driver.cleanup(processId);\n}\n\nasync function createRoomReferences(room: Room, init: boolean = false): Promise<boolean> {\n rooms[room.roomId] = room;\n\n if (init) {\n await subscribeIPC(\n presence,\n getRoomChannel(room.roomId),\n (method, args) => {\n return (!args && typeof (room[method]) !== 'function')\n ? room[method]\n : room[method].apply(room, args);\n },\n );\n }\n\n return true;\n}\n\n/**\n * Used only during `joinOrCreate` to handle concurrent requests for creating a room.\n */\nasync function concurrentJoinOrCreateRoomLock(\n handler: RegisteredHandler,\n concurrencyKey: string,\n callback: (roomId?: string) => Promise<IRoomCache>\n): Promise<IRoomCache> {\n return new Promise(async (resolve, reject) => {\n const hkey = getConcurrencyHashKey(handler.name);\n const concurrency = await presence.hincrbyex(\n hkey,\n concurrencyKey,\n 1, // increment by 1\n MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2 // expire in 2x the time of MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME\n ) - 1; // do not consider the current request\n\n const fulfill = async (roomId?: string) => {\n try {\n resolve(await callback(roomId));\n\n } catch (e) {\n reject(e);\n\n } finally {\n await presence.hincrbyex(hkey, concurrencyKey, -1, MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME * 2);\n }\n };\n\n if (concurrency > 0) {\n debugMatchMaking(\n 'receiving %d concurrent joinOrCreate for \\'%s\\' (%s)',\n concurrency, handler.name, concurrencyKey\n );\n\n try {\n const roomId = await subscribeWithTimeout(\n presence,\n `concurrent:${handler.name}:${concurrencyKey}`,\n (MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME +\n (Math.min(concurrency, 3) * 0.2)) * 1000 // convert to milliseconds\n );\n\n return await fulfill(roomId);\n } catch (error) {\n // Ignore ipc_timeout error\n }\n }\n\n return await fulfill();\n });\n}\n\nfunction onClientJoinRoom(room: Room, client: Client) {\n // increment local CCU\n stats.local.ccu++;\n stats.persist();\n\n handlers[room.roomName].emit('join', room, client);\n}\n\nfunction onClientLeaveRoom(room: Room, client: Client, willDispose: boolean) {\n // decrement local CCU\n stats.local.ccu--;\n stats.persist();\n\n handlers[room.roomName].emit('leave', room, client, willDispose);\n}\n\nfunction lockRoom(room: Room): void {\n // emit public event on registered handler\n handlers[room.roomName].emit('lock', room);\n}\n\nasync function unlockRoom(room: Room) {\n if (await createRoomReferences(room)) {\n // emit public event on registered handler\n handlers[room.roomName].emit('unlock', room);\n }\n}\n\nfunction onVisibilityChange(room: Room, isInvisible: boolean): void {\n handlers[room.roomName].emit('visibility-change', room, isInvisible);\n}\n\nasync function disposeRoom(roomName: string, room: Room) {\n debugMatchMaking('disposing \\'%s\\' (%s) on processId \\'%s\\' (graceful shutdown: %s)', roomName, room.roomId, processId, state === MatchMakerState.SHUTTING_DOWN);\n\n //\n // FIXME: this call should not be necessary.\n //\n // there's an unidentified edge case using LocalDriver where Room._dispose()\n // doesn't seem to be called [?], but \"disposeRoom\" is, leaving the matchmaker\n // in a broken state. (repeated ipc_timeout's for seat reservation on\n // non-existing rooms)\n //\n room.listing.remove();\n stats.local.roomCount--;\n\n // decrease amount of rooms this process is handling\n if (state !== MatchMakerState.SHUTTING_DOWN) {\n stats.persist();\n\n // remove from devMode restore list\n if (isDevMode) {\n await presence.hdel(getRoomRestoreListKey(), room.roomId);\n }\n }\n\n // emit disposal on registered session handler\n handlers[roomName].emit('dispose', room);\n\n // unsubscribe from remote connections\n presence.unsubscribe(getRoomChannel(room.roomId));\n\n // remove actual room reference\n delete rooms[room.roomId];\n}\n\n//\n// Presence keys\n//\nfunction getRoomChannel(roomId: string) {\n return `$${roomId}`;\n}\n\nfunction getConcurrencyHashKey(roomName: string) {\n // concurrency hash\n return `ch:${roomName}`;\n}\n\nfunction getProcessChannel(id: string = processId) {\n return `p:${id}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["MatchMakerState", "processId"]
7
7
  }
@@ -16,7 +16,7 @@ export declare class LocalPresence implements Presence {
16
16
  };
17
17
  private timeouts;
18
18
  constructor();
19
- subscribe(topic: string, callback: (...args: any[]) => void): this;
19
+ subscribe(topic: string, callback: (...args: any[]) => void): Promise<Awaited<this>>;
20
20
  unsubscribe(topic: string, callback?: Callback): this;
21
21
  publish(topic: string, data: any): this;
22
22
  channels(pattern?: string): Promise<string[]>;
@@ -59,7 +59,7 @@ class LocalPresence {
59
59
  }
60
60
  subscribe(topic, callback) {
61
61
  this.subscriptions.on(topic, callback);
62
- return this;
62
+ return Promise.resolve(this);
63
63
  }
64
64
  unsubscribe(topic, callback) {
65
65
  if (callback) {
@@ -173,11 +173,17 @@ class LocalPresence {
173
173
  let value = Number(this.hash[key][field] || "0");
174
174
  value += incrBy;
175
175
  this.hash[key][field] = value.toString();
176
- this.setex(key, field, expireInSeconds);
176
+ if (this.timeouts[key]) {
177
+ clearTimeout(this.timeouts[key]);
178
+ }
179
+ this.timeouts[key] = setTimeout(() => {
180
+ delete this.hash[key];
181
+ delete this.timeouts[key];
182
+ }, expireInSeconds * 1e3);
177
183
  return Promise.resolve(value);
178
184
  }
179
185
  async hget(key, field) {
180
- return this.hash[key] && this.hash[key][field];
186
+ return typeof this.hash[key] === "object" ? this.hash[key][field] ?? null : null;
181
187
  }
182
188
  async hgetall(key) {
183
189
  return this.hash[key] || {};
@@ -236,7 +242,7 @@ class LocalPresence {
236
242
  return Promise.resolve(this.data[key].pop());
237
243
  }
238
244
  brpop(...args) {
239
- const keys = args.slice(0, -2);
245
+ const keys = args.slice(0, -1);
240
246
  const timeoutInSeconds = args[args.length - 1];
241
247
  const getFirstPopulated = () => {
242
248
  const keyWithValue = keys.find((key) => this.data[key] && this.data[key].length > 0);
@@ -261,7 +267,7 @@ class LocalPresence {
261
267
  return resolve(firstPopulated2);
262
268
  } else if (tries >= maxRetries) {
263
269
  clearInterval(interval);
264
- return resolve(void 0);
270
+ return resolve(null);
265
271
  }
266
272
  }, timeoutInSeconds * 1e3 / maxRetries);
267
273
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/presence/LocalPresence.ts"],
4
- "sourcesContent": ["import fs from 'fs';\nimport path from 'path';\n\nimport { EventEmitter } from 'events';\nimport { spliceOne } from '../utils/Utils.js';\nimport { Presence } from './Presence.js';\n\nimport { isDevMode } from '../utils/DevMode.js';\n\ntype Callback = (...args: any[]) => void;\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport class LocalPresence implements Presence {\n public subscriptions = new EventEmitter();\n\n public data: {[roomName: string]: string[]} = {};\n public hash: {[roomName: string]: {[key: string]: string}} = {};\n\n public keys: {[name: string]: string | number} = {};\n\n private timeouts: {[name: string]: NodeJS.Timeout} = {};\n\n constructor() {\n //\n // reload from local cache on devMode\n //\n if (\n isDevMode &&\n fs.existsSync(DEVMODE_CACHE_FILE_PATH)\n ) {\n const cache = fs.readFileSync(DEVMODE_CACHE_FILE_PATH).toString('utf-8') || \"{}\";\n const parsed = JSON.parse(cache);\n if (parsed.data) { this.data = parsed.data; }\n if (parsed.hash) { this.hash = parsed.hash; }\n if (parsed.keys) { this.keys = parsed.keys; }\n }\n }\n\n public subscribe(topic: string, callback: (...args: any[]) => void) {\n this.subscriptions.on(topic, callback);\n return this;\n }\n\n public unsubscribe(topic: string, callback?: Callback) {\n if (callback) {\n this.subscriptions.removeListener(topic, callback);\n\n } else {\n this.subscriptions.removeAllListeners(topic);\n }\n\n return this;\n }\n\n public publish(topic: string, data: any) {\n this.subscriptions.emit(topic, data);\n return this;\n }\n\n public async channels (pattern?: string) {\n let eventNames = this.subscriptions.eventNames() as string[];\n if (pattern) {\n //\n // This is a limited glob pattern to regexp implementation.\n // If needed, we can use a full implementation like picomatch: https://github.com/micromatch/picomatch/\n //\n const regexp = new RegExp(\n pattern.\n replaceAll(\".\", \"\\\\.\").\n replaceAll(\"$\", \"\\\\$\").\n replaceAll(\"*\", \".*\").\n replaceAll(\"?\", \".\"),\n \"gi\"\n );\n eventNames = eventNames.filter((eventName) => regexp.test(eventName));\n }\n return eventNames;\n }\n\n public async exists(key: string): Promise<boolean> {\n return (\n this.keys[key] !== undefined ||\n this.data[key] !== undefined ||\n this.hash[key] !== undefined\n );\n }\n\n public set(key: string, value: string) {\n this.keys[key] = value;\n }\n\n public setex(key: string, value: string, seconds: number) {\n this.keys[key] = value;\n this.expire(key, seconds);\n }\n\n public expire(key: string, seconds: number) {\n // ensure previous timeout is clear before setting another one.\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.keys[key];\n delete this.timeouts[key];\n }, seconds * 1000);\n }\n\n public get(key: string) {\n return this.keys[key];\n }\n\n public del(key: string) {\n delete this.keys[key];\n delete this.data[key];\n delete this.hash[key];\n }\n\n public sadd(key: string, value: any) {\n if (!this.data[key]) {\n this.data[key] = [];\n }\n\n if (this.data[key].indexOf(value) === -1) {\n this.data[key].push(value);\n }\n }\n\n public async smembers(key: string): Promise<string[]> {\n return this.data[key] || [];\n }\n\n public async sismember(key: string, field: string) {\n return this.data[key] && this.data[key].includes(field) ? 1 : 0;\n }\n\n public srem(key: string, value: any) {\n if (this.data[key]) {\n spliceOne(this.data[key], this.data[key].indexOf(value));\n }\n }\n\n public scard(key: string) {\n return (this.data[key] || []).length;\n }\n\n public async sinter(...keys: string[]) {\n const intersection: {[value: string]: number} = {};\n\n for (let i = 0, l = keys.length; i < l; i++) {\n (await this.smembers(keys[i])).forEach((member) => {\n if (!intersection[member]) {\n intersection[member] = 0;\n }\n\n intersection[member]++;\n });\n }\n\n return Object.keys(intersection).reduce((prev, curr) => {\n if (intersection[curr] > 1) {\n prev.push(curr);\n }\n return prev;\n }, []);\n }\n\n public hset(key: string, field: string, value: string) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n this.hash[key][field] = value;\n return Promise.resolve(true);\n }\n\n public hincrby(key: string, field: string, incrBy: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n return Promise.resolve(value);\n }\n\n public hincrbyex(key: string, field: string, incrBy: number, expireInSeconds: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n this.setex(key, field, expireInSeconds);\n return Promise.resolve(value);\n }\n\n public async hget(key: string, field: string) {\n return this.hash[key] && this.hash[key][field];\n }\n\n public async hgetall(key: string) {\n return this.hash[key] || {};\n }\n\n public hdel(key: string, field: any) {\n const success = this.hash?.[key]?.[field] !== undefined;\n if (success) {\n delete this.hash[key][field];\n }\n return Promise.resolve(success);\n }\n\n public async hlen(key: string) {\n return this.hash[key] && Object.keys(this.hash[key]).length || 0;\n }\n\n public async incr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)++;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public async decr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)--;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public llen(key: string) {\n return Promise.resolve((this.data[key] && this.data[key].length) || 0);\n }\n\n public rpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].push(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].unshift(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpop(key: string): Promise<string> {\n return Promise.resolve(Array.isArray(this.data[key])\n ? this.data[key].shift()\n : null);\n }\n\n public rpop(key: string): Promise<string | null> {\n return Promise.resolve(this.data[key].pop());\n }\n\n public brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null> {\n const keys = args.slice(0, -2) as string[];\n const timeoutInSeconds = args[args.length - 1] as number;\n\n const getFirstPopulated = (): [string, string] | null => {\n const keyWithValue = keys.find(key => this.data[key] && this.data[key].length > 0);\n if (keyWithValue) {\n return [keyWithValue, this.data[keyWithValue].pop()];\n } else {\n return null;\n }\n }\n\n const firstPopulated = getFirstPopulated();\n\n if (firstPopulated) {\n // return first populated key + item\n return Promise.resolve(firstPopulated);\n\n } else {\n // 8 retries per second\n const maxRetries = timeoutInSeconds * 8;\n\n let tries = 0;\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n tries++;\n\n const firstPopulated = getFirstPopulated();\n if (firstPopulated) {\n clearInterval(interval);\n return resolve(firstPopulated);\n\n } else if (tries >= maxRetries) {\n clearInterval(interval);\n return resolve(undefined);\n }\n\n }, (timeoutInSeconds * 1000) / maxRetries);\n });\n }\n }\n\n public setMaxListeners(number: number) {\n this.subscriptions.setMaxListeners(number);\n }\n\n public shutdown() {\n if (isDevMode) {\n const cache = JSON.stringify({\n data: this.data,\n hash: this.hash,\n keys: this.keys\n });\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, cache, { encoding: \"utf-8\" });\n }\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fs from 'fs';\nimport path from 'path';\n\nimport { EventEmitter } from 'events';\nimport { spliceOne } from '../utils/Utils.js';\nimport { Presence } from './Presence.js';\n\nimport { isDevMode } from '../utils/DevMode.js';\n\ntype Callback = (...args: any[]) => void;\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport class LocalPresence implements Presence {\n public subscriptions = new EventEmitter();\n\n public data: {[roomName: string]: string[]} = {};\n public hash: {[roomName: string]: {[key: string]: string}} = {};\n\n public keys: {[name: string]: string | number} = {};\n\n private timeouts: {[name: string]: NodeJS.Timeout} = {};\n\n constructor() {\n //\n // reload from local cache on devMode\n //\n if (\n isDevMode &&\n fs.existsSync(DEVMODE_CACHE_FILE_PATH)\n ) {\n const cache = fs.readFileSync(DEVMODE_CACHE_FILE_PATH).toString('utf-8') || \"{}\";\n const parsed = JSON.parse(cache);\n if (parsed.data) { this.data = parsed.data; }\n if (parsed.hash) { this.hash = parsed.hash; }\n if (parsed.keys) { this.keys = parsed.keys; }\n }\n }\n\n public subscribe(topic: string, callback: (...args: any[]) => void) {\n this.subscriptions.on(topic, callback);\n return Promise.resolve(this);\n }\n\n public unsubscribe(topic: string, callback?: Callback) {\n if (callback) {\n this.subscriptions.removeListener(topic, callback);\n\n } else {\n this.subscriptions.removeAllListeners(topic);\n }\n\n return this;\n }\n\n public publish(topic: string, data: any) {\n this.subscriptions.emit(topic, data);\n return this;\n }\n\n public async channels (pattern?: string) {\n let eventNames = this.subscriptions.eventNames() as string[];\n if (pattern) {\n //\n // This is a limited glob pattern to regexp implementation.\n // If needed, we can use a full implementation like picomatch: https://github.com/micromatch/picomatch/\n //\n const regexp = new RegExp(\n pattern.\n replaceAll(\".\", \"\\\\.\").\n replaceAll(\"$\", \"\\\\$\").\n replaceAll(\"*\", \".*\").\n replaceAll(\"?\", \".\"),\n \"gi\"\n );\n eventNames = eventNames.filter((eventName) => regexp.test(eventName));\n }\n return eventNames;\n }\n\n public async exists(key: string): Promise<boolean> {\n return (\n this.keys[key] !== undefined ||\n this.data[key] !== undefined ||\n this.hash[key] !== undefined\n );\n }\n\n public set(key: string, value: string) {\n this.keys[key] = value;\n }\n\n public setex(key: string, value: string, seconds: number) {\n this.keys[key] = value;\n this.expire(key, seconds);\n }\n\n public expire(key: string, seconds: number) {\n // ensure previous timeout is clear before setting another one.\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.keys[key];\n delete this.timeouts[key];\n }, seconds * 1000);\n }\n\n public get(key: string) {\n return this.keys[key];\n }\n\n public del(key: string) {\n delete this.keys[key];\n delete this.data[key];\n delete this.hash[key];\n }\n\n public sadd(key: string, value: any) {\n if (!this.data[key]) {\n this.data[key] = [];\n }\n\n if (this.data[key].indexOf(value) === -1) {\n this.data[key].push(value);\n }\n }\n\n public async smembers(key: string): Promise<string[]> {\n return this.data[key] || [];\n }\n\n public async sismember(key: string, field: string) {\n return this.data[key] && this.data[key].includes(field) ? 1 : 0;\n }\n\n public srem(key: string, value: any) {\n if (this.data[key]) {\n spliceOne(this.data[key], this.data[key].indexOf(value));\n }\n }\n\n public scard(key: string) {\n return (this.data[key] || []).length;\n }\n\n public async sinter(...keys: string[]) {\n const intersection: {[value: string]: number} = {};\n\n for (let i = 0, l = keys.length; i < l; i++) {\n (await this.smembers(keys[i])).forEach((member) => {\n if (!intersection[member]) {\n intersection[member] = 0;\n }\n\n intersection[member]++;\n });\n }\n\n return Object.keys(intersection).reduce((prev, curr) => {\n if (intersection[curr] > 1) {\n prev.push(curr);\n }\n return prev;\n }, []);\n }\n\n public hset(key: string, field: string, value: string) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n this.hash[key][field] = value;\n return Promise.resolve(true);\n }\n\n public hincrby(key: string, field: string, incrBy: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n return Promise.resolve(value);\n }\n\n public hincrbyex(key: string, field: string, incrBy: number, expireInSeconds: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n\n //\n // FIXME: delete only hash[key][field]\n // (we can't use \"HEXPIRE\" in Redis because it's only available since Redis version 7.4.0+)\n //\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.hash[key];\n delete this.timeouts[key];\n }, expireInSeconds * 1000);\n\n return Promise.resolve(value);\n }\n\n public async hget(key: string, field: string) {\n return (typeof(this.hash[key]) === 'object')\n ? this.hash[key][field] ?? null\n : null;\n }\n\n public async hgetall(key: string) {\n return this.hash[key] || {};\n }\n\n public hdel(key: string, field: any) {\n const success = this.hash?.[key]?.[field] !== undefined;\n if (success) {\n delete this.hash[key][field];\n }\n return Promise.resolve(success);\n }\n\n public async hlen(key: string) {\n return this.hash[key] && Object.keys(this.hash[key]).length || 0;\n }\n\n public async incr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)++;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public async decr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)--;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public llen(key: string) {\n return Promise.resolve((this.data[key] && this.data[key].length) || 0);\n }\n\n public rpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].push(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].unshift(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpop(key: string): Promise<string> {\n return Promise.resolve(Array.isArray(this.data[key])\n ? this.data[key].shift()\n : null);\n }\n\n public rpop(key: string): Promise<string | null> {\n return Promise.resolve(this.data[key].pop());\n }\n\n public brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null> {\n const keys = args.slice(0, -1) as string[];\n const timeoutInSeconds = args[args.length - 1] as number;\n\n const getFirstPopulated = (): [string, string] | null => {\n const keyWithValue = keys.find(key => this.data[key] && this.data[key].length > 0);\n if (keyWithValue) {\n return [keyWithValue, this.data[keyWithValue].pop()];\n } else {\n return null;\n }\n }\n\n const firstPopulated = getFirstPopulated();\n\n if (firstPopulated) {\n // return first populated key + item\n return Promise.resolve(firstPopulated);\n\n } else {\n // 8 retries per second\n const maxRetries = timeoutInSeconds * 8;\n\n let tries = 0;\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n tries++;\n\n const firstPopulated = getFirstPopulated();\n if (firstPopulated) {\n clearInterval(interval);\n return resolve(firstPopulated);\n\n } else if (tries >= maxRetries) {\n clearInterval(interval);\n return resolve(null);\n }\n\n }, (timeoutInSeconds * 1000) / maxRetries);\n });\n }\n }\n\n public setMaxListeners(number: number) {\n this.subscriptions.setMaxListeners(number);\n }\n\n public shutdown() {\n if (isDevMode) {\n const cache = JSON.stringify({\n data: this.data,\n hash: this.hash,\n keys: this.keys\n });\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, cache, { encoding: \"utf-8\" });\n }\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "fs", "firstPopulated"]
7
7
  }
@@ -28,7 +28,7 @@ var LocalPresence = class {
28
28
  }
29
29
  subscribe(topic, callback) {
30
30
  this.subscriptions.on(topic, callback);
31
- return this;
31
+ return Promise.resolve(this);
32
32
  }
33
33
  unsubscribe(topic, callback) {
34
34
  if (callback) {
@@ -142,11 +142,17 @@ var LocalPresence = class {
142
142
  let value = Number(this.hash[key][field] || "0");
143
143
  value += incrBy;
144
144
  this.hash[key][field] = value.toString();
145
- this.setex(key, field, expireInSeconds);
145
+ if (this.timeouts[key]) {
146
+ clearTimeout(this.timeouts[key]);
147
+ }
148
+ this.timeouts[key] = setTimeout(() => {
149
+ delete this.hash[key];
150
+ delete this.timeouts[key];
151
+ }, expireInSeconds * 1e3);
146
152
  return Promise.resolve(value);
147
153
  }
148
154
  async hget(key, field) {
149
- return this.hash[key] && this.hash[key][field];
155
+ return typeof this.hash[key] === "object" ? this.hash[key][field] ?? null : null;
150
156
  }
151
157
  async hgetall(key) {
152
158
  return this.hash[key] || {};
@@ -205,7 +211,7 @@ var LocalPresence = class {
205
211
  return Promise.resolve(this.data[key].pop());
206
212
  }
207
213
  brpop(...args) {
208
- const keys = args.slice(0, -2);
214
+ const keys = args.slice(0, -1);
209
215
  const timeoutInSeconds = args[args.length - 1];
210
216
  const getFirstPopulated = () => {
211
217
  const keyWithValue = keys.find((key) => this.data[key] && this.data[key].length > 0);
@@ -230,7 +236,7 @@ var LocalPresence = class {
230
236
  return resolve(firstPopulated2);
231
237
  } else if (tries >= maxRetries) {
232
238
  clearInterval(interval);
233
- return resolve(void 0);
239
+ return resolve(null);
234
240
  }
235
241
  }, timeoutInSeconds * 1e3 / maxRetries);
236
242
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/presence/LocalPresence.ts"],
4
- "sourcesContent": ["import fs from 'fs';\nimport path from 'path';\n\nimport { EventEmitter } from 'events';\nimport { spliceOne } from '../utils/Utils.js';\nimport { Presence } from './Presence.js';\n\nimport { isDevMode } from '../utils/DevMode.js';\n\ntype Callback = (...args: any[]) => void;\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport class LocalPresence implements Presence {\n public subscriptions = new EventEmitter();\n\n public data: {[roomName: string]: string[]} = {};\n public hash: {[roomName: string]: {[key: string]: string}} = {};\n\n public keys: {[name: string]: string | number} = {};\n\n private timeouts: {[name: string]: NodeJS.Timeout} = {};\n\n constructor() {\n //\n // reload from local cache on devMode\n //\n if (\n isDevMode &&\n fs.existsSync(DEVMODE_CACHE_FILE_PATH)\n ) {\n const cache = fs.readFileSync(DEVMODE_CACHE_FILE_PATH).toString('utf-8') || \"{}\";\n const parsed = JSON.parse(cache);\n if (parsed.data) { this.data = parsed.data; }\n if (parsed.hash) { this.hash = parsed.hash; }\n if (parsed.keys) { this.keys = parsed.keys; }\n }\n }\n\n public subscribe(topic: string, callback: (...args: any[]) => void) {\n this.subscriptions.on(topic, callback);\n return this;\n }\n\n public unsubscribe(topic: string, callback?: Callback) {\n if (callback) {\n this.subscriptions.removeListener(topic, callback);\n\n } else {\n this.subscriptions.removeAllListeners(topic);\n }\n\n return this;\n }\n\n public publish(topic: string, data: any) {\n this.subscriptions.emit(topic, data);\n return this;\n }\n\n public async channels (pattern?: string) {\n let eventNames = this.subscriptions.eventNames() as string[];\n if (pattern) {\n //\n // This is a limited glob pattern to regexp implementation.\n // If needed, we can use a full implementation like picomatch: https://github.com/micromatch/picomatch/\n //\n const regexp = new RegExp(\n pattern.\n replaceAll(\".\", \"\\\\.\").\n replaceAll(\"$\", \"\\\\$\").\n replaceAll(\"*\", \".*\").\n replaceAll(\"?\", \".\"),\n \"gi\"\n );\n eventNames = eventNames.filter((eventName) => regexp.test(eventName));\n }\n return eventNames;\n }\n\n public async exists(key: string): Promise<boolean> {\n return (\n this.keys[key] !== undefined ||\n this.data[key] !== undefined ||\n this.hash[key] !== undefined\n );\n }\n\n public set(key: string, value: string) {\n this.keys[key] = value;\n }\n\n public setex(key: string, value: string, seconds: number) {\n this.keys[key] = value;\n this.expire(key, seconds);\n }\n\n public expire(key: string, seconds: number) {\n // ensure previous timeout is clear before setting another one.\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.keys[key];\n delete this.timeouts[key];\n }, seconds * 1000);\n }\n\n public get(key: string) {\n return this.keys[key];\n }\n\n public del(key: string) {\n delete this.keys[key];\n delete this.data[key];\n delete this.hash[key];\n }\n\n public sadd(key: string, value: any) {\n if (!this.data[key]) {\n this.data[key] = [];\n }\n\n if (this.data[key].indexOf(value) === -1) {\n this.data[key].push(value);\n }\n }\n\n public async smembers(key: string): Promise<string[]> {\n return this.data[key] || [];\n }\n\n public async sismember(key: string, field: string) {\n return this.data[key] && this.data[key].includes(field) ? 1 : 0;\n }\n\n public srem(key: string, value: any) {\n if (this.data[key]) {\n spliceOne(this.data[key], this.data[key].indexOf(value));\n }\n }\n\n public scard(key: string) {\n return (this.data[key] || []).length;\n }\n\n public async sinter(...keys: string[]) {\n const intersection: {[value: string]: number} = {};\n\n for (let i = 0, l = keys.length; i < l; i++) {\n (await this.smembers(keys[i])).forEach((member) => {\n if (!intersection[member]) {\n intersection[member] = 0;\n }\n\n intersection[member]++;\n });\n }\n\n return Object.keys(intersection).reduce((prev, curr) => {\n if (intersection[curr] > 1) {\n prev.push(curr);\n }\n return prev;\n }, []);\n }\n\n public hset(key: string, field: string, value: string) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n this.hash[key][field] = value;\n return Promise.resolve(true);\n }\n\n public hincrby(key: string, field: string, incrBy: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n return Promise.resolve(value);\n }\n\n public hincrbyex(key: string, field: string, incrBy: number, expireInSeconds: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n this.setex(key, field, expireInSeconds);\n return Promise.resolve(value);\n }\n\n public async hget(key: string, field: string) {\n return this.hash[key] && this.hash[key][field];\n }\n\n public async hgetall(key: string) {\n return this.hash[key] || {};\n }\n\n public hdel(key: string, field: any) {\n const success = this.hash?.[key]?.[field] !== undefined;\n if (success) {\n delete this.hash[key][field];\n }\n return Promise.resolve(success);\n }\n\n public async hlen(key: string) {\n return this.hash[key] && Object.keys(this.hash[key]).length || 0;\n }\n\n public async incr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)++;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public async decr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)--;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public llen(key: string) {\n return Promise.resolve((this.data[key] && this.data[key].length) || 0);\n }\n\n public rpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].push(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].unshift(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpop(key: string): Promise<string> {\n return Promise.resolve(Array.isArray(this.data[key])\n ? this.data[key].shift()\n : null);\n }\n\n public rpop(key: string): Promise<string | null> {\n return Promise.resolve(this.data[key].pop());\n }\n\n public brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null> {\n const keys = args.slice(0, -2) as string[];\n const timeoutInSeconds = args[args.length - 1] as number;\n\n const getFirstPopulated = (): [string, string] | null => {\n const keyWithValue = keys.find(key => this.data[key] && this.data[key].length > 0);\n if (keyWithValue) {\n return [keyWithValue, this.data[keyWithValue].pop()];\n } else {\n return null;\n }\n }\n\n const firstPopulated = getFirstPopulated();\n\n if (firstPopulated) {\n // return first populated key + item\n return Promise.resolve(firstPopulated);\n\n } else {\n // 8 retries per second\n const maxRetries = timeoutInSeconds * 8;\n\n let tries = 0;\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n tries++;\n\n const firstPopulated = getFirstPopulated();\n if (firstPopulated) {\n clearInterval(interval);\n return resolve(firstPopulated);\n\n } else if (tries >= maxRetries) {\n clearInterval(interval);\n return resolve(undefined);\n }\n\n }, (timeoutInSeconds * 1000) / maxRetries);\n });\n }\n }\n\n public setMaxListeners(number: number) {\n this.subscriptions.setMaxListeners(number);\n }\n\n public shutdown() {\n if (isDevMode) {\n const cache = JSON.stringify({\n data: this.data,\n hash: this.hash,\n keys: this.keys\n });\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, cache, { encoding: \"utf-8\" });\n }\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fs from 'fs';\nimport path from 'path';\n\nimport { EventEmitter } from 'events';\nimport { spliceOne } from '../utils/Utils.js';\nimport { Presence } from './Presence.js';\n\nimport { isDevMode } from '../utils/DevMode.js';\n\ntype Callback = (...args: any[]) => void;\n\nconst DEVMODE_CACHE_FILE_PATH = path.resolve(\".devmode.json\");\n\nexport class LocalPresence implements Presence {\n public subscriptions = new EventEmitter();\n\n public data: {[roomName: string]: string[]} = {};\n public hash: {[roomName: string]: {[key: string]: string}} = {};\n\n public keys: {[name: string]: string | number} = {};\n\n private timeouts: {[name: string]: NodeJS.Timeout} = {};\n\n constructor() {\n //\n // reload from local cache on devMode\n //\n if (\n isDevMode &&\n fs.existsSync(DEVMODE_CACHE_FILE_PATH)\n ) {\n const cache = fs.readFileSync(DEVMODE_CACHE_FILE_PATH).toString('utf-8') || \"{}\";\n const parsed = JSON.parse(cache);\n if (parsed.data) { this.data = parsed.data; }\n if (parsed.hash) { this.hash = parsed.hash; }\n if (parsed.keys) { this.keys = parsed.keys; }\n }\n }\n\n public subscribe(topic: string, callback: (...args: any[]) => void) {\n this.subscriptions.on(topic, callback);\n return Promise.resolve(this);\n }\n\n public unsubscribe(topic: string, callback?: Callback) {\n if (callback) {\n this.subscriptions.removeListener(topic, callback);\n\n } else {\n this.subscriptions.removeAllListeners(topic);\n }\n\n return this;\n }\n\n public publish(topic: string, data: any) {\n this.subscriptions.emit(topic, data);\n return this;\n }\n\n public async channels (pattern?: string) {\n let eventNames = this.subscriptions.eventNames() as string[];\n if (pattern) {\n //\n // This is a limited glob pattern to regexp implementation.\n // If needed, we can use a full implementation like picomatch: https://github.com/micromatch/picomatch/\n //\n const regexp = new RegExp(\n pattern.\n replaceAll(\".\", \"\\\\.\").\n replaceAll(\"$\", \"\\\\$\").\n replaceAll(\"*\", \".*\").\n replaceAll(\"?\", \".\"),\n \"gi\"\n );\n eventNames = eventNames.filter((eventName) => regexp.test(eventName));\n }\n return eventNames;\n }\n\n public async exists(key: string): Promise<boolean> {\n return (\n this.keys[key] !== undefined ||\n this.data[key] !== undefined ||\n this.hash[key] !== undefined\n );\n }\n\n public set(key: string, value: string) {\n this.keys[key] = value;\n }\n\n public setex(key: string, value: string, seconds: number) {\n this.keys[key] = value;\n this.expire(key, seconds);\n }\n\n public expire(key: string, seconds: number) {\n // ensure previous timeout is clear before setting another one.\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.keys[key];\n delete this.timeouts[key];\n }, seconds * 1000);\n }\n\n public get(key: string) {\n return this.keys[key];\n }\n\n public del(key: string) {\n delete this.keys[key];\n delete this.data[key];\n delete this.hash[key];\n }\n\n public sadd(key: string, value: any) {\n if (!this.data[key]) {\n this.data[key] = [];\n }\n\n if (this.data[key].indexOf(value) === -1) {\n this.data[key].push(value);\n }\n }\n\n public async smembers(key: string): Promise<string[]> {\n return this.data[key] || [];\n }\n\n public async sismember(key: string, field: string) {\n return this.data[key] && this.data[key].includes(field) ? 1 : 0;\n }\n\n public srem(key: string, value: any) {\n if (this.data[key]) {\n spliceOne(this.data[key], this.data[key].indexOf(value));\n }\n }\n\n public scard(key: string) {\n return (this.data[key] || []).length;\n }\n\n public async sinter(...keys: string[]) {\n const intersection: {[value: string]: number} = {};\n\n for (let i = 0, l = keys.length; i < l; i++) {\n (await this.smembers(keys[i])).forEach((member) => {\n if (!intersection[member]) {\n intersection[member] = 0;\n }\n\n intersection[member]++;\n });\n }\n\n return Object.keys(intersection).reduce((prev, curr) => {\n if (intersection[curr] > 1) {\n prev.push(curr);\n }\n return prev;\n }, []);\n }\n\n public hset(key: string, field: string, value: string) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n this.hash[key][field] = value;\n return Promise.resolve(true);\n }\n\n public hincrby(key: string, field: string, incrBy: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n return Promise.resolve(value);\n }\n\n public hincrbyex(key: string, field: string, incrBy: number, expireInSeconds: number) {\n if (!this.hash[key]) { this.hash[key] = {}; }\n let value = Number(this.hash[key][field] || '0');\n value += incrBy;\n this.hash[key][field] = value.toString();\n\n //\n // FIXME: delete only hash[key][field]\n // (we can't use \"HEXPIRE\" in Redis because it's only available since Redis version 7.4.0+)\n //\n if (this.timeouts[key]) {\n clearTimeout(this.timeouts[key]);\n }\n this.timeouts[key] = setTimeout(() => {\n delete this.hash[key];\n delete this.timeouts[key];\n }, expireInSeconds * 1000);\n\n return Promise.resolve(value);\n }\n\n public async hget(key: string, field: string) {\n return (typeof(this.hash[key]) === 'object')\n ? this.hash[key][field] ?? null\n : null;\n }\n\n public async hgetall(key: string) {\n return this.hash[key] || {};\n }\n\n public hdel(key: string, field: any) {\n const success = this.hash?.[key]?.[field] !== undefined;\n if (success) {\n delete this.hash[key][field];\n }\n return Promise.resolve(success);\n }\n\n public async hlen(key: string) {\n return this.hash[key] && Object.keys(this.hash[key]).length || 0;\n }\n\n public async incr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)++;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public async decr(key: string) {\n if (!this.keys[key]) {\n this.keys[key] = 0;\n }\n (this.keys[key] as number)--;\n return Promise.resolve(this.keys[key] as number);\n }\n\n public llen(key: string) {\n return Promise.resolve((this.data[key] && this.data[key].length) || 0);\n }\n\n public rpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].push(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpush(key: string, ...values: string[]): Promise<number> {\n if (!this.data[key]) { this.data[key] = []; }\n\n let lastLength: number = 0;\n\n values.forEach(value => {\n lastLength = this.data[key].unshift(value);\n });\n\n return Promise.resolve(lastLength);\n }\n\n public lpop(key: string): Promise<string> {\n return Promise.resolve(Array.isArray(this.data[key])\n ? this.data[key].shift()\n : null);\n }\n\n public rpop(key: string): Promise<string | null> {\n return Promise.resolve(this.data[key].pop());\n }\n\n public brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null> {\n const keys = args.slice(0, -1) as string[];\n const timeoutInSeconds = args[args.length - 1] as number;\n\n const getFirstPopulated = (): [string, string] | null => {\n const keyWithValue = keys.find(key => this.data[key] && this.data[key].length > 0);\n if (keyWithValue) {\n return [keyWithValue, this.data[keyWithValue].pop()];\n } else {\n return null;\n }\n }\n\n const firstPopulated = getFirstPopulated();\n\n if (firstPopulated) {\n // return first populated key + item\n return Promise.resolve(firstPopulated);\n\n } else {\n // 8 retries per second\n const maxRetries = timeoutInSeconds * 8;\n\n let tries = 0;\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n tries++;\n\n const firstPopulated = getFirstPopulated();\n if (firstPopulated) {\n clearInterval(interval);\n return resolve(firstPopulated);\n\n } else if (tries >= maxRetries) {\n clearInterval(interval);\n return resolve(null);\n }\n\n }, (timeoutInSeconds * 1000) / maxRetries);\n });\n }\n }\n\n public setMaxListeners(number: number) {\n this.subscriptions.setMaxListeners(number);\n }\n\n public shutdown() {\n if (isDevMode) {\n const cache = JSON.stringify({\n data: this.data,\n hash: this.hash,\n keys: this.keys\n });\n fs.writeFileSync(DEVMODE_CACHE_FILE_PATH, cache, { encoding: \"utf-8\" });\n }\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["firstPopulated"]
7
7
  }
@@ -16,7 +16,7 @@ export interface Presence {
16
16
  * @param topic - Topic name.
17
17
  * @param callback - Callback to trigger on subscribing.
18
18
  */
19
- subscribe(topic: string, callback: Function): any;
19
+ subscribe(topic: string, callback: Function): Promise<this>;
20
20
  /**
21
21
  * Unsubscribe from given topic.
22
22
  *
@@ -138,7 +138,7 @@ export interface Presence {
138
138
  /**
139
139
  * Returns the value associated with field in the hash stored at key.
140
140
  */
141
- hget(key: string, field: string): Promise<string>;
141
+ hget(key: string, field: string): Promise<string | null>;
142
142
  /**
143
143
  * Returns all fields and values of the hash stored at key.
144
144
  */
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/presence/Presence.ts"],
4
- "sourcesContent": ["/**\n * When you need to scale your server on multiple processes and/or machines, you'd need to provide\n * the Presence option to the Server. The purpose of Presence is to allow communicating and\n * sharing data between different processes, specially during match-making.\n *\n * - Local presence - This is the default option. It's meant to be used when you're running Colyseus in\n * a single process.\n * - Redis presence - Use this option when you're running Colyseus on multiple processes and/or machines.\n *\n * @default Local presence\n */\nexport interface Presence {\n /**\n * Subscribes to the given topic. The callback will be triggered whenever a message is published on topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on subscribing.\n */\n subscribe(topic: string, callback: Function);\n\n /**\n * Unsubscribe from given topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on topic unsubscribing.\n */\n unsubscribe(topic: string, callback?: Function);\n\n /**\n * Lists the currently active channels / subscriptions\n * @param pattern\n */\n channels(pattern?: string): Promise<string[]>;\n\n /**\n * Posts a message to given topic.\n *\n * @param topic - Topic name.\n * @param data - Message body/object.\n */\n publish(topic: string, data: any);\n\n /**\n * Returns if key exists.\n *\n * @param key\n */\n exists(key: string): Promise<boolean>;\n\n /**\n * Set key to hold the string value.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n */\n set(key: string, value: string);\n\n /**\n * Set key to hold the string value and set key to timeout after a given number of seconds.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n * @param seconds - Timeout value.\n */\n setex(key: string, value: string, seconds: number);\n\n /**\n * Expire the key in seconds.\n *\n * @param key - Identifier.\n * @param seconds - Seconds to expire the key.\n */\n expire(key: string, seconds: number);\n\n /**\n * Get the value of key.\n *\n * @param key - Identifier.\n */\n get(key: string);\n\n /**\n * Removes the specified key.\n *\n * @param key - Identifier of the object to removed.\n */\n del(key: string): void;\n\n /**\n * Add the specified members to the set stored at key. Specified members that are already\n * a member of this set are ignored. If key does not exist, a new set is created before\n * adding the specified members.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Message body/object.\n */\n sadd(key: string, value: any);\n\n /**\n * Returns all the members of the set value stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n smembers(key: string): Promise<string[]>;\n\n /**\n * Returns if member is a member of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n * @param field - Key value within the set.\n * @returns `1` if the element is a member of the set else `0`.\n */\n sismember(key: string, field: string);\n\n /**\n * Remove the specified members from the set stored at key. Specified members that are not a\n * member of this set are ignored. If key does not exist, it is treated as an empty set\n * and this command returns 0.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Key value within the set.\n */\n srem(key: string, value: any);\n\n /**\n * Returns the set cardinality (number of elements) of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n scard(key: string);\n\n /**\n * Returns the members of the set resulting from the intersection of all the given sets.\n *\n * @param keys - Key values within the set.\n */\n sinter(...keys: string[]): Promise<string[]>;\n\n /**\n * Sets field in the hash stored at key to value. If key does not exist, a new key holding a hash is created.\n * If field already exists in the hash, it is overwritten.\n */\n hset(key: string, field: string, value: string): Promise<boolean>;\n\n /**\n * Increments the number stored at field in the hash stored at key by increment. If key does not exist, a new key\n * holding a hash is created. If field does not exist the value is set to 0 before the operation is performed.\n */\n hincrby(key: string, field: string, value: number): Promise<number>;\n\n /**\n * WARNING: DO NOT USE THIS METHOD. It is meant for internal use only.\n * @private\n */\n hincrbyex(key: string, field: string, value: number, expireInSeconds: number): Promise<number>;\n\n /**\n * Returns the value associated with field in the hash stored at key.\n */\n hget(key: string, field: string): Promise<string>;\n\n /**\n * Returns all fields and values of the hash stored at key.\n */\n hgetall(key: string): Promise<{ [key: string]: string }>;\n\n /**\n * Removes the specified fields from the hash stored at key. Specified fields that do not exist within\n * this hash are ignored. If key does not exist, it is treated as an empty hash and this command returns 0.\n */\n hdel(key: string, field: string): Promise<boolean>;\n\n /**\n * Returns the number of fields contained in the hash stored at key\n */\n hlen(key: string): Promise<number>;\n\n /**\n * Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or\n * contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n incr(key: string): Promise<number>;\n\n /**\n * Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or contains a string\n * that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n decr(key: string): Promise<number>;\n\n /**\n * Returns the length of the list stored at key.\n */\n llen(key: string): Promise<number>;\n\n /**\n * Adds the string value to the end of the list stored at key. If key does not exist, it is created as empty list before performing the push operation.\n */\n rpush(key: string, ...values: string[]): Promise<number>;\n\n /**\n * Adds the string value to the begginning of the list stored at key. If key does not exist, it is created as empty list before performing the push operation.\n */\n lpush(key: string, ...values: string[]): Promise<number>;\n\n /**\n * Removes and returns the last element of the list stored at key.\n */\n rpop(key: string): Promise<string | null>;\n\n /**\n * Removes and returns the first element of the list stored at key.\n */\n lpop(key: string): Promise<string | null>;\n\n /**\n * Removes and returns the last element of the list stored at key. If the list is empty, the execution is halted until an element is available or the timeout is reached.\n */\n brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null>;\n\n setMaxListeners(number: number): void;\n\n shutdown(): void;\n}\n"],
4
+ "sourcesContent": ["/**\n * When you need to scale your server on multiple processes and/or machines, you'd need to provide\n * the Presence option to the Server. The purpose of Presence is to allow communicating and\n * sharing data between different processes, specially during match-making.\n *\n * - Local presence - This is the default option. It's meant to be used when you're running Colyseus in\n * a single process.\n * - Redis presence - Use this option when you're running Colyseus on multiple processes and/or machines.\n *\n * @default Local presence\n */\nexport interface Presence {\n /**\n * Subscribes to the given topic. The callback will be triggered whenever a message is published on topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on subscribing.\n */\n subscribe(topic: string, callback: Function): Promise<this>;\n\n /**\n * Unsubscribe from given topic.\n *\n * @param topic - Topic name.\n * @param callback - Callback to trigger on topic unsubscribing.\n */\n unsubscribe(topic: string, callback?: Function);\n\n /**\n * Lists the currently active channels / subscriptions\n * @param pattern\n */\n channels(pattern?: string): Promise<string[]>;\n\n /**\n * Posts a message to given topic.\n *\n * @param topic - Topic name.\n * @param data - Message body/object.\n */\n publish(topic: string, data: any);\n\n /**\n * Returns if key exists.\n *\n * @param key\n */\n exists(key: string): Promise<boolean>;\n\n /**\n * Set key to hold the string value.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n */\n set(key: string, value: string);\n\n /**\n * Set key to hold the string value and set key to timeout after a given number of seconds.\n *\n * @param key - Identifier.\n * @param value - Message body/object.\n * @param seconds - Timeout value.\n */\n setex(key: string, value: string, seconds: number);\n\n /**\n * Expire the key in seconds.\n *\n * @param key - Identifier.\n * @param seconds - Seconds to expire the key.\n */\n expire(key: string, seconds: number);\n\n /**\n * Get the value of key.\n *\n * @param key - Identifier.\n */\n get(key: string);\n\n /**\n * Removes the specified key.\n *\n * @param key - Identifier of the object to removed.\n */\n del(key: string): void;\n\n /**\n * Add the specified members to the set stored at key. Specified members that are already\n * a member of this set are ignored. If key does not exist, a new set is created before\n * adding the specified members.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Message body/object.\n */\n sadd(key: string, value: any);\n\n /**\n * Returns all the members of the set value stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n smembers(key: string): Promise<string[]>;\n\n /**\n * Returns if member is a member of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n * @param field - Key value within the set.\n * @returns `1` if the element is a member of the set else `0`.\n */\n sismember(key: string, field: string);\n\n /**\n * Remove the specified members from the set stored at key. Specified members that are not a\n * member of this set are ignored. If key does not exist, it is treated as an empty set\n * and this command returns 0.\n *\n * @param key - Name/Identifier of the set.\n * @param value - Key value within the set.\n */\n srem(key: string, value: any);\n\n /**\n * Returns the set cardinality (number of elements) of the set stored at key.\n *\n * @param key - Name/Identifier of the set.\n */\n scard(key: string);\n\n /**\n * Returns the members of the set resulting from the intersection of all the given sets.\n *\n * @param keys - Key values within the set.\n */\n sinter(...keys: string[]): Promise<string[]>;\n\n /**\n * Sets field in the hash stored at key to value. If key does not exist, a new key holding a hash is created.\n * If field already exists in the hash, it is overwritten.\n */\n hset(key: string, field: string, value: string): Promise<boolean>;\n\n /**\n * Increments the number stored at field in the hash stored at key by increment. If key does not exist, a new key\n * holding a hash is created. If field does not exist the value is set to 0 before the operation is performed.\n */\n hincrby(key: string, field: string, value: number): Promise<number>;\n\n /**\n * WARNING: DO NOT USE THIS METHOD. It is meant for internal use only.\n * @private\n */\n hincrbyex(key: string, field: string, value: number, expireInSeconds: number): Promise<number>;\n\n /**\n * Returns the value associated with field in the hash stored at key.\n */\n hget(key: string, field: string): Promise<string | null>;\n\n /**\n * Returns all fields and values of the hash stored at key.\n */\n hgetall(key: string): Promise<{ [key: string]: string }>;\n\n /**\n * Removes the specified fields from the hash stored at key. Specified fields that do not exist within\n * this hash are ignored. If key does not exist, it is treated as an empty hash and this command returns 0.\n */\n hdel(key: string, field: string): Promise<boolean>;\n\n /**\n * Returns the number of fields contained in the hash stored at key\n */\n hlen(key: string): Promise<number>;\n\n /**\n * Increments the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or\n * contains a string that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n incr(key: string): Promise<number>;\n\n /**\n * Decrements the number stored at key by one. If the key does not exist, it is set to 0 before performing\n * the operation. An error is returned if the key contains a value of the wrong type or contains a string\n * that can not be represented as integer. This operation is limited to 64-bit signed integers.\n */\n decr(key: string): Promise<number>;\n\n /**\n * Returns the length of the list stored at key.\n */\n llen(key: string): Promise<number>;\n\n /**\n * Adds the string value to the end of the list stored at key. If key does not exist, it is created as empty list before performing the push operation.\n */\n rpush(key: string, ...values: string[]): Promise<number>;\n\n /**\n * Adds the string value to the begginning of the list stored at key. If key does not exist, it is created as empty list before performing the push operation.\n */\n lpush(key: string, ...values: string[]): Promise<number>;\n\n /**\n * Removes and returns the last element of the list stored at key.\n */\n rpop(key: string): Promise<string | null>;\n\n /**\n * Removes and returns the first element of the list stored at key.\n */\n lpop(key: string): Promise<string | null>;\n\n /**\n * Removes and returns the last element of the list stored at key. If the list is empty, the execution is halted until an element is available or the timeout is reached.\n */\n brpop(...args: [...keys: string[], timeoutInSeconds: number]): Promise<[string, string] | null>;\n\n setMaxListeners(number: number): void;\n\n shutdown(): void;\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/core",
3
- "version": "0.16.19",
3
+ "version": "0.16.20",
4
4
  "description": "Multiplayer Framework for Node.js.",
5
5
  "input": "./src/index.ts",
6
6
  "main": "./build/index.js",