@noya-app/noya-multiplayer-react 0.1.71 → 0.1.73
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +22 -0
- package/dist/index.bundle.js +42 -26
- package/dist/index.d.mts +69 -49
- package/dist/index.d.ts +69 -49
- package/dist/index.js +475 -253
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -203
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -3
- package/src/NoyaStateContext.tsx +148 -97
- package/src/__tests__/noyaApp.test.ts +6 -0
- package/src/__tests__/resourcePayload.test.ts +24 -134
- package/src/components/UserPointersOverlay.tsx +18 -16
- package/src/hooks.ts +19 -12
- package/src/inspector/StateInspector.tsx +84 -36
- package/src/inspector/sections/ServerScriptLogsSection.tsx +128 -0
- package/src/inspector/serialization.ts +4 -2
- package/src/inspector/useStateInspector.tsx +2 -1
- package/src/noyaApp.ts +78 -20
- package/src/singleton.tsx +33 -9
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { Observable } from "@noya-app/observable";
|
|
4
4
|
import { memoGeneric } from "@noya-app/react-utils";
|
|
5
5
|
import {
|
|
6
|
-
EphemeralUserDataManager,
|
|
7
6
|
MultiplayerUser,
|
|
7
|
+
SharedConnectionDataManager,
|
|
8
8
|
UserManager,
|
|
9
9
|
} from "@noya-app/state-manager";
|
|
10
10
|
import React, { useEffect, useMemo, useState } from "react";
|
|
@@ -26,7 +26,7 @@ export type RenderUserPointer = {
|
|
|
26
26
|
|
|
27
27
|
type InternalUserPointerProps<E extends UserPointerData> = {
|
|
28
28
|
user: MultiplayerUser;
|
|
29
|
-
|
|
29
|
+
sharedConnectionDataManager: SharedConnectionDataManager<E>;
|
|
30
30
|
hideAfter?: number;
|
|
31
31
|
} & RenderUserPointer;
|
|
32
32
|
|
|
@@ -38,22 +38,22 @@ const UserPointerInternal = memoGeneric(function UserPointerInternal<
|
|
|
38
38
|
E extends UserPointerData,
|
|
39
39
|
>({
|
|
40
40
|
user,
|
|
41
|
-
|
|
41
|
+
sharedConnectionDataManager,
|
|
42
42
|
hideAfter = 5000,
|
|
43
43
|
renderUserPointer,
|
|
44
44
|
}: InternalUserPointerProps<E>) {
|
|
45
45
|
const observable = useMemo(() => {
|
|
46
|
-
const metadata$ =
|
|
47
|
-
.observePath([user.
|
|
46
|
+
const metadata$ = sharedConnectionDataManager.metadata$
|
|
47
|
+
.observePath([user.connectionId] as const)
|
|
48
48
|
.throttle(50);
|
|
49
|
-
const data$ =
|
|
50
|
-
.observePath([user.
|
|
49
|
+
const data$ = sharedConnectionDataManager.data$
|
|
50
|
+
.observePath([user.connectionId] as const)
|
|
51
51
|
.throttle(50);
|
|
52
52
|
|
|
53
53
|
return Observable.combine([metadata$, data$], ([metadata, data]) => {
|
|
54
54
|
return { metadata, data };
|
|
55
55
|
});
|
|
56
|
-
}, [
|
|
56
|
+
}, [sharedConnectionDataManager, user.connectionId]);
|
|
57
57
|
|
|
58
58
|
const { metadata, data } = useObservable(observable);
|
|
59
59
|
|
|
@@ -75,7 +75,7 @@ const UserPointerInternal = memoGeneric(function UserPointerInternal<
|
|
|
75
75
|
if (!data?.pointer) return null;
|
|
76
76
|
|
|
77
77
|
return renderUserPointer({
|
|
78
|
-
userId: user.
|
|
78
|
+
userId: user.connectionId,
|
|
79
79
|
name: user.name,
|
|
80
80
|
point: data.pointer,
|
|
81
81
|
visible: show,
|
|
@@ -84,30 +84,32 @@ const UserPointerInternal = memoGeneric(function UserPointerInternal<
|
|
|
84
84
|
|
|
85
85
|
export type UserPointersOverlayProps<E extends UserPointerData> = {
|
|
86
86
|
userManager: UserManager;
|
|
87
|
-
|
|
87
|
+
sharedConnectionDataManager: SharedConnectionDataManager<E>;
|
|
88
88
|
} & RenderUserPointer;
|
|
89
89
|
|
|
90
90
|
export const UserPointersOverlay = memoGeneric(function UserPointers<
|
|
91
91
|
E extends UserPointerData,
|
|
92
92
|
>({
|
|
93
93
|
userManager,
|
|
94
|
-
|
|
94
|
+
sharedConnectionDataManager,
|
|
95
95
|
renderUserPointer,
|
|
96
96
|
}: UserPointersOverlayProps<E>) {
|
|
97
|
-
const
|
|
97
|
+
const currentConnectionId = useObservable(
|
|
98
|
+
sharedConnectionDataManager.currentConnectionId$
|
|
99
|
+
);
|
|
98
100
|
const connectedUsers = useObservable(userManager.connectedUsers$);
|
|
99
101
|
|
|
100
102
|
return (
|
|
101
103
|
<>
|
|
102
104
|
{connectedUsers.map((user) => {
|
|
103
|
-
if (user.
|
|
105
|
+
if (user.connectionId === currentConnectionId) return null;
|
|
104
106
|
|
|
105
107
|
return (
|
|
106
108
|
<UserPointerInternal
|
|
107
|
-
key={user.
|
|
109
|
+
key={user.connectionId}
|
|
108
110
|
user={user}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
sharedConnectionDataManager={
|
|
112
|
+
sharedConnectionDataManager as unknown as SharedConnectionDataManager<UserPointerData>
|
|
111
113
|
}
|
|
112
114
|
renderUserPointer={renderUserPointer}
|
|
113
115
|
/>
|
package/src/hooks.ts
CHANGED
|
@@ -108,8 +108,9 @@ export type UseMultiplayerStateOptions<
|
|
|
108
108
|
M extends object = object,
|
|
109
109
|
E extends object = object,
|
|
110
110
|
MenuT extends string = string,
|
|
111
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
111
112
|
> = NoyaManagerOptions<S, M> & {
|
|
112
|
-
sync?: SyncAdapter<S, M, E, MenuT>;
|
|
113
|
+
sync?: SyncAdapter<S, M, E, MenuT, I>;
|
|
113
114
|
inspector?: boolean | StateInspectorOptions;
|
|
114
115
|
};
|
|
115
116
|
|
|
@@ -119,10 +120,11 @@ export function useBindNoyaManager<
|
|
|
119
120
|
M extends object = object,
|
|
120
121
|
E extends object = object,
|
|
121
122
|
MenuT extends string = string,
|
|
123
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
122
124
|
>(
|
|
123
|
-
noyaManager: NoyaManager<S, M, E, MenuT>,
|
|
125
|
+
noyaManager: NoyaManager<S, M, E, MenuT, I>,
|
|
124
126
|
options?: Pick<
|
|
125
|
-
UseMultiplayerStateOptions<S, M, E, MenuT>,
|
|
127
|
+
UseMultiplayerStateOptions<S, M, E, MenuT, I>,
|
|
126
128
|
"inspector" | "sync"
|
|
127
129
|
>
|
|
128
130
|
) {
|
|
@@ -136,7 +138,7 @@ export function useBindNoyaManager<
|
|
|
136
138
|
taskManager: typeof noyaManager.taskManager;
|
|
137
139
|
connectionEventManager: typeof noyaManager.connectionEventManager;
|
|
138
140
|
userManager: typeof noyaManager.userManager;
|
|
139
|
-
|
|
141
|
+
sharedConnectionDataManager: typeof noyaManager.sharedConnectionDataManager;
|
|
140
142
|
multiplayerStateManager: typeof noyaManager.multiplayerStateManager;
|
|
141
143
|
assetManager: typeof noyaManager.assetManager;
|
|
142
144
|
aiManager: typeof noyaManager.aiManager;
|
|
@@ -155,7 +157,7 @@ export function useBindNoyaManager<
|
|
|
155
157
|
taskManager: noyaManager.taskManager,
|
|
156
158
|
connectionEventManager: noyaManager.connectionEventManager,
|
|
157
159
|
userManager: noyaManager.userManager,
|
|
158
|
-
|
|
160
|
+
sharedConnectionDataManager: noyaManager.sharedConnectionDataManager,
|
|
159
161
|
multiplayerStateManager: noyaManager.multiplayerStateManager,
|
|
160
162
|
assetManager: noyaManager.assetManager,
|
|
161
163
|
aiManager: noyaManager.aiManager,
|
|
@@ -179,7 +181,7 @@ export function useBindNoyaManager<
|
|
|
179
181
|
|
|
180
182
|
useErrorOverlay<MenuT>(noyaManager);
|
|
181
183
|
|
|
182
|
-
useStateInspector<S, M, E, MenuT>({
|
|
184
|
+
useStateInspector<S, M, E, MenuT, I>({
|
|
183
185
|
noyaManager,
|
|
184
186
|
disabled: !options?.inspector,
|
|
185
187
|
advanced: true,
|
|
@@ -194,30 +196,35 @@ export function useMultiplayerState<
|
|
|
194
196
|
M extends object = object,
|
|
195
197
|
E extends object = object,
|
|
196
198
|
MenuT extends string = string,
|
|
199
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
197
200
|
>(
|
|
198
201
|
initialState: S | (() => S),
|
|
199
|
-
options?: UseMultiplayerStateOptions<S, M, E, MenuT>
|
|
202
|
+
options?: UseMultiplayerStateOptions<S, M, E, MenuT, I>
|
|
200
203
|
) {
|
|
201
204
|
const {
|
|
202
205
|
sync = stubSync<
|
|
203
206
|
unknown,
|
|
204
207
|
object,
|
|
205
208
|
object,
|
|
206
|
-
string
|
|
207
|
-
|
|
209
|
+
string,
|
|
210
|
+
Record<string, unknown>
|
|
211
|
+
>() as unknown as SyncAdapter<S, M, E, MenuT, I>,
|
|
208
212
|
inspector,
|
|
209
213
|
...rest
|
|
210
214
|
} = options ?? {};
|
|
211
215
|
|
|
212
216
|
const [noyaManager] = useState(
|
|
213
|
-
() => new NoyaManager<S, M, E, MenuT>(initialState, rest)
|
|
217
|
+
() => new NoyaManager<S, M, E, MenuT, I>(initialState, rest)
|
|
214
218
|
);
|
|
215
219
|
|
|
216
|
-
return useBindNoyaManager<S, M, E, MenuT>(noyaManager, {
|
|
220
|
+
return useBindNoyaManager<S, M, E, MenuT, I>(noyaManager, {
|
|
221
|
+
sync,
|
|
222
|
+
inspector,
|
|
223
|
+
});
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
function useErrorOverlay<MenuT extends string = string>(
|
|
220
|
-
noyaManager: NoyaManager<any, any, any, MenuT>
|
|
227
|
+
noyaManager: NoyaManager<any, any, any, MenuT, any>
|
|
221
228
|
) {
|
|
222
229
|
const unrecoverableError = useObservable(noyaManager.unrecoverableError);
|
|
223
230
|
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
import { ActivityEventsSection } from "./sections/ActivityEventsSection";
|
|
24
24
|
import { EventsSection } from "./sections/EventsSection";
|
|
25
25
|
import { HistorySection } from "./sections/HistorySection";
|
|
26
|
+
import { ServerScriptLogsSection } from "./sections/ServerScriptLogsSection";
|
|
26
27
|
import { exportAll, importAll } from "./serialization";
|
|
27
28
|
import { StateInspectorButton } from "./StateInspectorButton";
|
|
28
29
|
import {
|
|
@@ -52,6 +53,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
52
53
|
M extends object,
|
|
53
54
|
E extends object,
|
|
54
55
|
MenuT extends string = string,
|
|
56
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
55
57
|
>({
|
|
56
58
|
noyaManager,
|
|
57
59
|
colorScheme = "light",
|
|
@@ -60,7 +62,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
60
62
|
advanced = false,
|
|
61
63
|
...props
|
|
62
64
|
}: {
|
|
63
|
-
noyaManager: NoyaManager<S, M, E, MenuT>;
|
|
65
|
+
noyaManager: NoyaManager<S, M, E, MenuT, I>;
|
|
64
66
|
unstyled?: boolean;
|
|
65
67
|
} & StateInspectorOptions &
|
|
66
68
|
ComponentPropsWithoutRef<"div">) {
|
|
@@ -69,12 +71,13 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
69
71
|
assetManager,
|
|
70
72
|
userManager,
|
|
71
73
|
secretManager,
|
|
72
|
-
|
|
74
|
+
sharedConnectionDataManager,
|
|
73
75
|
connectionEventManager,
|
|
74
76
|
taskManager,
|
|
75
77
|
ioManager,
|
|
76
78
|
resourceManager,
|
|
77
79
|
activityEventsManager,
|
|
80
|
+
logManager,
|
|
78
81
|
} = noyaManager;
|
|
79
82
|
|
|
80
83
|
const [didMount, setDidMount] = React.useState(false);
|
|
@@ -88,6 +91,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
88
91
|
|
|
89
92
|
const eventsContainerRef = React.useRef<HTMLDivElement>(null);
|
|
90
93
|
const activityEventsContainerRef = React.useRef<HTMLDivElement>(null);
|
|
94
|
+
const serverLogsContainerRef = React.useRef<HTMLDivElement>(null);
|
|
91
95
|
|
|
92
96
|
const [showInspector, setShowInspector] = useLocalStorageState(
|
|
93
97
|
"noya-multiplayer-react-show-inspector",
|
|
@@ -113,8 +117,8 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
113
117
|
"noya-multiplayer-react-show-tasks",
|
|
114
118
|
false
|
|
115
119
|
);
|
|
116
|
-
const [
|
|
117
|
-
"noya-multiplayer-react-show-
|
|
120
|
+
const [showShared, setShowShared] = useLocalStorageState(
|
|
121
|
+
"noya-multiplayer-react-show-shared",
|
|
118
122
|
false
|
|
119
123
|
);
|
|
120
124
|
const [showAssets, setShowAssets] = useLocalStorageState(
|
|
@@ -141,6 +145,10 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
141
145
|
"noya-multiplayer-react-show-activity-events",
|
|
142
146
|
false
|
|
143
147
|
);
|
|
148
|
+
const [showServerLogs, setShowServerLogs] = useLocalStorageState(
|
|
149
|
+
"noya-multiplayer-react-show-server-logs",
|
|
150
|
+
false
|
|
151
|
+
);
|
|
144
152
|
|
|
145
153
|
const connectionEvents = useObservable(connectionEventManager.events$);
|
|
146
154
|
useEffect(() => {
|
|
@@ -149,11 +157,38 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
149
157
|
eventsContainerRef.current.scrollHeight;
|
|
150
158
|
}
|
|
151
159
|
}, [connectionEvents]);
|
|
160
|
+
const serverLogs = useObservable(logManager.logs$);
|
|
161
|
+
const [serverLogsPaused, setServerLogsPaused] = React.useState(false);
|
|
162
|
+
const [visibleServerLogs, setVisibleServerLogs] = React.useState(
|
|
163
|
+
serverLogs ?? []
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const toggleServerLogsPaused = useCallback(() => {
|
|
167
|
+
setServerLogsPaused((value) => {
|
|
168
|
+
if (!value) {
|
|
169
|
+
setVisibleServerLogs(serverLogs ?? []);
|
|
170
|
+
}
|
|
171
|
+
return !value;
|
|
172
|
+
});
|
|
173
|
+
}, [serverLogs]);
|
|
174
|
+
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (serverLogsPaused) return;
|
|
177
|
+
|
|
178
|
+
setVisibleServerLogs(serverLogs ?? []);
|
|
179
|
+
}, [serverLogs, serverLogsPaused]);
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (!showServerLogs) return;
|
|
182
|
+
if (serverLogsContainerRef.current) {
|
|
183
|
+
serverLogsContainerRef.current.scrollTop =
|
|
184
|
+
serverLogsContainerRef.current.scrollHeight;
|
|
185
|
+
}
|
|
186
|
+
}, [showServerLogs, visibleServerLogs]);
|
|
152
187
|
|
|
153
188
|
const multipeerStateInitialized = useObservable(
|
|
154
189
|
multiplayerStateManager.isInitialized$
|
|
155
190
|
);
|
|
156
|
-
const
|
|
191
|
+
const shared = useObservable(sharedConnectionDataManager.data$);
|
|
157
192
|
const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
|
|
158
193
|
const assets = useObservable(assetManager.assets$);
|
|
159
194
|
const resources = useObservable(resourceManager.resources$);
|
|
@@ -162,7 +197,7 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
162
197
|
const secrets = useObservable(secretManager.secrets$);
|
|
163
198
|
const secretsInitialized = useObservable(secretManager.isInitialized$);
|
|
164
199
|
const connectedUsers = useObservable(userManager.connectedUsers$);
|
|
165
|
-
const
|
|
200
|
+
const connectionId = useObservable(userManager.currentConnectionId$);
|
|
166
201
|
const state = useObservable(multiplayerStateManager.optimisticState$);
|
|
167
202
|
const inputsInitialized = useObservable(ioManager.inputsInitialized$);
|
|
168
203
|
const outputTransformsInitialized = useObservable(
|
|
@@ -265,32 +300,36 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
265
300
|
}
|
|
266
301
|
>
|
|
267
302
|
<StateInspectorDisclosureRowInner style={{ flex: "0 0 auto" }}>
|
|
268
|
-
{connectedUsers?.map((user, index, array) =>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
303
|
+
{connectedUsers?.map((user, index, array) => {
|
|
304
|
+
const identifier = user.userId ?? user.connectionId;
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<StateInspectorRow
|
|
308
|
+
key={user.connectionId}
|
|
309
|
+
colorScheme={colorScheme}
|
|
310
|
+
variant={user.connectionId === connectionId ? "up" : undefined}
|
|
311
|
+
bordered={index !== array.length - 1}
|
|
312
|
+
>
|
|
313
|
+
{user.image && (
|
|
314
|
+
<img
|
|
315
|
+
src={user.image}
|
|
316
|
+
alt={user.name}
|
|
317
|
+
style={{
|
|
318
|
+
width: "13px",
|
|
319
|
+
height: "13px",
|
|
320
|
+
borderRadius: "50%",
|
|
321
|
+
marginRight: "4px",
|
|
322
|
+
display: "inline-block",
|
|
323
|
+
position: "relative",
|
|
324
|
+
background: solidBorderColor,
|
|
325
|
+
verticalAlign: "middle",
|
|
326
|
+
}}
|
|
327
|
+
/>
|
|
328
|
+
)}
|
|
329
|
+
{user.name} ({ellipsis(identifier, 8, "middle")})
|
|
330
|
+
</StateInspectorRow>
|
|
331
|
+
);
|
|
332
|
+
})}
|
|
294
333
|
{!connectedUsers && (
|
|
295
334
|
<div
|
|
296
335
|
style={{
|
|
@@ -686,13 +725,13 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
686
725
|
</StateInspectorDisclosureSection>
|
|
687
726
|
)}
|
|
688
727
|
<StateInspectorDisclosureSection
|
|
689
|
-
open={
|
|
690
|
-
setOpen={
|
|
691
|
-
title="
|
|
728
|
+
open={showShared}
|
|
729
|
+
setOpen={setShowShared}
|
|
730
|
+
title="Shared Connection Data"
|
|
692
731
|
colorScheme={colorScheme}
|
|
693
732
|
>
|
|
694
733
|
<StateInspectorDisclosureRowInner>
|
|
695
|
-
{Object.entries(
|
|
734
|
+
{Object.entries(shared).map(([key, value]) => (
|
|
696
735
|
<StateInspectorRow key={key} colorScheme={colorScheme}>
|
|
697
736
|
<ObjectInspector
|
|
698
737
|
name={key}
|
|
@@ -704,6 +743,15 @@ export const StateInspector = memoGeneric(function StateInspector<
|
|
|
704
743
|
))}
|
|
705
744
|
</StateInspectorDisclosureRowInner>
|
|
706
745
|
</StateInspectorDisclosureSection>
|
|
746
|
+
<ServerScriptLogsSection
|
|
747
|
+
colorScheme={colorScheme}
|
|
748
|
+
showLogs={showServerLogs}
|
|
749
|
+
setShowLogs={setShowServerLogs}
|
|
750
|
+
logs={visibleServerLogs}
|
|
751
|
+
paused={serverLogsPaused}
|
|
752
|
+
onTogglePaused={toggleServerLogsPaused}
|
|
753
|
+
containerRef={serverLogsContainerRef}
|
|
754
|
+
/>
|
|
707
755
|
{advanced && (
|
|
708
756
|
<ActivityEventsSection
|
|
709
757
|
colorScheme={colorScheme}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ServerScriptLogEntry } from "@noya-app/state-manager";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ObjectInspector } from "react-inspector";
|
|
4
|
+
import { getStateInspectorTheme } from "../inspectorTheme";
|
|
5
|
+
import { StateInspectorButton } from "../StateInspectorButton";
|
|
6
|
+
import {
|
|
7
|
+
StateInspectorDisclosureRowInner,
|
|
8
|
+
StateInspectorDisclosureSection,
|
|
9
|
+
} from "../StateInspectorDisclosureSection";
|
|
10
|
+
import { StateInspectorRow } from "../StateInspectorRow";
|
|
11
|
+
|
|
12
|
+
const levelColors: Record<ServerScriptLogEntry["level"], string> = {
|
|
13
|
+
error: "#f87171",
|
|
14
|
+
warn: "#facc15",
|
|
15
|
+
log: "#9ca3af",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function formatTimestamp(timestamp: number) {
|
|
19
|
+
return new Date(timestamp).toLocaleTimeString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function ServerScriptLogsSection({
|
|
23
|
+
showLogs,
|
|
24
|
+
setShowLogs,
|
|
25
|
+
colorScheme,
|
|
26
|
+
logs,
|
|
27
|
+
containerRef,
|
|
28
|
+
paused,
|
|
29
|
+
onTogglePaused,
|
|
30
|
+
}: {
|
|
31
|
+
showLogs: boolean;
|
|
32
|
+
setShowLogs: (value: boolean) => void;
|
|
33
|
+
colorScheme: "light" | "dark";
|
|
34
|
+
logs: ServerScriptLogEntry[];
|
|
35
|
+
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
36
|
+
paused: boolean;
|
|
37
|
+
onTogglePaused: () => void;
|
|
38
|
+
}) {
|
|
39
|
+
const theme = getStateInspectorTheme(colorScheme);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<StateInspectorDisclosureSection
|
|
43
|
+
open={showLogs}
|
|
44
|
+
setOpen={setShowLogs}
|
|
45
|
+
title="Server Script Logs"
|
|
46
|
+
colorScheme={colorScheme}
|
|
47
|
+
right={
|
|
48
|
+
<StateInspectorButton theme={theme} onClick={onTogglePaused}>
|
|
49
|
+
{paused ? "Resume" : "Pause"}
|
|
50
|
+
</StateInspectorButton>
|
|
51
|
+
}
|
|
52
|
+
>
|
|
53
|
+
<StateInspectorDisclosureRowInner ref={containerRef}>
|
|
54
|
+
{paused && (
|
|
55
|
+
<div
|
|
56
|
+
style={{
|
|
57
|
+
padding: "4px 12px",
|
|
58
|
+
fontSize: "11px",
|
|
59
|
+
opacity: 0.7,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
Log streaming paused
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
{logs.map((log) => (
|
|
66
|
+
<StateInspectorRow key={log.id} colorScheme={colorScheme}>
|
|
67
|
+
<div
|
|
68
|
+
style={{
|
|
69
|
+
display: "flex",
|
|
70
|
+
gap: "6px",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
fontSize: "10px",
|
|
73
|
+
color: "inherit",
|
|
74
|
+
marginBottom: log.values.length ? "4px" : 0,
|
|
75
|
+
textTransform: "uppercase",
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<span>{formatTimestamp(log.timestamp)}</span>
|
|
79
|
+
<span style={{ color: levelColors[log.level] }}>{log.level}</span>
|
|
80
|
+
<span
|
|
81
|
+
style={{
|
|
82
|
+
fontFamily: "monospace",
|
|
83
|
+
textTransform: "none",
|
|
84
|
+
fontSize: "10px",
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{log.scriptId}
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
<div
|
|
91
|
+
style={{
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
gap: "4px",
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{log.values.length > 0 ? (
|
|
98
|
+
log.values.map((value, index) => (
|
|
99
|
+
<ObjectInspector
|
|
100
|
+
key={index}
|
|
101
|
+
data={value}
|
|
102
|
+
theme={theme}
|
|
103
|
+
expandLevel={3}
|
|
104
|
+
/>
|
|
105
|
+
))
|
|
106
|
+
) : (
|
|
107
|
+
<span style={{ fontSize: "11px", opacity: 0.7 }}>No data</span>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
</StateInspectorRow>
|
|
111
|
+
))}
|
|
112
|
+
{!logs?.length && (
|
|
113
|
+
<div
|
|
114
|
+
style={{
|
|
115
|
+
padding: "12px",
|
|
116
|
+
fontSize: "12px",
|
|
117
|
+
display: "flex",
|
|
118
|
+
flexDirection: "column",
|
|
119
|
+
gap: "4px",
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
<span>No logs received</span>
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
</StateInspectorDisclosureRowInner>
|
|
126
|
+
</StateInspectorDisclosureSection>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
@@ -17,7 +17,9 @@ export async function fetchAssetMap(
|
|
|
17
17
|
return Object.fromEntries(res);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export async function exportAll(
|
|
20
|
+
export async function exportAll(
|
|
21
|
+
noyaManager: NoyaManager<any, any, any, any, any>
|
|
22
|
+
) {
|
|
21
23
|
const { multiplayerStateManager, assetManager } = noyaManager;
|
|
22
24
|
|
|
23
25
|
const assetMap = await fetchAssetMap(assetManager.assets$.get());
|
|
@@ -64,7 +66,7 @@ export function encodeAll({
|
|
|
64
66
|
|
|
65
67
|
export async function importAll(
|
|
66
68
|
buffer: Uint8Array | ArrayBuffer,
|
|
67
|
-
noyaManager: NoyaManager<any, any, any, any>,
|
|
69
|
+
noyaManager: NoyaManager<any, any, any, any, any>,
|
|
68
70
|
{
|
|
69
71
|
shouldAllowSchemaChange,
|
|
70
72
|
}: {
|
|
@@ -35,6 +35,7 @@ export function useStateInspector<
|
|
|
35
35
|
M extends object,
|
|
36
36
|
E extends object,
|
|
37
37
|
MenuT extends string = string,
|
|
38
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
38
39
|
>({
|
|
39
40
|
noyaManager,
|
|
40
41
|
disabled = false,
|
|
@@ -42,7 +43,7 @@ export function useStateInspector<
|
|
|
42
43
|
colorScheme,
|
|
43
44
|
anchor,
|
|
44
45
|
}: {
|
|
45
|
-
noyaManager: NoyaManager<S, M, E, MenuT>;
|
|
46
|
+
noyaManager: NoyaManager<S, M, E, MenuT, I>;
|
|
46
47
|
disabled?: boolean;
|
|
47
48
|
advanced?: boolean;
|
|
48
49
|
colorScheme?: ComponentProps<typeof StateInspector>["colorScheme"];
|