@colyseus/react 0.1.1 → 0.1.3
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/dist/index.cjs +42 -32
- package/dist/index.mjs +43 -33
- package/package.json +2 -6
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,14 @@ let _colyseus_schema = require("@colyseus/schema");
|
|
|
3
3
|
|
|
4
4
|
//#region src/schema/createSnapshot.ts
|
|
5
5
|
/**
|
|
6
|
+
* Returns the `@type`-decorated field names for a Schema class,
|
|
7
|
+
* reading from `Symbol.metadata` set by v4's `@type()` decorators.
|
|
8
|
+
*/
|
|
9
|
+
function getSchemaFieldNames(node) {
|
|
10
|
+
const metadata = node.constructor?.[Symbol.metadata];
|
|
11
|
+
if (metadata && typeof metadata === "object") return Object.values(metadata).map((f) => f.name);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
6
14
|
* Builds a reverse lookup map from objects to their refIds.
|
|
7
15
|
*/
|
|
8
16
|
function buildObjectToRefIdMap(refs) {
|
|
@@ -64,8 +72,9 @@ function createSnapshotForArraySchema(node, previousResult, ctx) {
|
|
|
64
72
|
function createSnapshotForSchema(node, previousResult, ctx) {
|
|
65
73
|
const snapshotted = {};
|
|
66
74
|
let hasChanged = previousResult === void 0;
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
75
|
+
const fieldNames = getSchemaFieldNames(node);
|
|
76
|
+
if (!fieldNames) throw new Error(`createSnapshotForSchema: no field metadata found on ${node.constructor?.name ?? "unknown"}. Is @colyseus/schema v4 installed?`);
|
|
77
|
+
for (const fieldName of fieldNames) {
|
|
69
78
|
const value = node[fieldName];
|
|
70
79
|
if (typeof value !== "function") {
|
|
71
80
|
const snapshottedValue = createSnapshot(value, ctx);
|
|
@@ -73,15 +82,6 @@ function createSnapshotForSchema(node, previousResult, ctx) {
|
|
|
73
82
|
if (!hasChanged && previousResult && previousResult[fieldName] !== snapshottedValue) hasChanged = true;
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
|
-
else for (const key in node) {
|
|
77
|
-
if (key.startsWith("_") || key.startsWith("$")) continue;
|
|
78
|
-
const value = node[key];
|
|
79
|
-
if (typeof value !== "function") {
|
|
80
|
-
const snapshottedValue = createSnapshot(value, ctx);
|
|
81
|
-
snapshotted[key] = snapshottedValue;
|
|
82
|
-
if (!hasChanged && previousResult && previousResult[key] !== snapshottedValue) hasChanged = true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
85
|
return hasChanged ? snapshotted : previousResult;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
@@ -108,9 +108,9 @@ function createSnapshot(node, ctx) {
|
|
|
108
108
|
const savedParentRefId = ctx.currentParentRefId;
|
|
109
109
|
ctx.currentParentRefId = refId;
|
|
110
110
|
let result;
|
|
111
|
-
if (node
|
|
112
|
-
else if (node
|
|
113
|
-
else if (
|
|
111
|
+
if (typeof node["set"] === "function") result = createSnapshotForMapSchema(node, previousResult, ctx);
|
|
112
|
+
else if (typeof node["push"] === "function") result = createSnapshotForArraySchema(node, previousResult, ctx);
|
|
113
|
+
else if (_colyseus_schema.Schema.isSchema(node)) result = createSnapshotForSchema(node, previousResult, ctx);
|
|
114
114
|
else result = node;
|
|
115
115
|
ctx.currentParentRefId = savedParentRefId;
|
|
116
116
|
if (refId !== -1) ctx.currentResultsByRefId.set(refId, result);
|
|
@@ -125,7 +125,14 @@ const subscriptionsByState = /* @__PURE__ */ new WeakMap();
|
|
|
125
125
|
* Gets or creates a subscription for the given room state.
|
|
126
126
|
*
|
|
127
127
|
* This function sets up change notification by wrapping the decoder's
|
|
128
|
-
* `
|
|
128
|
+
* `decode` method to intercept all state changes and notify subscribed
|
|
129
|
+
* React components.
|
|
130
|
+
*
|
|
131
|
+
* We wrap `decode()` rather than assigning `decoder.triggerChanges` because
|
|
132
|
+
* `triggerChanges` is a single-slot callback that gets unconditionally
|
|
133
|
+
* overwritten by `Callbacks.get()` / `getDecoderStateCallbacks()` on every
|
|
134
|
+
* call. Wrapping `decode()` sidesteps this conflict entirely since it is the
|
|
135
|
+
* sole entry point for both full-state syncs and incremental patches.
|
|
129
136
|
*
|
|
130
137
|
* @param roomState - The Colyseus room state Schema instance
|
|
131
138
|
* @param decoder - The Colyseus decoder associated with the room
|
|
@@ -140,27 +147,30 @@ function getOrCreateSubscription(roomState, decoder) {
|
|
|
140
147
|
dirtyRefIds: /* @__PURE__ */ new Set(),
|
|
141
148
|
parentRefIdMap: /* @__PURE__ */ new Map(),
|
|
142
149
|
objectToRefId: void 0,
|
|
143
|
-
cleanupCounter: 0
|
|
150
|
+
cleanupCounter: 0,
|
|
151
|
+
originalDecode: decoder.decode
|
|
144
152
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
decoder.decode = function(...args) {
|
|
154
|
+
const changes = subscription.originalDecode.apply(decoder, args);
|
|
155
|
+
if (changes && changes.length > 0) {
|
|
156
|
+
const refs = decoder.root?.refs;
|
|
157
|
+
if (refs) {
|
|
158
|
+
subscription.objectToRefId = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const [refId, obj] of refs.entries()) if (obj !== null && typeof obj === "object") subscription.objectToRefId.set(obj, refId);
|
|
160
|
+
}
|
|
161
|
+
for (const change of changes) {
|
|
162
|
+
const refId = subscription.objectToRefId?.get(change.ref) ?? -1;
|
|
163
|
+
if (refId !== -1) {
|
|
164
|
+
let currentRefId = refId;
|
|
165
|
+
while (currentRefId !== void 0) {
|
|
166
|
+
subscription.dirtyRefIds.add(currentRefId);
|
|
167
|
+
currentRefId = subscription.parentRefIdMap.get(currentRefId);
|
|
168
|
+
}
|
|
160
169
|
}
|
|
161
170
|
}
|
|
171
|
+
subscription.listeners.forEach((callback) => callback());
|
|
162
172
|
}
|
|
163
|
-
|
|
173
|
+
return changes;
|
|
164
174
|
};
|
|
165
175
|
subscriptionsByState.set(roomState, subscription);
|
|
166
176
|
return subscription;
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { Schema } from "@colyseus/schema";
|
|
3
3
|
|
|
4
4
|
//#region src/schema/createSnapshot.ts
|
|
5
5
|
/**
|
|
6
|
+
* Returns the `@type`-decorated field names for a Schema class,
|
|
7
|
+
* reading from `Symbol.metadata` set by v4's `@type()` decorators.
|
|
8
|
+
*/
|
|
9
|
+
function getSchemaFieldNames(node) {
|
|
10
|
+
const metadata = node.constructor?.[Symbol.metadata];
|
|
11
|
+
if (metadata && typeof metadata === "object") return Object.values(metadata).map((f) => f.name);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
6
14
|
* Builds a reverse lookup map from objects to their refIds.
|
|
7
15
|
*/
|
|
8
16
|
function buildObjectToRefIdMap(refs) {
|
|
@@ -64,8 +72,9 @@ function createSnapshotForArraySchema(node, previousResult, ctx) {
|
|
|
64
72
|
function createSnapshotForSchema(node, previousResult, ctx) {
|
|
65
73
|
const snapshotted = {};
|
|
66
74
|
let hasChanged = previousResult === void 0;
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
75
|
+
const fieldNames = getSchemaFieldNames(node);
|
|
76
|
+
if (!fieldNames) throw new Error(`createSnapshotForSchema: no field metadata found on ${node.constructor?.name ?? "unknown"}. Is @colyseus/schema v4 installed?`);
|
|
77
|
+
for (const fieldName of fieldNames) {
|
|
69
78
|
const value = node[fieldName];
|
|
70
79
|
if (typeof value !== "function") {
|
|
71
80
|
const snapshottedValue = createSnapshot(value, ctx);
|
|
@@ -73,15 +82,6 @@ function createSnapshotForSchema(node, previousResult, ctx) {
|
|
|
73
82
|
if (!hasChanged && previousResult && previousResult[fieldName] !== snapshottedValue) hasChanged = true;
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
|
-
else for (const key in node) {
|
|
77
|
-
if (key.startsWith("_") || key.startsWith("$")) continue;
|
|
78
|
-
const value = node[key];
|
|
79
|
-
if (typeof value !== "function") {
|
|
80
|
-
const snapshottedValue = createSnapshot(value, ctx);
|
|
81
|
-
snapshotted[key] = snapshottedValue;
|
|
82
|
-
if (!hasChanged && previousResult && previousResult[key] !== snapshottedValue) hasChanged = true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
85
|
return hasChanged ? snapshotted : previousResult;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
@@ -108,9 +108,9 @@ function createSnapshot(node, ctx) {
|
|
|
108
108
|
const savedParentRefId = ctx.currentParentRefId;
|
|
109
109
|
ctx.currentParentRefId = refId;
|
|
110
110
|
let result;
|
|
111
|
-
if (node
|
|
112
|
-
else if (node
|
|
113
|
-
else if (node
|
|
111
|
+
if (typeof node["set"] === "function") result = createSnapshotForMapSchema(node, previousResult, ctx);
|
|
112
|
+
else if (typeof node["push"] === "function") result = createSnapshotForArraySchema(node, previousResult, ctx);
|
|
113
|
+
else if (Schema.isSchema(node)) result = createSnapshotForSchema(node, previousResult, ctx);
|
|
114
114
|
else result = node;
|
|
115
115
|
ctx.currentParentRefId = savedParentRefId;
|
|
116
116
|
if (refId !== -1) ctx.currentResultsByRefId.set(refId, result);
|
|
@@ -125,7 +125,14 @@ const subscriptionsByState = /* @__PURE__ */ new WeakMap();
|
|
|
125
125
|
* Gets or creates a subscription for the given room state.
|
|
126
126
|
*
|
|
127
127
|
* This function sets up change notification by wrapping the decoder's
|
|
128
|
-
* `
|
|
128
|
+
* `decode` method to intercept all state changes and notify subscribed
|
|
129
|
+
* React components.
|
|
130
|
+
*
|
|
131
|
+
* We wrap `decode()` rather than assigning `decoder.triggerChanges` because
|
|
132
|
+
* `triggerChanges` is a single-slot callback that gets unconditionally
|
|
133
|
+
* overwritten by `Callbacks.get()` / `getDecoderStateCallbacks()` on every
|
|
134
|
+
* call. Wrapping `decode()` sidesteps this conflict entirely since it is the
|
|
135
|
+
* sole entry point for both full-state syncs and incremental patches.
|
|
129
136
|
*
|
|
130
137
|
* @param roomState - The Colyseus room state Schema instance
|
|
131
138
|
* @param decoder - The Colyseus decoder associated with the room
|
|
@@ -140,27 +147,30 @@ function getOrCreateSubscription(roomState, decoder) {
|
|
|
140
147
|
dirtyRefIds: /* @__PURE__ */ new Set(),
|
|
141
148
|
parentRefIdMap: /* @__PURE__ */ new Map(),
|
|
142
149
|
objectToRefId: void 0,
|
|
143
|
-
cleanupCounter: 0
|
|
150
|
+
cleanupCounter: 0,
|
|
151
|
+
originalDecode: decoder.decode
|
|
144
152
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
decoder.decode = function(...args) {
|
|
154
|
+
const changes = subscription.originalDecode.apply(decoder, args);
|
|
155
|
+
if (changes && changes.length > 0) {
|
|
156
|
+
const refs = decoder.root?.refs;
|
|
157
|
+
if (refs) {
|
|
158
|
+
subscription.objectToRefId = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const [refId, obj] of refs.entries()) if (obj !== null && typeof obj === "object") subscription.objectToRefId.set(obj, refId);
|
|
160
|
+
}
|
|
161
|
+
for (const change of changes) {
|
|
162
|
+
const refId = subscription.objectToRefId?.get(change.ref) ?? -1;
|
|
163
|
+
if (refId !== -1) {
|
|
164
|
+
let currentRefId = refId;
|
|
165
|
+
while (currentRefId !== void 0) {
|
|
166
|
+
subscription.dirtyRefIds.add(currentRefId);
|
|
167
|
+
currentRefId = subscription.parentRefIdMap.get(currentRefId);
|
|
168
|
+
}
|
|
160
169
|
}
|
|
161
170
|
}
|
|
171
|
+
subscription.listeners.forEach((callback) => callback());
|
|
162
172
|
}
|
|
163
|
-
|
|
173
|
+
return changes;
|
|
164
174
|
};
|
|
165
175
|
subscriptionsByState.set(roomState, subscription);
|
|
166
176
|
return subscription;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "tsdown --watch --format esm,cjs",
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"@colyseus/schema": "^4.0.8",
|
|
24
24
|
"@colyseus/sdk": "^0.17.26",
|
|
25
|
-
"react": "
|
|
26
|
-
"react-dom": "^18.3.1"
|
|
25
|
+
"react": ">=18.3.1"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
28
|
"@colyseus/schema": "^4.0.8",
|
|
@@ -31,15 +30,12 @@
|
|
|
31
30
|
"@eslint/js": "^9.9.0",
|
|
32
31
|
"@testing-library/react": "^16.3.1",
|
|
33
32
|
"@types/react": "^18.3.3",
|
|
34
|
-
"@types/react-dom": "^18.3.0",
|
|
35
33
|
"buffer": "^6.0.3",
|
|
36
34
|
"eslint": "^9.9.0",
|
|
37
35
|
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
|
38
36
|
"eslint-plugin-react-refresh": "^0.4.9",
|
|
39
37
|
"globals": "^15.9.0",
|
|
40
38
|
"happy-dom": "^20.0.11",
|
|
41
|
-
"react": "^18.3.1",
|
|
42
|
-
"react-dom": "^18.3.1",
|
|
43
39
|
"reflect-metadata": "^0.2.2",
|
|
44
40
|
"tsdown": "^0.20.1",
|
|
45
41
|
"typescript": "^5.5.3",
|