@hology/core 0.0.211 → 0.0.212

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/effects/sequence/sequence-player.js +1 -1
  2. package/dist/effects/vfx/initializsers.d.ts +8 -1
  3. package/dist/effects/vfx/initializsers.js +1 -1
  4. package/dist/effects/vfx/vfx-collision-behaviour.js +1 -1
  5. package/dist/effects/vfx/vfx-defs.d.ts +10 -1
  6. package/dist/effects/vfx/vfx-defs.js +1 -1
  7. package/dist/effects/vfx/vfx-renderers.d.ts +1 -0
  8. package/dist/effects/vfx/vfx-renderers.js +1 -1
  9. package/dist/gameplay/actors/actor.d.ts +23 -1
  10. package/dist/gameplay/actors/actor.js +1 -1
  11. package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
  12. package/dist/gameplay/actors/builtin/components/character/character-movement-like.d.ts +23 -0
  13. package/dist/gameplay/actors/builtin/components/character/character-movement-like.js +4 -0
  14. package/dist/gameplay/actors/builtin/components/character/character-movement-policy.d.ts +26 -0
  15. package/dist/gameplay/actors/builtin/components/character/character-movement-policy.js +4 -0
  16. package/dist/gameplay/actors/builtin/components/character/character-movement.d.ts +145 -55
  17. package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
  18. package/dist/gameplay/actors/builtin/components/character/net-character-movement-protocol.d.ts +125 -0
  19. package/dist/gameplay/actors/builtin/components/character/net-character-movement-protocol.js +4 -0
  20. package/dist/gameplay/actors/builtin/components/character/old-character-movement.d.ts +100 -0
  21. package/dist/gameplay/actors/builtin/components/character/old-character-movement.js +4 -0
  22. package/dist/gameplay/actors/builtin/components/index.d.ts +2 -0
  23. package/dist/gameplay/actors/builtin/components/index.js +1 -1
  24. package/dist/gameplay/actors/camera/third-person-camera-component.d.ts +3 -0
  25. package/dist/gameplay/actors/camera/third-person-camera-component.js +1 -1
  26. package/dist/gameplay/actors/component.js +1 -1
  27. package/dist/gameplay/actors/controller/actor-controller.d.ts +16 -0
  28. package/dist/gameplay/actors/controller/actor-controller.js +4 -0
  29. package/dist/gameplay/actors/factory.d.ts +3 -0
  30. package/dist/gameplay/actors/factory.js +1 -1
  31. package/dist/gameplay/actors/index.d.ts +4 -0
  32. package/dist/gameplay/actors/index.js +1 -1
  33. package/dist/gameplay/actors/internal/component-init.js +1 -1
  34. package/dist/gameplay/ai/behavior-tree/move.d.ts +2 -2
  35. package/dist/gameplay/index.d.ts +3 -1
  36. package/dist/gameplay/index.js +1 -1
  37. package/dist/gameplay/initiate.d.ts +4 -0
  38. package/dist/gameplay/initiate.js +1 -1
  39. package/dist/gameplay/net/browser/index.d.ts +147 -0
  40. package/dist/gameplay/net/browser/index.js +4 -0
  41. package/dist/gameplay/net/index.d.ts +7 -0
  42. package/dist/gameplay/net/index.js +4 -0
  43. package/dist/gameplay/net/net-connection.d.ts +25 -0
  44. package/dist/gameplay/net/net-connection.js +4 -0
  45. package/dist/gameplay/net/net-session.d.ts +70 -0
  46. package/dist/gameplay/net/net-session.js +4 -0
  47. package/dist/gameplay/net/service/net-actor-role.d.ts +12 -0
  48. package/dist/gameplay/net/service/net-actor-role.js +4 -0
  49. package/dist/gameplay/net/service/net-decorator.d.ts +29 -0
  50. package/dist/gameplay/net/service/net-decorator.js +4 -0
  51. package/dist/gameplay/net/service/net-serializer.d.ts +15 -0
  52. package/dist/gameplay/net/service/net-serializer.js +4 -0
  53. package/dist/gameplay/net/service/net-service.d.ts +171 -0
  54. package/dist/gameplay/net/service/net-service.js +4 -0
  55. package/dist/gameplay/net/service/net-utils.d.ts +8 -0
  56. package/dist/gameplay/net/service/net-utils.js +4 -0
  57. package/dist/gameplay/net/service/replication.d.ts +31 -0
  58. package/dist/gameplay/net/service/replication.js +4 -0
  59. package/dist/gameplay/net/service/rpc-decorator.d.ts +21 -0
  60. package/dist/gameplay/net/service/rpc-decorator.js +4 -0
  61. package/dist/gameplay/net/service/rpc.d.ts +35 -0
  62. package/dist/gameplay/net/service/rpc.js +4 -0
  63. package/dist/gameplay/services/asset-loader.d.ts +3 -2
  64. package/dist/gameplay/services/asset-loader.js +1 -1
  65. package/dist/gameplay/services/physics/physics-system.d.ts +2 -1
  66. package/dist/gameplay/services/physics/physics-system.js +1 -1
  67. package/dist/gameplay/services/world.d.ts +13 -2
  68. package/dist/gameplay/services/world.js +1 -1
  69. package/dist/rendering/color-pass.js +1 -1
  70. package/dist/rendering.d.ts +2 -0
  71. package/dist/rendering.js +1 -1
  72. package/dist/scene/asset-resource-loader.js +1 -1
  73. package/dist/scene/batched-mesh-2.d.ts +9 -0
  74. package/dist/scene/batched-mesh-2.js +1 -1
  75. package/dist/scene/bootstrap.d.ts +2 -0
  76. package/dist/scene/bootstrap.js +1 -1
  77. package/dist/scene/materializer.d.ts +4 -0
  78. package/dist/scene/materializer.js +1 -1
  79. package/dist/scene/storage/storage.d.ts +1 -1
  80. package/dist/scene/storage/storage.js +1 -1
  81. package/dist/shader/builtin/standard-shader.js +1 -1
  82. package/dist/shader/builtin/toon-shader.js +1 -1
  83. package/dist/shader/builtin/unlit-shader.js +1 -1
  84. package/dist/shader/color-layer.js +1 -1
  85. package/dist/shader/parameter.d.ts +1 -1
  86. package/dist/shader/parameter.js +1 -1
  87. package/dist/shader-nodes/depth.js +1 -1
  88. package/dist/shader-nodes/scene-sample.js +1 -1
  89. package/dist/test/batched-mesh-2.test.d.ts +2 -0
  90. package/dist/test/batched-mesh-2.test.js +4 -0
  91. package/dist/test/browser-net-session.test.d.ts +2 -0
  92. package/dist/test/browser-net-session.test.js +4 -0
  93. package/dist/test/first-person-camera-component.test.js +1 -1
  94. package/dist/test/net-character-movement.test.d.ts +2 -0
  95. package/dist/test/net-character-movement.test.js +4 -0
  96. package/dist/test/net-property-snapshot.test.d.ts +2 -0
  97. package/dist/test/net-property-snapshot.test.js +4 -0
  98. package/dist/test/sequence-animation-retiming.test.js +1 -1
  99. package/dist/test/vfx-random-color-initializer.test.d.ts +2 -0
  100. package/dist/test/vfx-random-color-initializer.test.js +4 -0
  101. package/dist/test/world-prefab-spawn.test.d.ts +2 -0
  102. package/dist/test/world-prefab-spawn.test.js +4 -0
  103. package/dist/utils/three/placeholder-texture.d.ts +3 -0
  104. package/dist/utils/three/placeholder-texture.js +4 -0
  105. package/package.json +9 -1
  106. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,147 @@
1
+ import { BehaviorSubject, Subject } from 'rxjs';
2
+ import { NetReceivedMessage, NetSession, NetMode } from '../net-session';
3
+ import { NetConnection } from '../net-connection';
4
+ export type BrowserNetSimulationOptions = {
5
+ /**
6
+ * Fixed one-way latency applied to outgoing messages from this session.
7
+ */
8
+ latencyMs?: number;
9
+ /**
10
+ * Random +/- latency added on top of latencyMs.
11
+ */
12
+ jitterMs?: number;
13
+ /**
14
+ * Drop chance for unreliable messages, from 0 to 1.
15
+ */
16
+ packetLoss?: number;
17
+ /**
18
+ * Optional drop chance for reliable messages. Defaults to 0 so reliable
19
+ * traffic remains reliable unless a test deliberately breaks it.
20
+ */
21
+ reliablePacketLoss?: number;
22
+ };
23
+ export type BrowserNetSessionOptions = BrowserNetSimulationOptions & {
24
+ /**
25
+ * How often this tab refreshes its membership entry. Set to 0 to disable.
26
+ */
27
+ heartbeatIntervalMs?: number;
28
+ /**
29
+ * Members without a recent heartbeat are removed from the local view.
30
+ */
31
+ memberTimeoutMs?: number;
32
+ /**
33
+ * Prevents a paused consumer from growing the inbound queue forever.
34
+ */
35
+ maxQueuedMessages?: number;
36
+ };
37
+ export declare class BrowserNetSession implements NetSession {
38
+ /** The id of the session */
39
+ private sessionId;
40
+ /** The ID used by this node. */
41
+ private localId;
42
+ /**
43
+ * A unique ID for this sessions
44
+ */
45
+ id: string;
46
+ /**
47
+ * The network mode from this player's point of view.
48
+ */
49
+ readonly mode: NetMode;
50
+ /**
51
+ * Convenience function to check if mode === NetworkRole.client
52
+ */
53
+ get isClient(): boolean;
54
+ /**
55
+ * Convenience function to check if you are authoritative
56
+ */
57
+ get isServer(): boolean;
58
+ /**
59
+ * Convenience function to check if mode === NetworkRole.dedicatedServer
60
+ */
61
+ get isDedicatedServer(): boolean;
62
+ /**
63
+ * The server holds connections to all other clients.
64
+ */
65
+ get clients(): NetConnection[];
66
+ /**
67
+ * Clients hold a connection to the server
68
+ */
69
+ get server(): NetConnection;
70
+ readonly playerLeft: Subject<NetConnection>;
71
+ readonly playerJoined: Subject<NetConnection>;
72
+ readonly receivedMessageCount: BehaviorSubject<number>;
73
+ constructor(
74
+ /** Initialize with a specific role */
75
+ mode: NetMode,
76
+ /** The id of the session */
77
+ sessionId: string,
78
+ /** The ID used by this node. */
79
+ localId: number, options?: BrowserNetSessionOptions);
80
+ /**
81
+ * Adjust local network simulation at runtime. The settings only affect
82
+ * messages sent by this BrowserNetSession instance.
83
+ */
84
+ configureNetworkSimulation(options: BrowserNetSimulationOptions): void;
85
+ /**
86
+ * Attempt to reconnect back to a session.
87
+ */
88
+ reconnect(): void;
89
+ /**
90
+ * Leave the session and disonnect from other clients/server
91
+ */
92
+ disconnect(): void;
93
+ sendMessage(receiver: NetConnection, reliable: boolean, buffer: ArrayBufferLike): void;
94
+ /**
95
+ * Use this method to check if there is any messages to read.
96
+ * Returns the number of messages available.
97
+ */
98
+ hasMessage(): number;
99
+ /**
100
+ * Consumes a received message and removes it
101
+ */
102
+ readMessage(): NetReceivedMessage | null;
103
+ connect(): void;
104
+ private readonly storageKey;
105
+ private readonly channelName;
106
+ private readonly heartbeatIntervalMs;
107
+ private readonly memberTimeoutMs;
108
+ private readonly maxQueuedMessages;
109
+ private simulation;
110
+ private connected;
111
+ private storage;
112
+ private channel;
113
+ private heartbeatTimer;
114
+ private receivedMessages;
115
+ private readonly knownMemberIds;
116
+ private readonly pendingDeliveryTimers;
117
+ private readonly reliableQueues;
118
+ private messageSequence;
119
+ private readonly onStorage;
120
+ private readonly onChannelMessage;
121
+ private readonly onBeforeUnload;
122
+ private readonly onUnload;
123
+ private sendOrSimulateMessage;
124
+ private deliverMessage;
125
+ private enqueueReceivedMessage;
126
+ private consumeMessage;
127
+ private connectionForMessage;
128
+ private enqueueReliableDelivery;
129
+ private scheduleReliableDelivery;
130
+ private flushReliableDelivery;
131
+ private shouldDropMessage;
132
+ private simulatedDelayMs;
133
+ private startHeartbeat;
134
+ private stopHeartbeat;
135
+ private cancelPendingDeliveries;
136
+ private mergeMember;
137
+ private removeMember;
138
+ private setMembers;
139
+ private currentMembers;
140
+ private activeMembers;
141
+ private createLocalMember;
142
+ private createMessageId;
143
+ private broadcast;
144
+ private getLocalStorage;
145
+ private writeLocalStorage;
146
+ }
147
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{BehaviorSubject as e,Subject as s}from"rxjs";import{NetMode as t}from"../net-session";const n=0,i=0,r=0,o=0;export class BrowserNetSession{get isClient(){return this.mode===t.client}get isServer(){return this.mode<t.client}get isDedicatedServer(){return this.mode===t.dedicatedServer}get clients(){return this.isServer?this.currentMembers().filter(e=>e.mode===t.client).map(e=>a(e)):[]}get server(){if(!this.isServer){const e=this.currentMembers().find(e=>e.mode<t.client);if(null!=e)return a(e)}return null}constructor(t,n,i,r={}){this.sessionId=n,this.localId=i,this.playerLeft=new s,this.playerJoined=new s,this.receivedMessageCount=new e(0),this.storageKey=`net_session_${this.sessionId}`,this.channelName=`net_session_${this.sessionId}`,this.connected=!1,this.storage=null,this.channel=null,this.heartbeatTimer=null,this.receivedMessages=[],this.knownMemberIds=new Set,this.pendingDeliveryTimers=new Set,this.reliableQueues=new Map,this.messageSequence=0,this.onStorage=e=>{if(e.key!==this.storageKey)return;const s=this.getLocalStorage();this.setMembers(s?.members??[],!0)},this.onChannelMessage=e=>{const s=e.data;if(function(e){if(null==e||"object"!=typeof e)return!1;const s=e.kind;return"join"===s||"leave"===s||"heartbeat"===s||"members-request"===s||"members"===s||"message"===s}(s)&&s.sessionId===this.sessionId&&s.sourceConnectionId!==this.localId)switch(s.kind){case"join":case"heartbeat":this.mergeMember(s.member,!0);break;case"leave":this.removeMember(s.member.connectionId,!0);break;case"members-request":this.broadcast({kind:"members",sessionId:this.sessionId,sourceConnectionId:this.localId,members:this.currentMembers()});break;case"members":this.setMembers([...this.currentMembers(),...s.members],!0);break;case"message":this.mergeMember(s.fromMember,!0),s.message.toConnectionId===this.localId&&this.enqueueReceivedMessage(s.message)}},this.onBeforeUnload=()=>{this.disconnect()},this.onUnload=()=>{this.disconnect()},this.mode=t,this.id=n,this.simulation=l(r),this.heartbeatIntervalMs=Math.max(0,r.heartbeatIntervalMs??2e3),this.memberTimeoutMs=Math.max(1,r.memberTimeoutMs??1e4),this.maxQueuedMessages=Math.max(1,r.maxQueuedMessages??1e4)}configureNetworkSimulation(e){this.simulation=l({...this.simulation,...e})}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connected&&this.broadcast({kind:"leave",sessionId:this.sessionId,sourceConnectionId:this.localId,member:this.createLocalMember()}),this.stopHeartbeat(),this.cancelPendingDeliveries(),this.connected&&(window.removeEventListener("storage",this.onStorage),window.removeEventListener("beforeunload",this.onBeforeUnload),window.removeEventListener("unload",this.onUnload),this.channel?.removeEventListener("message",this.onChannelMessage),this.channel?.close(),this.channel=null,this.connected=!1);const e=this.getLocalStorage(),s=(e?.members??this.storage?.members??[]).filter(e=>e.connectionId!==this.localId);this.writeLocalStorage({members:this.activeMembers(s)}),this.storage=null,this.knownMemberIds.clear(),this.receivedMessages=[],this.receivedMessageCount.next(0)}sendMessage(e,s,t){const n=function(e){const s=(e.id,Number(e.id));return Number.isFinite(s)?s:null}(e);if(null==n)return void console.error(`BrowserNetSession can only send to numeric connection ids. Received ${e.id}`);const i={id:this.createMessageId(),fromConnectionId:this.localId,toConnectionId:n,reliable:s,sentAt:Date.now(),buffer:c(t)};this.sendOrSimulateMessage(i,s)}hasMessage(){return this.receivedMessages.length}readMessage(){const e=this.consumeMessage();if(null!=e){const s=this.connectionForMessage(e);return null==s?(console.error(`No connection found with id ${e.fromConnectionId}`),null):{from:s,buffer:e.buffer}}return null}connect(){if(this.connected)return;if("undefined"==typeof BroadcastChannel)throw new Error("BrowserNetSession requires BroadcastChannel support");this.channel=new BroadcastChannel(this.channelName),this.channel.addEventListener("message",this.onChannelMessage),window.addEventListener("storage",this.onStorage),window.addEventListener("beforeunload",this.onBeforeUnload),window.addEventListener("unload",this.onUnload),this.connected=!0;const e=this.getLocalStorage()??{members:[]},s=this.activeMembers([...e.members,this.createLocalMember()]);this.setMembers(s,!1),this.writeLocalStorage({members:s}),this.broadcast({kind:"join",sessionId:this.sessionId,sourceConnectionId:this.localId,member:this.createLocalMember()}),this.broadcast({kind:"members-request",sessionId:this.sessionId,sourceConnectionId:this.localId}),this.startHeartbeat()}sendOrSimulateMessage(e,s){if(this.shouldDropMessage(s))return;if(s)return void this.enqueueReliableDelivery(e);const t=this.simulatedDelayMs();if(t<=0)return void this.deliverMessage(e);const n=window.setTimeout(()=>{this.pendingDeliveryTimers.delete(n),this.deliverMessage(e)},t);this.pendingDeliveryTimers.add(n)}deliverMessage(e){e.toConnectionId!==this.localId?this.broadcast({kind:"message",sessionId:this.sessionId,sourceConnectionId:this.localId,fromMember:this.createLocalMember(),message:e}):this.enqueueReceivedMessage(e)}enqueueReceivedMessage(e){this.receivedMessages.length>=this.maxQueuedMessages&&this.receivedMessages.shift(),this.receivedMessages.push(e),this.receivedMessageCount.next(this.receivedMessages.length)}consumeMessage(){const e=this.receivedMessages.shift()??null;return this.receivedMessageCount.next(this.receivedMessages.length),e}connectionForMessage(e){const s=this.server;if(s?.id===e.fromConnectionId)return s;const t=this.clients.find(s=>s.id===e.fromConnectionId);if(null!=t)return t;const n=this.currentMembers().find(s=>s.connectionId===e.fromConnectionId);return null!=n?a(n):null}enqueueReliableDelivery(e){const s=this.simulatedDelayMs(),t=this.reliableQueues.get(e.toConnectionId);if((null==t||0===t.messages.length)&&s<=0)return void this.deliverMessage(e);const n=Date.now(),i=t??{messages:[],timer:null},r=i.messages[i.messages.length-1]?.deliveryAt??n,o=Math.max(n+s,r);i.messages.push({message:e,deliveryAt:o}),this.reliableQueues.set(e.toConnectionId,i),this.scheduleReliableDelivery(e.toConnectionId,i)}scheduleReliableDelivery(e,s){if(null!=s.timer||0===s.messages.length)return;const t=Math.max(0,s.messages[0].deliveryAt-Date.now()),n=window.setTimeout(()=>{this.pendingDeliveryTimers.delete(n),s.timer=null,this.flushReliableDelivery(e)},t);s.timer=n,this.pendingDeliveryTimers.add(n)}flushReliableDelivery(e){const s=this.reliableQueues.get(e);if(null==s)return;const t=Date.now();for(;s.messages.length>0&&s.messages[0].deliveryAt<=t+1;)this.deliverMessage(s.messages.shift().message);s.messages.length>0?this.scheduleReliableDelivery(e,s):this.reliableQueues.delete(e)}shouldDropMessage(e){const s=e?this.simulation.reliablePacketLoss:this.simulation.packetLoss;return s>0&&Math.random()<s}simulatedDelayMs(){const e=this.simulation.jitterMs>0?(2*Math.random()-1)*this.simulation.jitterMs:0;return Math.max(0,this.simulation.latencyMs+e)}startHeartbeat(){this.heartbeatIntervalMs<=0||null!=this.heartbeatTimer||(this.heartbeatTimer=window.setInterval(()=>{const e=this.activeMembers([...this.currentMembers(),this.createLocalMember()]);this.setMembers(e,!0),this.writeLocalStorage({members:e}),this.broadcast({kind:"heartbeat",sessionId:this.sessionId,sourceConnectionId:this.localId,member:this.createLocalMember()})},this.heartbeatIntervalMs))}stopHeartbeat(){null!=this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}cancelPendingDeliveries(){this.pendingDeliveryTimers.forEach(e=>clearTimeout(e)),this.pendingDeliveryTimers.clear(),this.reliableQueues.clear()}mergeMember(e,s){this.setMembers([...this.currentMembers(),e],s)}removeMember(e,s){this.setMembers(this.currentMembers().filter(s=>s.connectionId!==e),s)}setMembers(e,s){const t=this.storage?.members??[],n=this.activeMembers(this.connected?[...e,this.createLocalMember()]:e);if(s){const e=new Set(n.map(e=>e.connectionId));n.filter(e=>e.connectionId!==this.localId&&!this.knownMemberIds.has(e.connectionId)).forEach(e=>this.playerJoined.next(a(e))),this.knownMemberIds.forEach(s=>{if(s===this.localId||e.has(s))return;const n=t.find(e=>e.connectionId===s);null!=n&&this.playerLeft.next(a(n))})}this.storage={members:n},this.knownMemberIds.clear(),n.forEach(e=>this.knownMemberIds.add(e.connectionId))}currentMembers(){return this.activeMembers(this.storage?.members??this.getLocalStorage()?.members??[])}activeMembers(e){const s=Date.now();return function(e){const s=new Map;for(const t of e){if(!Number.isFinite(t.connectionId))continue;const e=s.get(t.connectionId);(null==e||(t.lastSeenAt??0)>=(e.lastSeenAt??0))&&s.set(t.connectionId,t)}return[...s.values()].sort((e,s)=>e.connectionId-s.connectionId)}(e).filter(e=>e.connectionId===this.localId||null!=e.lastSeenAt&&s-e.lastSeenAt<=this.memberTimeoutMs)}createLocalMember(){return{connectionId:this.localId,mode:this.mode,player:{id:this.localId.toString()},lastSeenAt:Date.now()}}createMessageId(){const e=window.crypto?.randomUUID;return"function"==typeof e?e.call(window.crypto):`${this.localId}-${Date.now()}-${this.messageSequence++}`}broadcast(e){this.channel?.postMessage(e)}getLocalStorage(){const e=localStorage.getItem(this.storageKey);if(null!=e)try{const s=JSON.parse(e);if(Array.isArray(s?.members))return{members:s.members}}catch(e){console.warn(`Failed to parse browser net session ${this.sessionId}`,e)}return null}writeLocalStorage(e){const s=this.activeMembers(e.members);0!==s.length?localStorage.setItem(this.storageKey,JSON.stringify({members:s})):localStorage.removeItem(this.storageKey)}}function a(e){return{id:e.connectionId,player:e.player}}function c(e){const s=new Uint8Array(e),t=new Uint8Array(s.byteLength);return t.set(s),t.buffer}function l(e){return{latencyMs:Math.max(0,e.latencyMs??n),jitterMs:Math.max(0,e.jitterMs??i),packetLoss:h(e.packetLoss??r),reliablePacketLoss:h(e.reliablePacketLoss??o)}}function h(e){return Math.min(1,Math.max(0,e))}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,7 @@
1
+ export * from './service/rpc-decorator.js';
2
+ export * from './service/rpc.js';
3
+ export * from './service/net-service.js';
4
+ export * from './service/replication.js';
5
+ export * from './service/net-decorator.js';
6
+ export * from './service/net-actor-role.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ export*from"./service/rpc-decorator.js";export*from"./service/rpc.js";export*from"./service/net-service.js";export*from"./service/replication.js";export*from"./service/net-decorator.js";export*from"./service/net-actor-role.js";/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Each participant in a networked game including clients, dedicated servers and listen servers,
3
+ * each are represented by a single connection.
4
+ */
5
+ export interface NetConnection {
6
+ readonly id: string | bigint | number;
7
+ /**
8
+ * Player connected to this client.
9
+ * Not present for dedicated servers
10
+ */
11
+ readonly player?: NetPlayerInfo;
12
+ }
13
+ /**
14
+ * Information that represents the player.
15
+ */
16
+ export interface NetPlayerInfo {
17
+ /**
18
+ * An ID of a player. This may refer to an account id or some other persistent information.
19
+ * This can be used by the server to load data for this user.
20
+ * The NetSession implementation is responsible for authenticating users and providing
21
+ * the player ID.
22
+ */
23
+ readonly id: string;
24
+ }
25
+ //# sourceMappingURL=net-connection.d.ts.map
@@ -0,0 +1,4 @@
1
+ export{};/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,70 @@
1
+ import { Observable } from "rxjs";
2
+ import { NetConnection } from "./net-connection.js";
3
+ export declare enum NetMode {
4
+ /**
5
+ * Game is played locally so it has authority but no other clients are connected.
6
+ * This game be supported by NetSession implmentations that support later connecting
7
+ * to a game hosting a server.
8
+ */
9
+ none = 1,
10
+ /**
11
+ * The server runs the game but has no po player
12
+ */
13
+ dedicatedServer = 2,
14
+ /**
15
+ * One client's game acts as a server and other clients are connected
16
+ * via P2P.
17
+ */
18
+ listenServer = 3,
19
+ /**
20
+ * A client is connected to a server and doesn't have authority.
21
+ * Should be sending inputs to the server to affect important state.
22
+ */
23
+ client = 4
24
+ }
25
+ export interface NetSession {
26
+ /**
27
+ * A unique ID for this sessions
28
+ */
29
+ id: string;
30
+ /**
31
+ * The network mode from this player's point of view.
32
+ */
33
+ readonly mode: NetMode;
34
+ /**
35
+ * The server holds connections to all other clients.
36
+ */
37
+ readonly clients: NetConnection[];
38
+ /**
39
+ * Clients hold a connection to the server
40
+ */
41
+ readonly server: NetConnection;
42
+ /**
43
+ * Attempt to reconnect back to a session.
44
+ */
45
+ reconnect(): void;
46
+ /**
47
+ * Leave the session and disonnect from other clients/server
48
+ */
49
+ disconnect(): void;
50
+ sendMessage(receiver: NetConnection, reliable: boolean, buffer: ArrayBufferLike): void;
51
+ /**
52
+ * Use this method to check if there is any messages to read.
53
+ * Returns the number of messages available.
54
+ */
55
+ hasMessage(): number;
56
+ /**
57
+ * Consumes a received message and removes it.
58
+ * Messages should be consumed at an appropriate time rather than randomly whenever received.
59
+ * For example, avoid using the render loop to read messages as its order is not deterministic.
60
+ * You may want to process all messages at a single point in time before any other processing.
61
+ */
62
+ readMessage(): NetReceivedMessage | null;
63
+ readonly playerLeft: Observable<NetConnection>;
64
+ readonly playerJoined: Observable<NetConnection>;
65
+ }
66
+ export type NetReceivedMessage = {
67
+ from: NetConnection;
68
+ buffer: ArrayBufferLike;
69
+ };
70
+ //# sourceMappingURL=net-session.d.ts.map
@@ -0,0 +1,4 @@
1
+ export var NetMode;!function(e){e[e.none=1]="none",e[e.dedicatedServer=2]="dedicatedServer",e[e.listenServer=3]="listenServer",e[e.client=4]="client"}(NetMode||(NetMode={}));/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The NetRole is sent by the server and set on actors so that
3
+ * they can understan what control they have on the actor without
4
+ * knowing the connection ownership.
5
+ */
6
+ export declare enum NetRole {
7
+ authority = 0,// Server's authoritative instance
8
+ autonomousProxy = 1,// Client-owned instance (owner's client)
9
+ simulatedProxy = 2,// Non-owner client instance
10
+ none = 3
11
+ }
12
+ //# sourceMappingURL=net-actor-role.d.ts.map
@@ -0,0 +1,4 @@
1
+ export var NetRole;!function(o){o[o.authority=0]="authority",o[o.autonomousProxy=1]="autonomousProxy",o[o.simulatedProxy=2]="simulatedProxy",o[o.none=3]="none"}(NetRole||(NetRole={}));/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,29 @@
1
+ import { ReplOn } from "./replication";
2
+ /**
3
+ * Ensures that this function is only executed if on the server.
4
+ * This can be used to prevent executing something on the client
5
+ * in response to some other game play event that requires authority.
6
+ *
7
+ * Example:
8
+ * - An explosion does a random amount of damage to all actors nearby. A function is called
9
+ * on the actor to respond to the damage. The explosion also happens
10
+ * on clients but we don't know the exact amount of damage to apply because it is random.
11
+ * We instead want to let the server handle this and inform clients after.
12
+ */
13
+ export declare function RunIfServer(): (originalMethod: Function, context: ClassMethodDecoratorContext) => (this: any, ...args: any[]) => any;
14
+ /**
15
+ * Ensures that this function is only executed if on the client.
16
+ * Note that this only includes those that are not also servers like standalone and listen servers.
17
+ */
18
+ export declare function RunIfClient(): (originalMethod: Function, context: ClassMethodDecoratorContext) => (this: any, ...args: any[]) => any;
19
+ /**
20
+ * Ensures that this function is only executed if on an instance of the game with a player
21
+ * Useful for logic that should only run on the client, such as UI updates, visual effects, playing sound.
22
+ */
23
+ export declare function RunIfNotDedicatedServer(): (originalMethod: Function, context: ClassMethodDecoratorContext) => (this: any, ...args: any[]) => any;
24
+ /**
25
+ * Marks a property to be automatically replicated to clients.
26
+ * This is a placeholder; actual replication logic would be handled by the networking system.
27
+ */
28
+ export declare function Replicated(replicatedOn?: ReplOn, reliable?: boolean): (target: undefined, context: ClassMemberDecoratorContext) => void;
29
+ //# sourceMappingURL=net-decorator.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{BaseActor as t}from"../../actors/actor";import{NetService as e}from"./net-service";import{inject as n}from"../../inject";import{ReplOn as r}from"./replication";import{isSignalLike as i}from"./net-utils";import{ActorComponent as o}from"../../actors/component";export function RunIfServer(){return function(r,i){let o;return i.addInitializer(function(){this instanceof t?o=n(e):console.warn("RunIfServer should not be defined on a non actor")}),function(...t){if(o.isServer)return r.apply(this,t)}}}export function RunIfClient(){return function(r,i){let o;return i.addInitializer(function(){this instanceof t?o=n(e):console.warn("RunIfClient should not be defined on a non actor")}),function(...t){if(!o.isServer)return r.apply(this,t)}}}export function RunIfNotDedicatedServer(){return function(r,i){let o;return i.addInitializer(function(){this instanceof t?o=n(e):console.warn("RunIfClient should not be defined on a non actor")}),function(...t){if(!o.isDedicatedServer)return r.apply(this,t)}}}export function Replicated(s=r.all,c=!0){return function(r,a){let f;a.addInitializer(function(){if(!(this instanceof t||this instanceof o))return void console.warn("RunIfClient should not only be used on actors and actor components");let r,u;if(this instanceof t)r=this;else if(this instanceof o&&(r=this.actor,u=this,null==r))return;f=n(e),f.registerReplicatedProperty(s,c,r,a.name.toString(),this,u);const l=this[a.name];if(i(l)){const t=l.subscribe(t=>{if(f.isServer){if(null==r.__netid)return;f.handlePropertySet(s,c,r,a.name.toString(),t,u)}});return void r.disposed.subscribe(()=>{t()})}let d=l;Object.defineProperty(this,a.name,{set:t=>{f.isServer&&f.handlePropertySet(s,c,r,a.name.toString(),t,u),d=t},get:()=>d})})}}export function makePropertyReplicated(t,e,n,r,i){n.registerReplicatedProperty(i,r,t,e,this,null);let o=t[e];Object.defineProperty(t,e,{set:s=>{n.isServer&&n.handlePropertySet(i,r,t,e,s,null),o=s},get:()=>o})}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,15 @@
1
+ export declare class NetSerializer {
2
+ private world;
3
+ private assetLoader;
4
+ encode(data: unknown): string;
5
+ decode(data: string, result: DecodeResult): unknown;
6
+ private wrapObject;
7
+ private unwrapObject;
8
+ /**
9
+ * Mutates the result from decode to resolve all assets
10
+ * but leaving already decoded values as they are.
11
+ * @returns
12
+ */
13
+ decodeAssets(obj: unknown): Promise<unknown>;
14
+ }
15
+ //# sourceMappingURL=net-serializer.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{__decorate as r}from"tslib";import{World as t}from"../../services/world";import{inject as e}from"../../inject";import{Service as s}from"typedi";import{BaseActor as n}from"../../actors/actor";import{Euler as o,Quaternion as i,Vector2 as a,Vector3 as c,Vector4 as f}from"three";import{DataAssetRef as u}from"../../../scene/objects/data-asset";import{AssetLoader as l}from"../../services/asset-loader";const p="-a",m="-v2",d="-v3",w="-v4",y="-e",A="-q",h="-s",b="-m",j="-dar";export class DecodeResult{constructor(){this.error=!1,this.requiresAssetLookups=!1}reset(){this.error=!1,this.missingActorId=void 0,this.requiresAssetLookups=!1}}let O=class{constructor(){this.world=e(t),this.assetLoader=e(l)}encode(r){return JSON.stringify(this.wrapObject(r))}decode(r,t){t.reset();const e=JSON.parse(r);return this.unwrapObject(e,t)}wrapObject(r){if(null!=r&&"object"==typeof r){if(Array.isArray(r))return r.map(r=>this.wrapObject(r));if(r instanceof n){if(null!=r.__netid)return{[p]:r.__netid};console.warn("Missing netid on actor",r)}else{if(r instanceof a)return{[m]:r.toArray([])};if(r instanceof c)return{[d]:r.toArray([])};if(r instanceof f)return{[w]:r.toArray([])};if(r instanceof o)return{[y]:r.toArray([])};if(r instanceof i)return{[A]:r.toArray([])};if(r instanceof Map)return{[b]:Array.from(r.entries()).map(([r,t])=>[r,this.wrapObject(t)])};if(r instanceof Set)return{[h]:Array.from(r.values()).map(r=>this.wrapObject(r))};if(r instanceof u)return{[j]:r.id}}const t={};for(const e of Object.keys(r))t[e]=this.wrapObject(r[e]);return t}return r}unwrapObject(r,t){if(null!=r&&"object"==typeof r){if(Array.isArray(r))return r.map(r=>this.unwrapObject(r,t));{if(null!=r[p]){const e=r[p],s=this.world.actors.find(r=>r.__netid===e);return null==s&&(t.error=!0,t.missingActorId=e,console.warn(`Failed to find replicated actor with netid "${e}"`)),s}if(null!=r[m]){const t=r[m];return(new a).fromArray(t)}if(null!=r[d]){const t=r[d];return(new c).fromArray(t)}if(null!=r[w]){const t=r[w];return(new f).fromArray(t)}if(null!=r[y]){const t=r[y];return(new o).fromArray(t)}if(null!=r[A]){const t=r[A];return(new i).fromArray(t)}if(null!=r[b]){const e=r[b];return new Map(e.map(([r,e])=>[r,this.unwrapObject(e,t)]))}if(null!=r[h]){const e=r[h];return new Set(e.map(r=>this.unwrapObject(r,t)))}if(null!=r[j])return t.requiresAssetLookups=!0,r;const e={};for(const s of Object.keys(r))e[s]=this.unwrapObject(r[s],t);return e}}return r}async decodeAssets(r){if(null!=r&&"object"==typeof r){if(Array.isArray(r))return await Promise.all(r.map(r=>this.decodeAssets(r)));if(r instanceof Map){const t=await Promise.all(Array.from(r.entries()).map(async([r,t])=>[r,await this.decodeAssets(t)]));r.clear();for(const[e,s]of t)r.set(e,s);return r}if(r instanceof Set){const t=await Promise.all(Array.from(r.values()).map(r=>this.decodeAssets(r)));r.clear();for(const e of t)r.add(e);return r}{const t=r;if(null!=t[j])return await this.assetLoader.getDataAssetById(t[j]);for(const r of Object.keys(t))t[r]=await this.decodeAssets(t[r]);return t}}return r}};O=r([s()],O);export{O as NetSerializer};new a,new c,new f,new o,new i;/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,171 @@
1
+ import { ActorOptions, BaseActor } from '../../actors/actor';
2
+ import { ActorComponent } from '../../actors/component';
3
+ import { NetConnection } from '../net-connection';
4
+ import { NetMode, NetSession } from '../net-session';
5
+ import { ReplOn } from "./replication";
6
+ import { RunsOn } from './rpc';
7
+ export declare class NetService {
8
+ session?: NetSession;
9
+ /**
10
+ * For connection ownership to be determined, actors have
11
+ * to be registerd with their owning connection.
12
+ * You might just have a single actor per player with a connection.
13
+ * For many games, most actors are owned by the server. The player might
14
+ * just have a single playable character as its owned actor.
15
+ * For example, if the character enters a car, the player should start controlling
16
+ * that car and the car's ownership should be to the player.
17
+ * A player controller migth be a good solution for this because
18
+ * we need to somehow inform the client that it entered the car?
19
+ * This could be done on the player character as well though and the client
20
+ * will need to check if the player character is in a car and adjust the input controls
21
+ * and camera accordingly.
22
+ *
23
+ * Spawning controllers from the server might also be a solution.
24
+ * You can then define inputs on the client when the game starts using the input service.
25
+ * This might be a nice abstraction but it is not required.
26
+ *
27
+ * When the server spawns a player character or controller, it will set the
28
+ * connection on the NetService.
29
+ */
30
+ private readonly actorConnection;
31
+ private destroyed;
32
+ get mode(): NetMode;
33
+ private world;
34
+ private serializer;
35
+ private serverTimeOffset;
36
+ /**
37
+ * Convenience function to check if mode === NetworkRole.client
38
+ */
39
+ get isClient(): boolean;
40
+ /**
41
+ * Convenience function to check if you are authoritative
42
+ */
43
+ get isServer(): boolean;
44
+ /**
45
+ * Convenience function to check if mode === NetworkRole.dedicatedServer
46
+ */
47
+ get isDedicatedServer(): boolean;
48
+ /**
49
+ * A queue of messages is kept for recently spawned actors so
50
+ * any RPC or property replication on this actor is not sent
51
+ * until we got a confirmation on the client that the actor is spawned.
52
+ *
53
+ * This is not perfect though.
54
+ * If a message contains a reference to an actor
55
+ */
56
+ private actorPreSpawnMessageBuffer;
57
+ /**
58
+ * If we receive a message with a reference to an actor
59
+ * that currently does not exist, we should queue it.
60
+ * This can happen if the server sends an RPC or property replication
61
+ * for an actor that we have not yet finished spawning.
62
+ * This is only relevant for reliable messages.
63
+ */
64
+ private actorRefMissingMessageQueue;
65
+ private netStartupActorSpawnQueue;
66
+ private spawningActorNetIds;
67
+ start(): void;
68
+ stop(): void;
69
+ private configureMaterializedActorRole;
70
+ shouldReplicate(actor: BaseActor): boolean;
71
+ setReplicate(actor: BaseActor, replicate: any): void;
72
+ /**
73
+ * Ensure that we only replicate an actor for spawn, properties and RPC
74
+ * to connections for which it is relevant. This includes checking
75
+ * if it is only relevant to owner and possibly other relevancy
76
+ * logic.
77
+ * @param actor
78
+ * @param connection
79
+ * @returns
80
+ */
81
+ isRelevant(actor: BaseActor, connection: NetConnection): boolean;
82
+ getRelevancyOptions(actor: BaseActor): Partial<ActorOptions['relevancy']> | undefined;
83
+ /**
84
+ * Actors are normally sent to clients as soon as they
85
+ * are spawned on the server. If a client joins after this
86
+ * has happened, then they have to know which ones to spawn.
87
+ */
88
+ private spawnOnLateClient;
89
+ private processMessages;
90
+ private processMessage;
91
+ private processServerTime;
92
+ private getActorRefMissingMessageQueue;
93
+ private processRpc;
94
+ private processProp;
95
+ private processPropSnapshot;
96
+ private applyReplicatedProperties;
97
+ private processSpawn;
98
+ private readOptionalSceneId;
99
+ private findMaterializedNetStartupActor;
100
+ private applySpawnNetInfo;
101
+ private getNetStartupActorSpawnQueue;
102
+ private processQueuedNetStartupActorSpawns;
103
+ private processQueuedActorMessages;
104
+ sendActorReady(actor: BaseActor): void;
105
+ /**
106
+ * Processes an actor ready message from a client.
107
+ * @param from The connection the message is from.
108
+ * @param buffer The message buffer.
109
+ */
110
+ private processActorReady;
111
+ sendRpc(runsOn: RunsOn, reliable: boolean, actor: BaseActor, fname: string, args: any[], component?: ActorComponent): void;
112
+ private sendWithQueue;
113
+ private findQueuedActorsInData;
114
+ handlePropertySet(replOn: ReplOn, reliable: boolean, actor: BaseActor, property: string, value: unknown, component?: ActorComponent): void;
115
+ sendProperty(replOn: ReplOn, reliable: boolean, actor: BaseActor, property: string, propertyBuffer: ArrayBufferLike, component?: ActorComponent): void;
116
+ private nextNetId;
117
+ /**
118
+ * Generate a uniqe id for the network to be used for replication and
119
+ * to not collide with the existing actor id
120
+ */
121
+ private defineNetId;
122
+ private shouldReplicateMap;
123
+ private replicatedActors;
124
+ private replicatedProperties;
125
+ private replicatedPropertyRegistry;
126
+ sendActorSpawn(actor: BaseActor, connections?: NetConnection[]): void;
127
+ /**
128
+ * Sends a message to remove an actor with the given id to all relevant connections.
129
+ * @param actor The actor to remove.
130
+ */
131
+ sendActorRemove(actor: BaseActor): void;
132
+ /**
133
+ * Processes a remove actor message.
134
+ * @param from The connection the message is from.
135
+ * @param buffer The message buffer.
136
+ */
137
+ private processRemoveActor;
138
+ setOwningConnection(actor: BaseActor, connection: NetConnection): void;
139
+ getOwningConnection(actor: BaseActor): NetConnection;
140
+ getActorsByOwningConnection(connection: NetConnection): BaseActor[];
141
+ private isLocallyControlled;
142
+ /**
143
+ * See if an actor is locally owned.
144
+ * This relies on role and player controller
145
+ */
146
+ isLocalOwner(actor: BaseActor): boolean;
147
+ /**
148
+ * Sends the current server time (Date.now()) to the specified NetConnection.
149
+ * The time is sent as an 8-byte (BigInt) value.
150
+ */
151
+ sendServerTime(connection: NetConnection): void;
152
+ /**
153
+ * Parses a server time message buffer and returns the timestamp (number).
154
+ * Expects the buffer to be at least 9 bytes: 1 byte type, 8 bytes timestamp.
155
+ */
156
+ parseServerTime(buffer: ArrayBufferLike): number;
157
+ private getConnectionKey;
158
+ private getPreSpawnMessageQueue;
159
+ private shouldQueueMessageUntilActorReady;
160
+ private isPropertyMessage;
161
+ private getReplicatedPropertyKey;
162
+ private sendPropertySnapshot;
163
+ private buildPropertySnapshotEntries;
164
+ private shouldReplicatePropertyToConnection;
165
+ private readReplicatedPropertyValue;
166
+ private createPropertySnapshotBuffer;
167
+ private decodeReplicatedValue;
168
+ private applyReplicatedProperty;
169
+ private callRepNotify;
170
+ }
171
+ //# sourceMappingURL=net-service.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{__decorate as e}from"tslib";import{delay as t,interval as n,Subject as r,takeUntil as s,tap as o}from"rxjs";import{Euler as i,Vector3 as c}from"three";import{Service as a}from"typedi";import{$actorOptions as l,BaseActor as d,getActorClassById as p}from"../../actors/actor";import{ActorComponent as u}from"../../actors/component";import{$actorComponents as f}from"../../actors/internal/component-init";import{inject as h}from"../../inject";import{World as g}from"../../services/world";import{NetMode as y}from"../net-session";import{NetRole as w}from"./net-actor-role";import{DecodeResult as m,NetSerializer as v}from"./net-serializer";import{isSignalLike as A}from"./net-utils";import{ReplOn as b}from"./replication";import{getRpcMethodId as R,getRpcMethodName as _,RunsOn as S}from"./rpc";import{ActorController as U,BasePlayerController as M}from"../../actors/controller/actor-controller";import{makePropertyReplicated as C}from"./net-decorator";var P;!function(e){e[e.rpc=1]="rpc",e[e.prop=2]="prop",e[e.spawn=3]="spawn",e[e.removeActor=4]="removeActor",e[e.rpcComponent=5]="rpcComponent",e[e.propComponent=6]="propComponent",e[e.rpcBinary=21]="rpcBinary",e[e.rpcComponentBinary=25]="rpcComponentBinary",e[e.actorReady=7]="actorReady",e[e.propSnapshot=8]="propSnapshot",e[e.serverTime=100]="serverTime"}(P||(P={}));var $;!function(e){e[e.actor=0]="actor",e[e.component=1]="component"}($||($={}));let L=class{constructor(){this.actorConnection=new Map,this.destroyed=new r,this.world=h(g),this.serializer=h(v),this.serverTimeOffset=0,this.actorPreSpawnMessageBuffer=new Map,this.actorRefMissingMessageQueue=new Map,this.netStartupActorSpawnQueue=new Map,this.spawningActorNetIds=new Set,this.nextNetId=1,this.shouldReplicateMap=new Map,this.replicatedActors=new Map,this.replicatedProperties=new Map,this.replicatedPropertyRegistry=new Map}get mode(){return this.session?.mode??y.none}get isClient(){return this.mode===y.client}get isServer(){return this.mode<y.client}get isDedicatedServer(){return this.mode===y.dedicatedServer}start(){if(this.stop(),this.destroyed=new r,this.isServer)for(const e of this.session.clients)this.sendServerTime(e);n(15).pipe(s(this.destroyed)).subscribe(()=>{this.processMessages()}),this.world.actorAdded.pipe(s(this.destroyed),o(e=>{this.configureMaterializedActorRole(e),this.isServer?this.defineNetId(e):this.processQueuedNetStartupActorSpawns(e)}),t(8)).subscribe(e=>{this.shouldReplicate(e)&&(C(e,"owner",this,!0,b.all),this.sendActorSpawn(e))}),this.world.actorRemoved.pipe(s(this.destroyed),t(10)).subscribe(e=>{this.sendActorRemove(e),this.replicatedActors.delete(e),this.replicatedProperties.delete(e),this.replicatedPropertyRegistry.delete(e),this.actorPreSpawnMessageBuffer.delete(e)}),this.session.playerJoined.pipe(s(this.destroyed)).subscribe(e=>{this.sendServerTime(e),this.isServer&&this.replicatedActors.size>0&&this.spawnOnLateClient(e)})}stop(){this.destroyed.next(null),this.destroyed.complete()}configureMaterializedActorRole(e){null!=e.__netSceneId&&this.mode!==y.none&&(this.shouldReplicate(e)?e.netRole=this.isServer?w.authority:w.simulatedProxy:this.isServer||(e.netRole=w.none))}shouldReplicate(e){const t=this.shouldReplicateMap.get(e);if(null==t){const t=e.constructor[l],n=!0===t?.replicate;return this.shouldReplicateMap.set(e,n),n}return!0===t}setReplicate(e,t){this.shouldReplicateMap.set(e,t)}isRelevant(e,t){const n=this.getRelevancyOptions(e);return!(!0===n?.ownerOnly)||this.resolveActorConnection(e)?.id===t.id}getRelevancyOptions(e){return e.constructor[l].relevancy}spawnOnLateClient(e){const t=[e];for(const e of this.replicatedActors.keys())this.sendActorSpawn(e,t)}async processMessages(){const e=this.session.hasMessage();for(let t=0;t<e;t++){const e=this.session.readMessage();null!=e&&await this.processMessage(e)}}async processMessage(e){try{switch(new DataView(e.buffer).getUint8(0)){case P.rpc:case P.rpcComponent:case P.rpcBinary:case P.rpcComponentBinary:this.processRpc(e.from,e.buffer);break;case P.prop:case P.propComponent:this.processProp(e.from,e.buffer);break;case P.propSnapshot:this.processPropSnapshot(e.from,e.buffer);break;case P.spawn:await this.processSpawn(e.from,e.buffer);break;case P.removeActor:this.processRemoveActor(e.from,e.buffer);break;case P.serverTime:this.processServerTime(e.from,e.buffer);break;case P.actorReady:this.processActorReady(e.from,e.buffer)}}catch(e){console.error("Failed to process message",e)}}processServerTime(e,t){if(this.isServer||this.session.server?.id!==e.id)return;const n=this.parseServerTime(t);this.serverTimeOffset=Date.now()-n}getActorRefMissingMessageQueue(e){let t=this.actorRefMissingMessageQueue.get(e);return null==t&&(t=[],this.actorRefMissingMessageQueue.set(e,t)),t}processRpc(e,t){let n=0;const r=new DataView(t),s=r.getUint8(n);n+=1;const o=r.getUint32(n,!0);n+=4;const i=r.getUint8(n);n+=1;const c=s===P.rpcBinary||s===P.rpcComponentBinary;let a,l,d="";if(c){const e=r.getUint32(n,!0);n+=4,l=new Uint8Array(t,n,e),n+=e}else{const e=r.getUint16(n,!0);n+=2,d=O(t,n,e),n+=e;try{a=this.serializer.decode(d,F)}catch(e){return void console.warn("Failed to parse RPC args JSON",d,e)}}if(!c&&F.error){if(null!=F.missingActorId){const n=this.getActorRefMissingMessageQueue(F.missingActorId);console.debug(`Missing referenced actor ${F.missingActorId}. Queueing message for later`),n.push({from:e,buffer:t})}else console.log("Unknown decode error",d);return}const p=this.world.actors.find(e=>e.__netid===o);if(null==p){return this.getActorRefMissingMessageQueue(o).push({from:e,buffer:t}),void console.debug(`Missing actor ${o}. Queueing message for later`)}let u;if(s===P.rpcComponent||s===P.rpcComponentBinary){const e=r.getUint8(n),t=p[f];if(null!=t&&(u=t.find(t=>t.__netid===e)),null==u)return void console.error(`RPC failed: Failed to find component with netid ${e} on actor with netid ${o}`)}if(this.isServer){if(!this.session.clients.some(t=>t.id==e.id))return void console.warn("Received RCP from non client");const t=this.resolveActorConnection(p);if(null==t)return void console.error("Received server RPC for actor without owner which can't be processed");if(t.id!==e.id)return void console.warn(`Received RPC for actor net id ${p.__netid} from connection ${e.id}, but this connection is not the owner. Can't process.`)}else{if(!(this.session.server?.id===e.id))return void console.warn("Received RCP from non server which is not allowed")}const h=u??p,g=_(h,i);if(null!=g)if("function"==typeof h[g])if(c)h[g](l);else{if(F.requiresAssetLookups)return this.serializer.decodeAssets(a).then(e=>{h[g](...e)}).catch(e=>{console.warn(`Failed to decode asset references for RPC '${g}' on actor net id ${p.__netid}`,e)});h[g](...a)}else console.error(`RPC function '${g}' not found on actor net id ${p.__netid}`+(null!=u?` and component net id ${u.__netid}`:""));else console.error(`RPC method id ${i} not found on actor net id ${p.__netid}`+(null!=u?` and component net id ${u.__netid}`:""))}processProp(e,t){if(this.session.server?.id!==e.id)return void console.warn(`Received property replication from non-server connection (id: ${e.id}), ignoring.`);let n=0;const r=new DataView(t),s=r.getUint8(n);n+=1;const o=r.getUint32(n,!0);n+=4;const i=r.getUint8(n);n+=1;let c="";for(let e=0;e<i;e++)c+=String.fromCharCode(r.getUint8(n+e));n+=i;const a=r.getUint16(n,!0);n+=2;const l=t.slice(n,n+a);n+=a;const d=this.world.actors.find(e=>e.__netid===o);if(!d){return this.getActorRefMissingMessageQueue(o).push({from:e,buffer:t}),void console.error("Failed to process property replication. No actor found with id",o,c)}let p;if(s===P.propComponent){const e=r.getUint8(n),t=d[f];if(null!=t&&(p=t.find(t=>t.__netid===e)),null==p)return void console.error(`Failed to find component with netid ${e} on actor with netid ${o}`)}const u=p??d;try{const n=this.decodeReplicatedValue(l);if(F.error){if(null!=F.missingActorId){let n=this.getActorRefMissingMessageQueue(F.missingActorId);console.debug(`Missing referenced actor ${F.missingActorId}. Queueing message for later`),n.push({from:e,buffer:t})}return}if(F.requiresAssetLookups)return this.serializer.decodeAssets(n).then(e=>{this.applyReplicatedProperty(u,c,e),this.callRepNotify(u,c)}).catch(e=>{console.warn(`Failed to decode asset references for replicated property '${c}' on actor id ${o}`,e)});this.applyReplicatedProperty(u,c,n),this.callRepNotify(u,c)}catch(e){console.warn(`Failed to decode replicated property '${c}' on actor id ${o}`,e)}}processPropSnapshot(e,t){if(this.session.server?.id!==e.id)return void console.warn(`Received property snapshot from non-server connection (id: ${e.id}), ignoring.`);let n=0;const r=new DataView(t);n+=1;const s=r.getUint32(n,!0);n+=4;const o=this.world.actors.find(e=>e.__netid===s);if(null==o){return this.getActorRefMissingMessageQueue(s).push({from:e,buffer:t}),void console.debug(`Missing actor ${s}. Queueing property snapshot for later`)}const i=r.getUint16(n,!0);n+=2;const c=[];for(let a=0;a<i;a++){const i=r.getUint8(n);n+=1;let a=o;if(i===$.component){const e=r.getUint8(n);n+=1;const t=o[f];if(a=t?.find(t=>t.__netid===e),null==a)return void console.error(`Failed to process property snapshot. No component found with netid ${e} on actor with netid ${s}`)}else if(i!==$.actor)return void console.error(`Failed to process property snapshot. Unknown target kind ${i}`);const l=r.getUint8(n);n+=1;let d="";for(let e=0;e<l;e++)d+=String.fromCharCode(r.getUint8(n+e));n+=l;const p=r.getUint32(n,!0);n+=4;const u=t.slice(n,n+p);n+=p;try{const n=this.decodeReplicatedValue(u);if(F.error){if(null!=F.missingActorId){this.getActorRefMissingMessageQueue(F.missingActorId).push({from:e,buffer:t}),console.debug(`Missing referenced actor ${F.missingActorId}. Queueing property snapshot for later`)}else console.warn(`Unknown decode error in property snapshot for actor ${s}`);return}c.push({target:a,property:d,value:n,requiresAssetLookups:F.requiresAssetLookups})}catch(e){return void console.warn(`Failed to decode replicated property snapshot entry '${d}' on actor id ${s}`,e)}}if(c.some(e=>e.requiresAssetLookups))return Promise.all(c.map(async e=>{e.requiresAssetLookups&&(e.value=await this.serializer.decodeAssets(e.value))})).then(()=>{this.applyReplicatedProperties(c)}).catch(e=>{console.warn(`Failed to decode asset references for property snapshot on actor id ${s}`,e)});this.applyReplicatedProperties(c)}applyReplicatedProperties(e){for(const t of e)this.applyReplicatedProperty(t.target,t.property,t.value);for(const t of e)this.callRepNotify(t.target,t.property)}async processSpawn(e,t){if(this.isServer)return;const n=new DataView(t);let r=0;r+=1;const s=n.getUint32(r,!0);r+=4;const o=n.getUint8(r);r+=1;let i="";for(let e=0;e<o;e++)i+=String.fromCharCode(n.getUint8(r+e));r+=o,Q.set(n.getFloat32(r,!0),n.getFloat32(r+4,!0),n.getFloat32(r+8,!0)),r+=12,B.set(n.getFloat32(r,!0),n.getFloat32(r+4,!0),n.getFloat32(r+8,!0)),r+=12;const c=n.getUint8(r);r+=1;const a=n.getUint32(r);r+=4;const l=this.readOptionalSceneId(t,r);console.log("Spawning "+i);const d=p(i);if(!d)return void console.error(`Failed to spawn actor: unknown actor type '${i}'`);const u=this.world.actors.find(e=>e.__netid===s);if(null!=u)return void this.applySpawnNetInfo(u,s,c,a);if(this.spawningActorNetIds.has(s))return void console.log("Actor was already spawned",null!=u);const f=this.findMaterializedNetStartupActor(l,i,s);if(null!=f)return this.applySpawnNetInfo(f,s,c,a),this.processQueuedActorMessages(s),void this.sendActorReady(f);if(null==l){this.spawningActorNetIds.add(s);try{const e=await this.world.spawnActorWithNetInfo(d,s,c,a,Q,B);this.processQueuedActorMessages(s),this.sendActorReady(e)}catch(e){return void console.error("Spawning replicated actor on client failed",e)}finally{this.spawningActorNetIds.delete(s)}}else this.getNetStartupActorSpawnQueue(l).push({from:e,buffer:t})}readOptionalSceneId(e,t){if(t+2>e.byteLength)return;const n=new DataView(e).getUint16(t,!0);return t+=2,0===n||t+n>e.byteLength?void 0:O(e,t,n)}findMaterializedNetStartupActor(e,t,n){if(null!=e)return this.world.actors.find(r=>{if(r.__netSceneId!==e)return!1;if(null!=r.__netid&&r.__netid!==n)return!1;return(r.constructor.__actorId??r.constructor.name)===t})}applySpawnNetInfo(e,t,n,r){if(e.__netid=t,e.netRole=n,0===r)e.owner=void 0;else{const t=this.world.actors.find(e=>e.__netid===r);null!=t&&(e.owner=t)}e instanceof M&&(e.isLocallyControlled=n===w.autonomousProxy)}getNetStartupActorSpawnQueue(e){let t=this.netStartupActorSpawnQueue.get(e);return null==t&&(t=[],this.netStartupActorSpawnQueue.set(e,t)),t}processQueuedNetStartupActorSpawns(e){if(null==e.__netSceneId)return;const t=this.netStartupActorSpawnQueue.get(e.__netSceneId);if(null!=t){this.netStartupActorSpawnQueue.delete(e.__netSceneId);for(const e of t)this.processMessage(e)}}processQueuedActorMessages(e){const t=this.actorRefMissingMessageQueue.get(e);if(null!=t){console.log(`Processing queued messages for actor ${e}`),this.actorRefMissingMessageQueue.delete(e);for(const e of t)this.processMessage(e)}}sendActorReady(e){if(this.mode===y.none)return;if(!e||null==e.__netid)return void console.warn("sendActorReady: actor or actor.__netid is undefined");if(this.isServer)return;const t=new ArrayBuffer(5),n=new DataView(t);let r=0;n.setUint8(r,P.actorReady),r+=1,n.setUint32(r,e.__netid,!0),this.session.server&&this.session.sendMessage(this.session.server,!0,t)}processActorReady(e,t){if(!this.isServer)return;const n=new DataView(t).getUint32(1,!0),r=this.world.actors.find(e=>e.__netid===n);if(!r)return void console.warn(`processActorReady: No actor found with net id ${n}`);const s=this.actorPreSpawnMessageBuffer.get(r),o=this.getConnectionKey(e),i=s?.get(o);if(null!=i){this.sendPropertySnapshot(r,e);const t=i.filter(e=>!this.isPropertyMessage(e.buffer));if(t.length>0){console.log(`Sending ${t.length} queued messages for actor ${r.__netid}`);for(const e of t)this.session.sendMessage(e.connection,e.reliable,e.buffer)}s.delete(o),0===s.size&&this.actorPreSpawnMessageBuffer.delete(r)}}sendRpc(e,t,n,r,s,o){if(this.mode==y.none)return;if(!this.shouldReplicate(n))return;const i=n.__netid;if(null==i)return void console.warn("No __netid defined on actor. Actor must be repliated for RPC");const c=this.resolveActorConnection(n),a=[];if(this.isServer)switch(e){case S.all:a.push(...this.session.clients.filter(e=>this.isRelevant(n,e)));break;case S.client:null!=c&&a.push(c);break;case S.notOwner:a.push(...this.session.clients.filter(e=>e.id!==c?.id&&this.isRelevant(n,e)));case S.server:}else switch(e){case S.all:case S.client:case S.notOwner:break;case S.server:a.push(this.session.server)}if(0==a.length)return;if(null!=o&&null==o.__netid)return void console.warn("No __netid defined on component. Component must be initialized for RPC",o);const l=null!=o,d=o??n,p=R(d,r);if(null==p)return void console.warn(`RPC function '${r}' is not registered on ${d.constructor?.name??"target"}`);const u=1===s.length?function(e){if(e instanceof Uint8Array)return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);if("undefined"!=typeof SharedArrayBuffer&&e instanceof SharedArrayBuffer)return new Uint8Array(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return null}(s[0]):null;if(null!=u){let e=10+u.byteLength;l&&(e+=1);const r=new ArrayBuffer(e),s=new DataView(r);let c=0;s.setUint8(c,l?P.rpcComponentBinary:P.rpcBinary),c+=1,s.setUint32(c,i,!0),c+=4,s.setUint8(c,p),c+=1,s.setUint32(c,u.byteLength,!0),c+=4,D(r,c,u),c+=u.byteLength,l&&(s.setUint8(c,o.__netid),c+=1);for(const e of a)null!=e&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:r});return}const f=this.serializer.encode(s),h=I.encode(f);if(h.byteLength>65535)return void console.warn("RPC arguments are too large to send with the JSON RPC format",r);let g=8+h.byteLength;l&&(g+=1);const w=new ArrayBuffer(g),m=new DataView(w);let v=0;m.setUint8(v,l?P.rpcComponent:P.rpc),v+=1,m.setUint32(v,i,!0),v+=4,m.setUint8(v,p),v+=1,m.setUint16(v,h.byteLength,!0),v+=2,D(w,v,h),v+=h.byteLength,l&&(m.setUint8(v,o.__netid),v+=1);for(const e of a)null!=e&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:w})}sendWithQueue(e,t){if(this.shouldQueueMessageUntilActorReady(e,t.connection)){return void this.getPreSpawnMessageQueue(e,t.connection).push(t)}this.session.sendMessage(t.connection,t.reliable,t.buffer)}findQueuedActorsInData(e,t=[]){if("object"==typeof e){if(Array.isArray(e))for(const t of e)this.findQueuedActorsInData(t);if(e instanceof d){this.actorPreSpawnMessageBuffer.get(e)}}}handlePropertySet(e,t,n,r,s,o){if(this.mode==y.none)return;if(!this.isServer)return;if(!this.shouldReplicate(n))return;let i=this.replicatedProperties.get(n);null==i&&(i={},this.replicatedProperties.set(n,i));const c=this.serializer.encode(s),a=I.encode(c),l=a.buffer.slice(a.byteOffset,a.byteOffset+a.byteLength);i[this.getReplicatedPropertyKey(r,o)]={property:r,replOn:e,reliable:t,buffer:l,component:o},this.sendProperty(e,t,n,r,l,o)}sendProperty(e,t,n,r,s,o){if(!this.isServer)return;const i=[],c=this.resolveActorConnection(n);switch(e){case b.all:i.push(...this.session.clients);break;case b.owner:null!=c&&i.push(c);break;case b.notOwner:null==c?i.push(...this.session.clients):i.push(...this.session.clients.filter(e=>e.id!==c.id))}const a=null!=o&&null!=o.__netid;let l=6+r.length+2+s.byteLength;a&&(l+=1);const d=new ArrayBuffer(l),p=new DataView(d);let u=0;p.setUint8(u,a?P.propComponent:P.prop),u+=1,p.setUint32(u,n.__netid,!0),u+=4,p.setUint8(u,r.length),u+=1;for(let e=0;e<r.length;e++)p.setUint8(u++,r.charCodeAt(e));p.setUint16(u,s.byteLength,!0),u+=2,new Uint8Array(d,u,s.byteLength).set(new Uint8Array(s)),u+=s.byteLength,a&&(p.setUint8(u,o.__netid),u+=1);for(const e of i)null!=e&&this.isRelevant(n,e)&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:d})}defineNetId(e){null==e.__netid&&(e.__netid=this.nextNetId++)}registerReplicatedProperty(e,t,n,r,s,o){if(null==n||null==r||null==s)return;let i=this.replicatedPropertyRegistry.get(n);null==i&&(i=[],this.replicatedPropertyRegistry.set(n,i));const c=o??(s instanceof u?s:void 0),a=i.find(e=>e.target===s&&e.property===r);if(null!=a)return a.replOn=e,a.reliable=t,void(a.component=c);i.push({property:r,replOn:e,reliable:t,target:s,component:c})}sendActorSpawn(e,t=this.session.clients){if(!this.isServer)return;const n=t.filter(t=>this.isRelevant(e,t));this.defineNetId(e);const r=e.__netid;let s=e.constructor.__actorId??e.constructor.name;const o=I.encode(s);let i;null!=e.__netSceneId&&(i=I.encode(e.__netSceneId),i.byteLength>65535&&(console.warn(`Scene actor id is too long to replicate as a startup actor: ${e.__netSceneId}`),i=void 0));const c=i?.byteLength??0,a=6+o.length+12+12+1+4+2+c,l=new ArrayBuffer(a),d=new DataView(l);let p=0;d.setUint8(p,P.spawn),p+=1,d.setUint32(p,r,!0),p+=4,d.setUint8(p,o.length),p+=1,new Uint8Array(l).set(o,p),p+=o.length,d.setFloat32(p,e.position.x,!0),p+=4,d.setFloat32(p,e.position.y,!0),p+=4,d.setFloat32(p,e.position.z,!0),p+=4,d.setFloat32(p,e.rotation.x,!0),p+=4,d.setFloat32(p,e.rotation.y,!0),p+=4,d.setFloat32(p,e.rotation.z,!0),p+=4;let u=p;p+=1,d.setUint32(p,e.owner?.__netid??0),p+=4,d.setUint16(p,c,!0),p+=2,null!=i&&(D(l,p,i),p+=i.byteLength);let f=this.replicatedActors.get(e);null==f&&(f={spawnedAt:Date.now(),connections:[]},this.replicatedActors.set(e,f));for(const t of n){const n=l.slice(0),r=new DataView(n),s=this.resolveActorConnection(e)?.id===t.id?w.autonomousProxy:w.simulatedProxy;r.setUint8(u,s),this.getPreSpawnMessageQueue(e,t),this.session.sendMessage(t,!0,n),f.connections.some(e=>e.id===t.id)||f.connections.push(t)}}sendActorRemove(e){if(!this.isServer)return;const t=new ArrayBuffer(5),n=new DataView(t);let r=0;n.setUint8(r,P.removeActor),r+=1,n.setUint32(r,e.__netid,!0);for(const e of this.session.clients)this.session.sendMessage(e,!0,t)}processRemoveActor(e,t){if(this.isServer||this.session.server?.id!==e.id)return;const n=new DataView(t).getUint32(1,!0),r=this.world.actors.find(e=>e.__netid===n);r&&(this.world.removeActor(r),this.replicatedActors?.delete?.(r),this.replicatedProperties?.delete?.(r))}setOwningConnection(e,t){this.actorConnection.set(e.id,t)}getOwningConnection(e){return this.actorConnection.get(e.id)}getActorsByOwningConnection(e){const t=[];return this.actorConnection.forEach((n,r)=>{if(n.id===e.id){const e=this.world.actors.find(e=>e.id===r);null!=e&&t.push(e)}}),t}resolveActorConnection(e){let t;for(;null==t&&null!=e;)t=this.actorConnection.get(e.id),null==t&&(e=e.owner);return t}isLocallyControlled(e){let t=e;for(;null!=t;){if(t.netRole===w.autonomousProxy)return!0;if(t instanceof U)return t.isLocallyControlled;t=e.owner}return!1}isLocalOwner(e){return this.isLocallyControlled(e)}sendServerTime(e){const t=new ArrayBuffer(9),n=new DataView(t);n.setUint8(0,P.serverTime),n.setBigUint64(1,BigInt(Date.now()),!0),this.session.sendMessage(e,!0,t)}parseServerTime(e){const t=new DataView(e).getBigUint64(1,!0);return Number(t)}getConnectionKey(e){return e.id.toString()}getPreSpawnMessageQueue(e,t){let n=this.actorPreSpawnMessageBuffer.get(e);null==n&&(n=new Map,this.actorPreSpawnMessageBuffer.set(e,n));const r=this.getConnectionKey(t);let s=n.get(r);return null==s&&(s=[],n.set(r,s)),s}shouldQueueMessageUntilActorReady(e,t){const n=this.actorPreSpawnMessageBuffer.get(e);return!!n?.has(this.getConnectionKey(t))||this.isServer&&!this.replicatedActors.has(e)&&this.shouldReplicate(e)}isPropertyMessage(e){const t=new DataView(e).getUint8(0);return t===P.prop||t===P.propComponent}getReplicatedPropertyKey(e,t){return null!=t?.__netid?`component:${t.__netid}:${e}`:`actor:${e}`}sendPropertySnapshot(e,t){const n=this.buildPropertySnapshotEntries(e,t);if(0===n.length)return;const r=this.createPropertySnapshotBuffer(e,n);this.session.sendMessage(t,!0,r)}buildPropertySnapshotEntries(e,t){if(!this.isRelevant(e,t))return[];const n=this.replicatedPropertyRegistry.get(e);if(null==n)return[];const r=[];for(const s of n){if(!this.shouldReplicatePropertyToConnection(e,s.replOn,t))continue;if(null!=s.component&&null==s.component.__netid)continue;const n=this.readReplicatedPropertyValue(s),o=this.serializer.encode(n),i=I.encode(o),c=i.buffer.slice(i.byteOffset,i.byteOffset+i.byteLength);r.push({property:s.property,component:s.component,buffer:c})}return r}shouldReplicatePropertyToConnection(e,t,n){const r=this.resolveActorConnection(e);switch(t){case b.all:return!0;case b.owner:return r?.id===n.id;case b.notOwner:return null==r||r.id!==n.id}}readReplicatedPropertyValue(e){const t=e.target[e.property];return A(t)?t.value:t}createPropertySnapshotBuffer(e,t){let n=7;const r=t.map(e=>I.encode(e.property));for(let e=0;e<t.length;e++){const s=t[e];n+=1,null!=s.component&&(n+=1),n+=1+r[e].byteLength+4+s.buffer.byteLength}const s=new ArrayBuffer(n),o=new DataView(s);let i=0;o.setUint8(i,P.propSnapshot),i+=1,o.setUint32(i,e.__netid,!0),i+=4,o.setUint16(i,t.length,!0),i+=2;for(let e=0;e<t.length;e++){const n=t[e],c=r[e],a=null!=n.component;o.setUint8(i,a?$.component:$.actor),i+=1,a&&(o.setUint8(i,n.component.__netid),i+=1),o.setUint8(i,c.byteLength),i+=1,new Uint8Array(s,i,c.byteLength).set(c),i+=c.byteLength,o.setUint32(i,n.buffer.byteLength,!0),i+=4,new Uint8Array(s,i,n.buffer.byteLength).set(new Uint8Array(n.buffer)),i+=n.buffer.byteLength}return s}decodeReplicatedValue(e){let t;if(null!=N)t=N.decode(e);else{t="";const n=new Uint8Array(e);for(let e=0;e<n.length;e++)t+=String.fromCharCode(n[e])}return this.serializer.decode(t,F)}applyReplicatedProperty(e,t,n){A(e[t])?e[t].value=n:e[t]=n}callRepNotify(e,t){const n=e[`onRep_${t}`];"function"==typeof n&&n.call(e)}};L=e([a()],L);export{L as NetService};const Q=new c,B=new i,I=new TextEncoder,N="undefined"!=typeof TextDecoder?new TextDecoder:void 0;function O(e,t,n){const r=new Uint8Array(e,t,n);if(null!=N)return N.decode(r);let s="";for(let e=0;e<r.length;e++)s+=String.fromCharCode(r[e]);return s}function D(e,t,n){new Uint8Array(e,t,n.byteLength).set(n)}const F=new m;/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,8 @@
1
+ type SignalDispose = () => void;
2
+ type SignalLike<T = unknown> = {
3
+ subscribe: (fn: (value: T) => void) => SignalDispose;
4
+ value: T;
5
+ };
6
+ export declare function isSignalLike<T = unknown>(value: unknown): value is SignalLike<T>;
7
+ export {};
8
+ //# sourceMappingURL=net-utils.d.ts.map
@@ -0,0 +1,4 @@
1
+ export function isSignalLike(e){return"object"==typeof e&&null!==e&&"function"==typeof e.subscribe&&"value"in e}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */