@hology/core 0.0.254 → 0.0.255

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.
@@ -21,9 +21,12 @@ export declare class NetPlayerRegistry {
21
21
  private readonly sessions;
22
22
  private readonly joinedSubject;
23
23
  private readonly leftSubject;
24
+ private readonly changedSubject;
24
25
  private subscriptions;
25
26
  readonly playerJoined$: Observable<PlayerSession>;
26
27
  readonly playerLeft$: Observable<PlayerSession>;
28
+ /** Emits whenever player membership, status, controller, or player state changes. */
29
+ readonly changed$: Observable<void>;
27
30
  get values(): readonly PlayerSession[];
28
31
  get(playerId: NetPlayerId): PlayerSession | undefined;
29
32
  start(session: NetSession): void;
@@ -1,4 +1,4 @@
1
- import{Subject as t}from"rxjs";import{NetMode as e}from"./net-session.js";export class NetPlayerRegistry{constructor(){this.sessions=new Map,this.joinedSubject=new t,this.leftSubject=new t,this.subscriptions=[],this.playerJoined$=this.joinedSubject.asObservable(),this.playerLeft$=this.leftSubject.asObservable()}get values(){return[...this.sessions.values()]}get(t){return this.sessions.get(t)}start(t){if(this.stop(!1),null!=t.localPlayer&&this.upsert(t.localPlayer,void 0,0),t.mode<e.client){for(const e of t.clients)this.addConnection(e);this.subscriptions.push(t.playerJoined.subscribe(t=>this.addConnection(t)),t.playerLeft.subscribe(t=>this.removeConnection(t)))}}startLocal(t){this.stop(!1),this.upsert(t,void 0,0)}attachController(t,e){const s=this.require(t);s.controller=e,e.playerId=t,null!=s.playerState?e.playerState=s.playerState:null!=e.playerState&&(s.playerState=e.playerState),s.status="active"}attachPlayerState(t,e){const s=this.require(t);s.playerState=e,e.playerId=t,null!=s.info.displayName&&(e.displayName=s.info.displayName),null!=s.controller&&(s.controller.playerState=e)}setStatus(t,e){this.require(t).status=e}stop(t=!0){for(const t of this.subscriptions)t.unsubscribe();this.subscriptions=[],t&&this.sessions.clear()}addConnection(t){null!=t.player&&this.upsert(t.player,t)}upsert(t,e,s){const n=this.sessions.get(t.id);if(null!=n){const t="disconnected"===n.status;return n.connection=e,n.status="active",null!=s&&(n.localPlayerIndex=s),t&&this.joinedSubject.next(n),n}const i={info:t,connection:e,localPlayerIndex:s,status:"joining"};return this.sessions.set(t.id,i),this.joinedSubject.next(i),i}removeConnection(t){const e=t.player?.id;if(null==e)return;const s=this.sessions.get(e);null!=s&&s.connection?.id===t.id&&(s.connection=void 0,s.status="disconnected",this.leftSubject.next(s))}require(t){const e=this.sessions.get(t);if(null==e)throw new Error(`Unknown network player ${JSON.stringify(t)}`);return e}}/*
1
+ import{Subject as t}from"rxjs";import{NetMode as e}from"./net-session.js";export class NetPlayerRegistry{constructor(){this.sessions=new Map,this.joinedSubject=new t,this.leftSubject=new t,this.changedSubject=new t,this.subscriptions=[],this.playerJoined$=this.joinedSubject.asObservable(),this.playerLeft$=this.leftSubject.asObservable(),this.changed$=this.changedSubject.asObservable()}get values(){return[...this.sessions.values()]}get(t){return this.sessions.get(t)}start(t){if(this.stop(!1),null!=t.localPlayer&&this.upsert(t.localPlayer,void 0,0),t.mode<e.client){for(const e of t.clients)this.addConnection(e);this.subscriptions.push(t.playerJoined.subscribe(t=>this.addConnection(t)),t.playerLeft.subscribe(t=>this.removeConnection(t)))}}startLocal(t){this.stop(!1),this.upsert(t,void 0,0)}attachController(t,e){const s=this.require(t);s.controller=e,e.playerId=t,null!=s.playerState?e.playerState=s.playerState:null!=e.playerState&&(s.playerState=e.playerState),s.status="active",this.changedSubject.next()}attachPlayerState(t,e){const s=this.require(t);s.playerState=e,e.playerId=t,null!=s.info.displayName&&(e.displayName=s.info.displayName),null!=s.controller&&(s.controller.playerState=e),this.changedSubject.next()}setStatus(t,e){const s=this.require(t);s.status!==e&&(s.status=e,this.changedSubject.next())}stop(t=!0){for(const t of this.subscriptions)t.unsubscribe();this.subscriptions=[],t&&this.sessions.size>0&&(this.sessions.clear(),this.changedSubject.next())}addConnection(t){null!=t.player&&this.upsert(t.player,t)}upsert(t,e,s){const n=this.sessions.get(t.id);if(null!=n){const t="disconnected"===n.status;return n.connection=e,n.status="active",null!=s&&(n.localPlayerIndex=s),t&&this.joinedSubject.next(n),this.changedSubject.next(),n}const i={info:t,connection:e,localPlayerIndex:s,status:"joining"};return this.sessions.set(t.id,i),this.joinedSubject.next(i),this.changedSubject.next(),i}removeConnection(t){const e=t.player?.id;if(null==e)return;const s=this.sessions.get(e);null!=s&&s.connection?.id===t.id&&(s.connection=void 0,s.status="disconnected",this.leftSubject.next(s),this.changedSubject.next())}require(t){const e=this.sessions.get(t);if(null==e)throw new Error(`Unknown network player ${JSON.stringify(t)}`);return e}}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,4 +1,4 @@
1
- import{__decorate as e}from"tslib";import{delay as t,interval as n,Subject as s,takeUntil as r,tap as i}from"rxjs";import{Euler as o,Vector3 as a}from"three";import{Service as c}from"typedi";import{$actorOptions as l,BaseActor as d,getActorClassById as p}from"../../actors/actor.js";import{ActorComponent as u}from"../../actors/component.js";import{$actorComponents as h}from"../../actors/internal/component-init.js";import{inject as f}from"../../inject.js";import{World as g}from"../../services/world.js";import{NetMode as y}from"../net-session.js";import{NetRole as w}from"./net-actor-role.js";import{DecodeResult as m,NetSerializer as v}from"./net-serializer.js";import{isSignalLike as S}from"./net-utils.js";import{ReplOn as R}from"./replication.js";import{getRpcMethodId as A,getRpcMethodName as b,RunsOn as C}from"./rpc.js";import{ActorController as _,BasePlayerController as M,BasePlayerState as U}from"../../actors/controller/actor-controller.js";import{BaseGameState as P}from"../../actors/controller/game-state.js";import{makePropertyReplicated as T}from"./net-decorator.js";import{PlayerRuntime as I}from"../../player-runtime.js";var B;!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",e[e.sceneTravelBegin=101]="sceneTravelBegin",e[e.sceneTravelReady=102]="sceneTravelReady",e[e.sceneTravelComplete=103]="sceneTravelComplete"}(B||(B={}));var $;!function(e){e[e.actor=0]="actor",e[e.component=1]="component"}($||($={}));let L=class{constructor(){this.playerRuntime=f(I),this.players=this.playerRuntime.players,this.suspendedReplicationConnections=new Set,this.sceneTravelBeginSubject=new s,this.sceneTravelReadySubject=new s,this.sceneTravelCompleteSubject=new s,this.sceneTravelBegin$=this.sceneTravelBeginSubject.asObservable(),this.sceneTravelReady$=this.sceneTravelReadySubject.asObservable(),this.sceneTravelComplete$=this.sceneTravelCompleteSubject.asObservable(),this.actorConnection=new Map,this.destroyed=new s,this.world=f(g),this.serializer=f(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 pendingSceneTravel(){return this.activeSceneTravel}get gameState(){return this.playerRuntime.gameState}setGameState(e){if(null!=this.playerRuntime.gameState&&this.playerRuntime.gameState!==e)throw new Error(`A game state is already registered as ${this.playerRuntime.gameState.constructor.name}`);this.playerRuntime.setGameState(e)}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 s,this.playerRuntime.startNetwork(this.session),this.playerRuntime.setOwningConnectionHandler((e,t)=>this.setOwningConnection(e,t)),this.playerRuntime.setClearOwningConnectionHandler(e=>this.clearOwningConnection(e)),this.playerRuntime.setGameStateValidator(e=>{if(!this.shouldReplicate(e))throw new Error(`${e.constructor.name} must use @Actor({ replicate: true }) to be a game state`)}),this.mode!==y.none){if(this.isServer)for(const e of this.session.clients)this.sendServerTime(e);n(15).pipe(r(this.destroyed)).subscribe(()=>{this.processMessages()}),this.world.actorAdded.pipe(r(this.destroyed),i(e=>{this.configureMaterializedActorRole(e),this.isServer?this.defineNetId(e):this.processQueuedNetStartupActorSpawns(e),e instanceof M?(T(e,"playerId",this,!0,R.all),T(e,"playerState",this,!0,R.all),T(e,"pawn",this,!0,R.all)):e instanceof U?(T(e,"playerId",this,!0,R.all),T(e,"displayName",this,!0,R.all),T(e,"connected",this,!0,R.all)):e instanceof P&&T(e,"playerStates",this,!0,R.all)}),t(8)).subscribe(e=>{this.shouldReplicate(e)&&(T(e,"owner",this,!0,R.all),this.sendActorSpawn(e))}),this.world.actorRemoved.pipe(r(this.destroyed),t(10)).subscribe(e=>{this.sendActorRemove(e),e===this.playerRuntime.gameState&&(this.playerRuntime.gameState=void 0),this.actorConnection.delete(e.id),this.replicatedActors.delete(e),this.replicatedProperties.delete(e),this.replicatedPropertyRegistry.delete(e),this.actorPreSpawnMessageBuffer.delete(e)}),this.session.playerJoined.pipe(r(this.destroyed)).subscribe(e=>{null==this.activeSceneTravel?(this.sendServerTime(e),this.isServer&&this.replicatedActors.size>0&&this.spawnOnLateClient(e)):this.suspendAndSendSceneTravel(e,this.activeSceneTravel)})}}stop(){this.destroyed.next(null),this.destroyed.complete(),this.playerRuntime.stop(),this.suspendedReplicationConnections.clear(),this.activeSceneTravel=void 0}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 B.rpc:case B.rpcComponent:case B.rpcBinary:case B.rpcComponentBinary:this.processRpc(e.from,e.buffer);break;case B.prop:case B.propComponent:this.processProp(e.from,e.buffer);break;case B.propSnapshot:this.processPropSnapshot(e.from,e.buffer);break;case B.spawn:await this.processSpawn(e.from,e.buffer);break;case B.removeActor:this.processRemoveActor(e.from,e.buffer);break;case B.serverTime:this.processServerTime(e.from,e.buffer);break;case B.actorReady:this.processActorReady(e.from,e.buffer);break;case B.sceneTravelBegin:this.processSceneTravelBegin(e.from,e.buffer);break;case B.sceneTravelReady:this.processSceneTravelReady(e.from,e.buffer);break;case B.sceneTravelComplete:this.processSceneTravelComplete(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 s=new DataView(t),r=s.getUint8(n);n+=1;const i=s.getUint32(n,!0);n+=4;const o=s.getUint8(n);n+=1;const a=r===B.rpcBinary||r===B.rpcComponentBinary;let c,l,d="";if(a){const e=s.getUint32(n,!0);n+=4,l=new Uint8Array(t,n,e),n+=e}else{const e=s.getUint16(n,!0);n+=2,d=j(t,n,e),n+=e;try{c=this.serializer.decode(d,V)}catch(e){return void console.warn("Failed to parse RPC args JSON",d,e)}}if(!a&&V.error){if(null!=V.missingActorId){const n=this.getActorRefMissingMessageQueue(V.missingActorId);console.debug(`Missing referenced actor ${V.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===i);if(null==p){return this.getActorRefMissingMessageQueue(i).push({from:e,buffer:t}),void console.debug(`Missing actor ${i}. Queueing message for later`)}let u;if(r===B.rpcComponent||r===B.rpcComponentBinary){const e=s.getUint8(n),t=p[h];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 ${i}`)}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 f=u??p,g=b(f,o);if(null!=g)if("function"==typeof f[g])if(a)f[g](l);else{if(V.requiresAssetLookups)return this.serializer.decodeAssets(c).then(e=>{f[g](...e)}).catch(e=>{console.warn(`Failed to decode asset references for RPC '${g}' on actor net id ${p.__netid}`,e)});f[g](...c)}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 ${o} 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 s=new DataView(t),r=s.getUint8(n);n+=1;const i=s.getUint32(n,!0);n+=4;const o=s.getUint8(n);n+=1;let a="";for(let e=0;e<o;e++)a+=String.fromCharCode(s.getUint8(n+e));n+=o;const c=s.getUint16(n,!0);n+=2;const l=t.slice(n,n+c);n+=c;const d=this.world.actors.find(e=>e.__netid===i);if(!d){return this.getActorRefMissingMessageQueue(i).push({from:e,buffer:t}),void console.error("Failed to process property replication. No actor found with id",i,a)}let p;if(r===B.propComponent){const e=s.getUint8(n),t=d[h];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 ${i}`)}const u=p??d;try{const n=this.decodeReplicatedValue(l);if(V.error){if(null!=V.missingActorId){let n=this.getActorRefMissingMessageQueue(V.missingActorId);console.debug(`Missing referenced actor ${V.missingActorId}. Queueing message for later`),n.push({from:e,buffer:t})}return}if(V.requiresAssetLookups)return this.serializer.decodeAssets(n).then(e=>{this.applyReplicatedProperty(u,a,e),this.callRepNotify(u,a)}).catch(e=>{console.warn(`Failed to decode asset references for replicated property '${a}' on actor id ${i}`,e)});this.applyReplicatedProperty(u,a,n),this.callRepNotify(u,a)}catch(e){console.warn(`Failed to decode replicated property '${a}' on actor id ${i}`,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 s=new DataView(t);n+=1;const r=s.getUint32(n,!0);n+=4;const i=this.world.actors.find(e=>e.__netid===r);if(null==i){return this.getActorRefMissingMessageQueue(r).push({from:e,buffer:t}),void console.debug(`Missing actor ${r}. Queueing property snapshot for later`)}const o=s.getUint16(n,!0);n+=2;const a=[];for(let c=0;c<o;c++){const o=s.getUint8(n);n+=1;let c=i;if(o===$.component){const e=s.getUint8(n);n+=1;const t=i[h];if(c=t?.find(t=>t.__netid===e),null==c)return void console.error(`Failed to process property snapshot. No component found with netid ${e} on actor with netid ${r}`)}else if(o!==$.actor)return void console.error(`Failed to process property snapshot. Unknown target kind ${o}`);const l=s.getUint8(n);n+=1;let d="";for(let e=0;e<l;e++)d+=String.fromCharCode(s.getUint8(n+e));n+=l;const p=s.getUint32(n,!0);n+=4;const u=t.slice(n,n+p);n+=p;try{const n=this.decodeReplicatedValue(u);if(V.error){if(null!=V.missingActorId){this.getActorRefMissingMessageQueue(V.missingActorId).push({from:e,buffer:t}),console.debug(`Missing referenced actor ${V.missingActorId}. Queueing property snapshot for later`)}else console.warn(`Unknown decode error in property snapshot for actor ${r}`);return}a.push({target:c,property:d,value:n,requiresAssetLookups:V.requiresAssetLookups})}catch(e){return void console.warn(`Failed to decode replicated property snapshot entry '${d}' on actor id ${r}`,e)}}if(a.some(e=>e.requiresAssetLookups))return Promise.all(a.map(async e=>{e.requiresAssetLookups&&(e.value=await this.serializer.decodeAssets(e.value))})).then(()=>{this.applyReplicatedProperties(a)}).catch(e=>{console.warn(`Failed to decode asset references for property snapshot on actor id ${r}`,e)});this.applyReplicatedProperties(a)}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 s=0;s+=1;const r=n.getUint32(s,!0);s+=4;const i=n.getUint8(s);s+=1;let o="";for(let e=0;e<i;e++)o+=String.fromCharCode(n.getUint8(s+e));s+=i,O.set(n.getFloat32(s,!0),n.getFloat32(s+4,!0),n.getFloat32(s+8,!0)),s+=12,Q.set(n.getFloat32(s,!0),n.getFloat32(s+4,!0),n.getFloat32(s+8,!0)),s+=12;const a=n.getUint8(s);s+=1;const c=n.getUint32(s);s+=4;const l=this.readOptionalSceneId(t,s);console.log("Spawning "+o);const d=p(o);if(!d)return void console.error(`Failed to spawn actor: unknown actor type '${o}'`);const u=this.world.actors.find(e=>e.__netid===r);if(null!=u)return this.applySpawnNetInfo(u,r,a,c),u instanceof P&&this.setGameState(u),this.processQueuedActorMessages(r),void this.sendActorReady(u);if(this.spawningActorNetIds.has(r))return void console.log("Actor was already spawned",null!=u);const h=this.findMaterializedNetStartupActor(l,o,r);if(null!=h)return this.applySpawnNetInfo(h,r,a,c),h instanceof P&&this.setGameState(h),this.processQueuedActorMessages(r),void this.sendActorReady(h);if(null==l){this.spawningActorNetIds.add(r);try{const e=await this.world.spawnActorWithNetInfo(d,r,a,c,O,Q);e instanceof P&&this.setGameState(e),this.processQueuedActorMessages(r),this.sendActorReady(e)}catch(e){return void console.error("Spawning replicated actor on client failed",e)}finally{this.spawningActorNetIds.delete(r)}}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:j(e,t,n)}findMaterializedNetStartupActor(e,t,n){if(null!=e)return this.world.actors.find(s=>{if(s.__netSceneId!==e)return!1;if(null!=s.__netid&&s.__netid!==n)return!1;return(s.constructor.__actorId??s.constructor.name)===t})}applySpawnNetInfo(e,t,n,s){if(e.__netid=t,e.netRole=n,0===s)e.owner=void 0;else{const t=this.world.actors.find(e=>e.__netid===s);null!=t&&(e.owner=t)}e instanceof M&&e.setLocallyControlled(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 s=0;n.setUint8(s,B.actorReady),s+=1,n.setUint32(s,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),s=this.world.actors.find(e=>e.__netid===n);if(!s)return void console.warn(`processActorReady: No actor found with net id ${n}`);const r=this.actorPreSpawnMessageBuffer.get(s),i=this.getConnectionKey(e),o=r?.get(i);if(null!=o){this.sendPropertySnapshot(s,e);const t=o.filter(e=>!this.isPropertyMessage(e.buffer));if(t.length>0){console.log(`Sending ${t.length} queued messages for actor ${s.__netid}`);for(const e of t)this.sendMessage(e.connection,e.reliable,e.buffer)}r.delete(i),0===r.size&&this.actorPreSpawnMessageBuffer.delete(s)}}sendRpc(e,t,n,s,r,i){if(this.mode==y.none)return;if(!this.shouldReplicate(n))return;const o=n.__netid;if(null==o)return void console.warn("No __netid defined on actor. Actor must be repliated for RPC");const a=this.resolveActorConnection(n),c=[];if(this.isServer)switch(e){case C.all:c.push(...this.session.clients.filter(e=>this.isRelevant(n,e)));break;case C.client:null!=a&&c.push(a);break;case C.notOwner:c.push(...this.session.clients.filter(e=>e.id!==a?.id&&this.isRelevant(n,e)));case C.server:}else switch(e){case C.all:case C.client:case C.notOwner:break;case C.server:c.push(this.session.server)}if(0==c.length)return;if(null!=i&&null==i.__netid)return void console.warn("No __netid defined on component. Component must be initialized for RPC",i);const l=null!=i,d=i??n,p=A(d,s);if(null==p)return void console.warn(`RPC function '${s}' is not registered on ${d.constructor?.name??"target"}`);const u=1===r.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}(r[0]):null;if(null!=u){let e=10+u.byteLength;l&&(e+=1);const s=new ArrayBuffer(e),r=new DataView(s);let a=0;r.setUint8(a,l?B.rpcComponentBinary:B.rpcBinary),a+=1,r.setUint32(a,o,!0),a+=4,r.setUint8(a,p),a+=1,r.setUint32(a,u.byteLength,!0),a+=4,k(s,a,u),a+=u.byteLength,l&&(r.setUint8(a,i.__netid),a+=1);for(const e of c)null!=e&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:s});return}const h=this.serializer.encode(r),f=N.encode(h);if(f.byteLength>65535)return void console.warn("RPC arguments are too large to send with the JSON RPC format",s);let g=8+f.byteLength;l&&(g+=1);const w=new ArrayBuffer(g),m=new DataView(w);let v=0;m.setUint8(v,l?B.rpcComponent:B.rpc),v+=1,m.setUint32(v,o,!0),v+=4,m.setUint8(v,p),v+=1,m.setUint16(v,f.byteLength,!0),v+=2,k(w,v,f),v+=f.byteLength,l&&(m.setUint8(v,i.__netid),v+=1);for(const e of c)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.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,s,r,i){if(this.mode==y.none)return;if(!this.isServer)return;if(!this.shouldReplicate(n))return;let o=this.replicatedProperties.get(n);null==o&&(o={},this.replicatedProperties.set(n,o));const a=this.serializer.encode(r),c=N.encode(a),l=c.buffer.slice(c.byteOffset,c.byteOffset+c.byteLength);o[this.getReplicatedPropertyKey(s,i)]={property:s,replOn:e,reliable:t,buffer:l,component:i},this.sendProperty(e,t,n,s,l,i)}sendProperty(e,t,n,s,r,i){if(!this.isServer)return;const o=[],a=this.resolveActorConnection(n);switch(e){case R.all:o.push(...this.session.clients);break;case R.owner:null!=a&&o.push(a);break;case R.notOwner:null==a?o.push(...this.session.clients):o.push(...this.session.clients.filter(e=>e.id!==a.id))}const c=null!=i&&null!=i.__netid;let l=6+s.length+2+r.byteLength;c&&(l+=1);const d=new ArrayBuffer(l),p=new DataView(d);let u=0;p.setUint8(u,c?B.propComponent:B.prop),u+=1,p.setUint32(u,n.__netid,!0),u+=4,p.setUint8(u,s.length),u+=1;for(let e=0;e<s.length;e++)p.setUint8(u++,s.charCodeAt(e));p.setUint16(u,r.byteLength,!0),u+=2,new Uint8Array(d,u,r.byteLength).set(new Uint8Array(r)),u+=r.byteLength,c&&(p.setUint8(u,i.__netid),u+=1);for(const e of o)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,s,r,i){if(null==n||null==s||null==r)return;let o=this.replicatedPropertyRegistry.get(n);null==o&&(o=[],this.replicatedPropertyRegistry.set(n,o));const a=i??(r instanceof u?r:void 0),c=o.find(e=>e.target===r&&e.property===s);if(null!=c)return c.replOn=e,c.reliable=t,void(c.component=a);o.push({property:s,replOn:e,reliable:t,target:r,component:a})}sendActorSpawn(e,t=this.session.clients){if(!this.isServer)return;const n=this.replicatedActors.get(e),s=t.filter(t=>!!this.isRelevant(e,t)&&!n?.connections.some(e=>e.id===t.id));if(0===s.length)return;this.defineNetId(e);const r=e.__netid;let i=e.constructor.__actorId??e.constructor.name;const o=N.encode(i);let a;null!=e.__netSceneId&&(a=N.encode(e.__netSceneId),a.byteLength>65535&&(console.warn(`Scene actor id is too long to replicate as a startup actor: ${e.__netSceneId}`),a=void 0));const c=a?.byteLength??0,l=6+o.length+12+12+1+4+2+c,d=new ArrayBuffer(l),p=new DataView(d);let u=0;p.setUint8(u,B.spawn),u+=1,p.setUint32(u,r,!0),u+=4,p.setUint8(u,o.length),u+=1,new Uint8Array(d).set(o,u),u+=o.length,p.setFloat32(u,e.position.x,!0),u+=4,p.setFloat32(u,e.position.y,!0),u+=4,p.setFloat32(u,e.position.z,!0),u+=4,p.setFloat32(u,e.rotation.x,!0),u+=4,p.setFloat32(u,e.rotation.y,!0),u+=4,p.setFloat32(u,e.rotation.z,!0),u+=4;let h=u;u+=1,p.setUint32(u,e.owner?.__netid??0),u+=4,p.setUint16(u,c,!0),u+=2,null!=a&&(k(d,u,a),u+=a.byteLength);let f=this.replicatedActors.get(e);null==f&&(f={spawnedAt:Date.now(),connections:[]},this.replicatedActors.set(e,f));for(const t of s){const n=d.slice(0),s=new DataView(n),r=this.resolveActorConnection(e)?.id===t.id?w.autonomousProxy:w.simulatedProxy;s.setUint8(h,r),this.getPreSpawnMessageQueue(e,t),this.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 s=0;n.setUint8(s,B.removeActor),s+=1,n.setUint32(s,e.__netid,!0);for(const e of this.session.clients)this.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),s=this.world.actors.find(e=>e.__netid===n);s&&(this.world.removeActor(s),this.replicatedActors?.delete?.(s),this.replicatedProperties?.delete?.(s))}setOwningConnection(e,t){this.actorConnection.set(e.id,t),this.isServer&&this.world.actors.includes(e)&&this.shouldReplicate(e)&&!0===this.getRelevancyOptions(e)?.ownerOnly&&this.sendActorSpawn(e,[t])}clearOwningConnection(e){this.actorConnection.delete(e.id)}getOwningConnection(e){return this.actorConnection.get(e.id)}getActorsByOwningConnection(e){const t=[];return this.actorConnection.forEach((n,s)=>{if(n.id===e.id){const e=this.world.actors.find(e=>e.id===s);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 _)return t.isLocallyControlled;t=t.owner}return!1}isLocalOwner(e){return this.isLocallyControlled(e)}beginSceneTravel(e,t){if(!this.isServer)throw new Error("Only authority can begin scene travel");const n={travelId:e,sceneName:t};this.activeSceneTravel=n;for(const e of this.session.clients)this.suspendAndSendSceneTravel(e,n)}sendSceneTravelReady(e,t){if(!this.isClient||null==this.session.server)return;const n=this.createSceneTravelMessage(B.sceneTravelReady,{travelId:e,sceneName:t});this.session.sendMessage(this.session.server,!0,n)}completeSceneTravel(e,t){if(!this.isServer)throw new Error("Only authority can complete scene travel");const n=this.createSceneTravelMessage(B.sceneTravelComplete,{travelId:e,sceneName:t});for(const e of this.session.clients)this.resetReplicationState(e),this.suspendedReplicationConnections.delete(this.getConnectionKey(e)),this.spawnOnLateClient(e),this.session.sendMessage(e,!0,n);this.activeSceneTravel=void 0}suspendAndSendSceneTravel(e,t){this.suspendedReplicationConnections.add(this.getConnectionKey(e));const n=this.createSceneTravelMessage(B.sceneTravelBegin,t);this.session.sendMessage(e,!0,n)}processSceneTravelBegin(e,t){if(!this.isClient||this.session.server?.id!==e.id)return;const n=this.parseSceneTravelMessage(t);this.activeSceneTravel=n,this.sceneTravelBeginSubject.next(n)}processSceneTravelReady(e,t){if(!this.isServer||!this.session.clients.some(t=>t.id===e.id))return;const n=this.parseSceneTravelMessage(t);this.sceneTravelReadySubject.next({travelId:n.travelId,connection:e})}processSceneTravelComplete(e,t){if(!this.isClient||this.session.server?.id!==e.id)return;const n=this.parseSceneTravelMessage(t);this.activeSceneTravel=void 0,this.sceneTravelCompleteSubject.next(n)}createSceneTravelMessage(e,t){const n=N.encode(JSON.stringify(t)),s=new ArrayBuffer(1+n.byteLength);return new DataView(s).setUint8(0,e),k(s,1,n),s}parseSceneTravelMessage(e){const t=e.slice(1),n=null!=D?D.decode(t):String.fromCharCode(...new Uint8Array(t));return JSON.parse(n)}sendMessage(e,t,n){this.isServer&&this.suspendedReplicationConnections.has(this.getConnectionKey(e))||this.session.sendMessage(e,t,n)}sendServerTime(e){const t=new ArrayBuffer(9),n=new DataView(t);n.setUint8(0,B.serverTime),n.setBigUint64(1,BigInt(Date.now()),!0),this.sendMessage(e,!0,t)}parseServerTime(e){const t=new DataView(e).getBigUint64(1,!0);return Number(t)}getConnectionKey(e){return e.id.toString()}resetReplicationState(e){const t=this.getConnectionKey(e);for(const[n,s]of this.replicatedActors){for(let t=s.connections.length-1;t>=0;t--)s.connections[t].id===e.id&&s.connections.splice(t,1);const r=this.actorPreSpawnMessageBuffer.get(n);r?.delete(t),0===r?.size&&this.actorPreSpawnMessageBuffer.delete(n)}}getPreSpawnMessageQueue(e,t){let n=this.actorPreSpawnMessageBuffer.get(e);null==n&&(n=new Map,this.actorPreSpawnMessageBuffer.set(e,n));const s=this.getConnectionKey(t);let r=n.get(s);return null==r&&(r=[],n.set(s,r)),r}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===B.prop||t===B.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 s=this.createPropertySnapshotBuffer(e,n);this.sendMessage(t,!0,s)}buildPropertySnapshotEntries(e,t){if(!this.isRelevant(e,t))return[];const n=this.replicatedPropertyRegistry.get(e);if(null==n)return[];const s=[];for(const r of n){if(!this.shouldReplicatePropertyToConnection(e,r.replOn,t))continue;if(null!=r.component&&null==r.component.__netid)continue;const n=this.readReplicatedPropertyValue(r),i=this.serializer.encode(n),o=N.encode(i),a=o.buffer.slice(o.byteOffset,o.byteOffset+o.byteLength);s.push({property:r.property,component:r.component,buffer:a})}return s}shouldReplicatePropertyToConnection(e,t,n){const s=this.resolveActorConnection(e);switch(t){case R.all:return!0;case R.owner:return s?.id===n.id;case R.notOwner:return null==s||s.id!==n.id}}readReplicatedPropertyValue(e){const t=e.target[e.property];return S(t)?t.value:t}createPropertySnapshotBuffer(e,t){let n=7;const s=t.map(e=>N.encode(e.property));for(let e=0;e<t.length;e++){const r=t[e];n+=1,null!=r.component&&(n+=1),n+=1+s[e].byteLength+4+r.buffer.byteLength}const r=new ArrayBuffer(n),i=new DataView(r);let o=0;i.setUint8(o,B.propSnapshot),o+=1,i.setUint32(o,e.__netid,!0),o+=4,i.setUint16(o,t.length,!0),o+=2;for(let e=0;e<t.length;e++){const n=t[e],a=s[e],c=null!=n.component;i.setUint8(o,c?$.component:$.actor),o+=1,c&&(i.setUint8(o,n.component.__netid),o+=1),i.setUint8(o,a.byteLength),o+=1,new Uint8Array(r,o,a.byteLength).set(a),o+=a.byteLength,i.setUint32(o,n.buffer.byteLength,!0),o+=4,new Uint8Array(r,o,n.buffer.byteLength).set(new Uint8Array(n.buffer)),o+=n.buffer.byteLength}return r}decodeReplicatedValue(e){let t;if(null!=D)t=D.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,V)}applyReplicatedProperty(e,t,n){S(e[t])?e[t].value=n:e[t]=n,this.synchronizeReplicatedPlayerActor(e)}synchronizeReplicatedPlayerActor(e){if(e instanceof M&&null!=e.playerId){const t=this.players.get(e.playerId);if(null==t)return;return this.players.attachController(e.playerId,e),void e.setLocallyControlled(null!=t.localPlayerIndex)}if(e instanceof U&&null!=e.playerId){null!=this.players.get(e.playerId)&&this.players.attachPlayerState(e.playerId,e)}}callRepNotify(e,t){const n=e[`onRep_${t}`];"function"==typeof n&&n.call(e)}};L=e([c()],L);export{L as NetService};const O=new a,Q=new o,N=new TextEncoder,D="undefined"!=typeof TextDecoder?new TextDecoder:void 0;function j(e,t,n){const s=new Uint8Array(e,t,n);if(null!=D)return D.decode(s);let r="";for(let e=0;e<s.length;e++)r+=String.fromCharCode(s[e]);return r}function k(e,t,n){new Uint8Array(e,t,n.byteLength).set(n)}const V=new m;/*
1
+ import{__decorate as e}from"tslib";import{delay as t,interval as n,Subject as s,takeUntil as r,tap as i}from"rxjs";import{Euler as o,Vector3 as a}from"three";import{Service as c}from"typedi";import{$actorOptions as l,BaseActor as d,getActorClassById as p}from"../../actors/actor.js";import{ActorComponent as u}from"../../actors/component.js";import{$actorComponents as h}from"../../actors/internal/component-init.js";import{inject as f}from"../../inject.js";import{World as g}from"../../services/world.js";import{NetMode as y}from"../net-session.js";import{NetRole as w}from"./net-actor-role.js";import{DecodeResult as m,NetSerializer as v}from"./net-serializer.js";import{isSignalLike as S}from"./net-utils.js";import{ReplOn as R}from"./replication.js";import{getRpcMethodId as A,getRpcMethodName as b,RunsOn as C}from"./rpc.js";import{ActorController as _,BasePlayerController as M,BasePlayerState as U}from"../../actors/controller/actor-controller.js";import{BaseGameState as P}from"../../actors/controller/game-state.js";import{makePropertyReplicated as T}from"./net-decorator.js";import{PlayerRuntime as I}from"../../player-runtime.js";var B;!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",e[e.sceneTravelBegin=101]="sceneTravelBegin",e[e.sceneTravelReady=102]="sceneTravelReady",e[e.sceneTravelComplete=103]="sceneTravelComplete"}(B||(B={}));var $;!function(e){e[e.actor=0]="actor",e[e.component=1]="component"}($||($={}));let L=class{constructor(){this.playerRuntime=f(I),this.players=this.playerRuntime.players,this.suspendedReplicationConnections=new Set,this.sceneTravelBeginSubject=new s,this.sceneTravelReadySubject=new s,this.sceneTravelCompleteSubject=new s,this.sceneTravelBegin$=this.sceneTravelBeginSubject.asObservable(),this.sceneTravelReady$=this.sceneTravelReadySubject.asObservable(),this.sceneTravelComplete$=this.sceneTravelCompleteSubject.asObservable(),this.actorConnection=new Map,this.destroyed=new s,this.world=f(g),this.serializer=f(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 pendingSceneTravel(){return this.activeSceneTravel}get gameState(){return this.playerRuntime.gameState}setGameState(e){if(null!=this.playerRuntime.gameState&&this.playerRuntime.gameState!==e)throw new Error(`A game state is already registered as ${this.playerRuntime.gameState.constructor.name}`);this.playerRuntime.setGameState(e)}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 s,this.playerRuntime.startNetwork(this.session),this.playerRuntime.setOwningConnectionHandler((e,t)=>this.setOwningConnection(e,t)),this.playerRuntime.setClearOwningConnectionHandler(e=>this.clearOwningConnection(e)),this.playerRuntime.setGameStateValidator(e=>{if(!this.shouldReplicate(e))throw new Error(`${e.constructor.name} must use @Actor({ replicate: true }) to be a game state`)}),this.mode!==y.none){if(this.isServer)for(const e of this.session.clients)this.sendServerTime(e);n(15).pipe(r(this.destroyed)).subscribe(()=>{this.processMessages()}),this.world.actorAdded.pipe(r(this.destroyed),i(e=>{this.configureMaterializedActorRole(e),this.isServer?this.defineNetId(e):this.processQueuedNetStartupActorSpawns(e),e instanceof M?(T(e,"playerId",this,!0,R.all),T(e,"playerState",this,!0,R.all),T(e,"pawn",this,!0,R.all)):e instanceof U?(T(e,"playerId",this,!0,R.all),T(e,"displayName",this,!0,R.all),T(e,"connected",this,!0,R.all)):e instanceof P&&T(e,"playerStates",this,!0,R.all)}),t(8)).subscribe(e=>{this.shouldReplicate(e)&&(T(e,"owner",this,!0,R.all),this.sendActorSpawn(e))}),this.world.actorRemoved.pipe(r(this.destroyed),t(10)).subscribe(e=>{this.sendActorRemove(e),e instanceof P&&this.playerRuntime.clearGameState(e),this.actorConnection.delete(e.id),this.replicatedActors.delete(e),this.replicatedProperties.delete(e),this.replicatedPropertyRegistry.delete(e),this.actorPreSpawnMessageBuffer.delete(e)}),this.session.playerJoined.pipe(r(this.destroyed)).subscribe(e=>{null==this.activeSceneTravel?(this.sendServerTime(e),this.isServer&&this.replicatedActors.size>0&&this.spawnOnLateClient(e)):this.suspendAndSendSceneTravel(e,this.activeSceneTravel)})}}stop(){this.destroyed.next(null),this.destroyed.complete(),this.playerRuntime.stop(),this.suspendedReplicationConnections.clear(),this.activeSceneTravel=void 0}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 B.rpc:case B.rpcComponent:case B.rpcBinary:case B.rpcComponentBinary:this.processRpc(e.from,e.buffer);break;case B.prop:case B.propComponent:this.processProp(e.from,e.buffer);break;case B.propSnapshot:this.processPropSnapshot(e.from,e.buffer);break;case B.spawn:await this.processSpawn(e.from,e.buffer);break;case B.removeActor:this.processRemoveActor(e.from,e.buffer);break;case B.serverTime:this.processServerTime(e.from,e.buffer);break;case B.actorReady:this.processActorReady(e.from,e.buffer);break;case B.sceneTravelBegin:this.processSceneTravelBegin(e.from,e.buffer);break;case B.sceneTravelReady:this.processSceneTravelReady(e.from,e.buffer);break;case B.sceneTravelComplete:this.processSceneTravelComplete(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 s=new DataView(t),r=s.getUint8(n);n+=1;const i=s.getUint32(n,!0);n+=4;const o=s.getUint8(n);n+=1;const a=r===B.rpcBinary||r===B.rpcComponentBinary;let c,l,d="";if(a){const e=s.getUint32(n,!0);n+=4,l=new Uint8Array(t,n,e),n+=e}else{const e=s.getUint16(n,!0);n+=2,d=j(t,n,e),n+=e;try{c=this.serializer.decode(d,V)}catch(e){return void console.warn("Failed to parse RPC args JSON",d,e)}}if(!a&&V.error){if(null!=V.missingActorId){const n=this.getActorRefMissingMessageQueue(V.missingActorId);console.debug(`Missing referenced actor ${V.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===i);if(null==p){return this.getActorRefMissingMessageQueue(i).push({from:e,buffer:t}),void console.debug(`Missing actor ${i}. Queueing message for later`)}let u;if(r===B.rpcComponent||r===B.rpcComponentBinary){const e=s.getUint8(n),t=p[h];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 ${i}`)}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 f=u??p,g=b(f,o);if(null!=g)if("function"==typeof f[g])if(a)f[g](l);else{if(V.requiresAssetLookups)return this.serializer.decodeAssets(c).then(e=>{f[g](...e)}).catch(e=>{console.warn(`Failed to decode asset references for RPC '${g}' on actor net id ${p.__netid}`,e)});f[g](...c)}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 ${o} 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 s=new DataView(t),r=s.getUint8(n);n+=1;const i=s.getUint32(n,!0);n+=4;const o=s.getUint8(n);n+=1;let a="";for(let e=0;e<o;e++)a+=String.fromCharCode(s.getUint8(n+e));n+=o;const c=s.getUint16(n,!0);n+=2;const l=t.slice(n,n+c);n+=c;const d=this.world.actors.find(e=>e.__netid===i);if(!d){return this.getActorRefMissingMessageQueue(i).push({from:e,buffer:t}),void console.error("Failed to process property replication. No actor found with id",i,a)}let p;if(r===B.propComponent){const e=s.getUint8(n),t=d[h];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 ${i}`)}const u=p??d;try{const n=this.decodeReplicatedValue(l);if(V.error){if(null!=V.missingActorId){let n=this.getActorRefMissingMessageQueue(V.missingActorId);console.debug(`Missing referenced actor ${V.missingActorId}. Queueing message for later`),n.push({from:e,buffer:t})}return}if(V.requiresAssetLookups)return this.serializer.decodeAssets(n).then(e=>{this.applyReplicatedProperty(u,a,e),this.callRepNotify(u,a)}).catch(e=>{console.warn(`Failed to decode asset references for replicated property '${a}' on actor id ${i}`,e)});this.applyReplicatedProperty(u,a,n),this.callRepNotify(u,a)}catch(e){console.warn(`Failed to decode replicated property '${a}' on actor id ${i}`,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 s=new DataView(t);n+=1;const r=s.getUint32(n,!0);n+=4;const i=this.world.actors.find(e=>e.__netid===r);if(null==i){return this.getActorRefMissingMessageQueue(r).push({from:e,buffer:t}),void console.debug(`Missing actor ${r}. Queueing property snapshot for later`)}const o=s.getUint16(n,!0);n+=2;const a=[];for(let c=0;c<o;c++){const o=s.getUint8(n);n+=1;let c=i;if(o===$.component){const e=s.getUint8(n);n+=1;const t=i[h];if(c=t?.find(t=>t.__netid===e),null==c)return void console.error(`Failed to process property snapshot. No component found with netid ${e} on actor with netid ${r}`)}else if(o!==$.actor)return void console.error(`Failed to process property snapshot. Unknown target kind ${o}`);const l=s.getUint8(n);n+=1;let d="";for(let e=0;e<l;e++)d+=String.fromCharCode(s.getUint8(n+e));n+=l;const p=s.getUint32(n,!0);n+=4;const u=t.slice(n,n+p);n+=p;try{const n=this.decodeReplicatedValue(u);if(V.error){if(null!=V.missingActorId){this.getActorRefMissingMessageQueue(V.missingActorId).push({from:e,buffer:t}),console.debug(`Missing referenced actor ${V.missingActorId}. Queueing property snapshot for later`)}else console.warn(`Unknown decode error in property snapshot for actor ${r}`);return}a.push({target:c,property:d,value:n,requiresAssetLookups:V.requiresAssetLookups})}catch(e){return void console.warn(`Failed to decode replicated property snapshot entry '${d}' on actor id ${r}`,e)}}if(a.some(e=>e.requiresAssetLookups))return Promise.all(a.map(async e=>{e.requiresAssetLookups&&(e.value=await this.serializer.decodeAssets(e.value))})).then(()=>{this.applyReplicatedProperties(a)}).catch(e=>{console.warn(`Failed to decode asset references for property snapshot on actor id ${r}`,e)});this.applyReplicatedProperties(a)}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 s=0;s+=1;const r=n.getUint32(s,!0);s+=4;const i=n.getUint8(s);s+=1;let o="";for(let e=0;e<i;e++)o+=String.fromCharCode(n.getUint8(s+e));s+=i,O.set(n.getFloat32(s,!0),n.getFloat32(s+4,!0),n.getFloat32(s+8,!0)),s+=12,Q.set(n.getFloat32(s,!0),n.getFloat32(s+4,!0),n.getFloat32(s+8,!0)),s+=12;const a=n.getUint8(s);s+=1;const c=n.getUint32(s);s+=4;const l=this.readOptionalSceneId(t,s);console.log("Spawning "+o);const d=p(o);if(!d)return void console.error(`Failed to spawn actor: unknown actor type '${o}'`);const u=this.world.actors.find(e=>e.__netid===r);if(null!=u)return this.applySpawnNetInfo(u,r,a,c),u instanceof P&&this.setGameState(u),this.processQueuedActorMessages(r),void this.sendActorReady(u);if(this.spawningActorNetIds.has(r))return void console.log("Actor was already spawned",null!=u);const h=this.findMaterializedNetStartupActor(l,o,r);if(null!=h)return this.applySpawnNetInfo(h,r,a,c),h instanceof P&&this.setGameState(h),this.processQueuedActorMessages(r),void this.sendActorReady(h);if(null==l){this.spawningActorNetIds.add(r);try{const e=await this.world.spawnActorWithNetInfo(d,r,a,c,O,Q);e instanceof P&&this.setGameState(e),this.processQueuedActorMessages(r),this.sendActorReady(e)}catch(e){return void console.error("Spawning replicated actor on client failed",e)}finally{this.spawningActorNetIds.delete(r)}}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:j(e,t,n)}findMaterializedNetStartupActor(e,t,n){if(null!=e)return this.world.actors.find(s=>{if(s.__netSceneId!==e)return!1;if(null!=s.__netid&&s.__netid!==n)return!1;return(s.constructor.__actorId??s.constructor.name)===t})}applySpawnNetInfo(e,t,n,s){if(e.__netid=t,e.netRole=n,0===s)e.owner=void 0;else{const t=this.world.actors.find(e=>e.__netid===s);null!=t&&(e.owner=t)}e instanceof M&&e.setLocallyControlled(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 s=0;n.setUint8(s,B.actorReady),s+=1,n.setUint32(s,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),s=this.world.actors.find(e=>e.__netid===n);if(!s)return void console.warn(`processActorReady: No actor found with net id ${n}`);const r=this.actorPreSpawnMessageBuffer.get(s),i=this.getConnectionKey(e),o=r?.get(i);if(null!=o){this.sendPropertySnapshot(s,e);const t=o.filter(e=>!this.isPropertyMessage(e.buffer));if(t.length>0){console.log(`Sending ${t.length} queued messages for actor ${s.__netid}`);for(const e of t)this.sendMessage(e.connection,e.reliable,e.buffer)}r.delete(i),0===r.size&&this.actorPreSpawnMessageBuffer.delete(s)}}sendRpc(e,t,n,s,r,i){if(this.mode==y.none)return;if(!this.shouldReplicate(n))return;const o=n.__netid;if(null==o)return void console.warn("No __netid defined on actor. Actor must be repliated for RPC");const a=this.resolveActorConnection(n),c=[];if(this.isServer)switch(e){case C.all:c.push(...this.session.clients.filter(e=>this.isRelevant(n,e)));break;case C.client:null!=a&&c.push(a);break;case C.notOwner:c.push(...this.session.clients.filter(e=>e.id!==a?.id&&this.isRelevant(n,e)));case C.server:}else switch(e){case C.all:case C.client:case C.notOwner:break;case C.server:c.push(this.session.server)}if(0==c.length)return;if(null!=i&&null==i.__netid)return void console.warn("No __netid defined on component. Component must be initialized for RPC",i);const l=null!=i,d=i??n,p=A(d,s);if(null==p)return void console.warn(`RPC function '${s}' is not registered on ${d.constructor?.name??"target"}`);const u=1===r.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}(r[0]):null;if(null!=u){let e=10+u.byteLength;l&&(e+=1);const s=new ArrayBuffer(e),r=new DataView(s);let a=0;r.setUint8(a,l?B.rpcComponentBinary:B.rpcBinary),a+=1,r.setUint32(a,o,!0),a+=4,r.setUint8(a,p),a+=1,r.setUint32(a,u.byteLength,!0),a+=4,k(s,a,u),a+=u.byteLength,l&&(r.setUint8(a,i.__netid),a+=1);for(const e of c)null!=e&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:s});return}const h=this.serializer.encode(r),f=N.encode(h);if(f.byteLength>65535)return void console.warn("RPC arguments are too large to send with the JSON RPC format",s);let g=8+f.byteLength;l&&(g+=1);const w=new ArrayBuffer(g),m=new DataView(w);let v=0;m.setUint8(v,l?B.rpcComponent:B.rpc),v+=1,m.setUint32(v,o,!0),v+=4,m.setUint8(v,p),v+=1,m.setUint16(v,f.byteLength,!0),v+=2,k(w,v,f),v+=f.byteLength,l&&(m.setUint8(v,i.__netid),v+=1);for(const e of c)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.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,s,r,i){if(this.mode==y.none)return;if(!this.isServer)return;if(!this.shouldReplicate(n))return;let o=this.replicatedProperties.get(n);null==o&&(o={},this.replicatedProperties.set(n,o));const a=this.serializer.encode(r),c=N.encode(a),l=c.buffer.slice(c.byteOffset,c.byteOffset+c.byteLength);o[this.getReplicatedPropertyKey(s,i)]={property:s,replOn:e,reliable:t,buffer:l,component:i},this.sendProperty(e,t,n,s,l,i)}sendProperty(e,t,n,s,r,i){if(!this.isServer)return;const o=[],a=this.resolveActorConnection(n);switch(e){case R.all:o.push(...this.session.clients);break;case R.owner:null!=a&&o.push(a);break;case R.notOwner:null==a?o.push(...this.session.clients):o.push(...this.session.clients.filter(e=>e.id!==a.id))}const c=null!=i&&null!=i.__netid;let l=6+s.length+2+r.byteLength;c&&(l+=1);const d=new ArrayBuffer(l),p=new DataView(d);let u=0;p.setUint8(u,c?B.propComponent:B.prop),u+=1,p.setUint32(u,n.__netid,!0),u+=4,p.setUint8(u,s.length),u+=1;for(let e=0;e<s.length;e++)p.setUint8(u++,s.charCodeAt(e));p.setUint16(u,r.byteLength,!0),u+=2,new Uint8Array(d,u,r.byteLength).set(new Uint8Array(r)),u+=r.byteLength,c&&(p.setUint8(u,i.__netid),u+=1);for(const e of o)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,s,r,i){if(null==n||null==s||null==r)return;let o=this.replicatedPropertyRegistry.get(n);null==o&&(o=[],this.replicatedPropertyRegistry.set(n,o));const a=i??(r instanceof u?r:void 0),c=o.find(e=>e.target===r&&e.property===s);if(null!=c)return c.replOn=e,c.reliable=t,void(c.component=a);o.push({property:s,replOn:e,reliable:t,target:r,component:a})}sendActorSpawn(e,t=this.session.clients){if(!this.isServer)return;const n=this.replicatedActors.get(e),s=t.filter(t=>!!this.isRelevant(e,t)&&!n?.connections.some(e=>e.id===t.id));if(0===s.length)return;this.defineNetId(e);const r=e.__netid;let i=e.constructor.__actorId??e.constructor.name;const o=N.encode(i);let a;null!=e.__netSceneId&&(a=N.encode(e.__netSceneId),a.byteLength>65535&&(console.warn(`Scene actor id is too long to replicate as a startup actor: ${e.__netSceneId}`),a=void 0));const c=a?.byteLength??0,l=6+o.length+12+12+1+4+2+c,d=new ArrayBuffer(l),p=new DataView(d);let u=0;p.setUint8(u,B.spawn),u+=1,p.setUint32(u,r,!0),u+=4,p.setUint8(u,o.length),u+=1,new Uint8Array(d).set(o,u),u+=o.length,p.setFloat32(u,e.position.x,!0),u+=4,p.setFloat32(u,e.position.y,!0),u+=4,p.setFloat32(u,e.position.z,!0),u+=4,p.setFloat32(u,e.rotation.x,!0),u+=4,p.setFloat32(u,e.rotation.y,!0),u+=4,p.setFloat32(u,e.rotation.z,!0),u+=4;let h=u;u+=1,p.setUint32(u,e.owner?.__netid??0),u+=4,p.setUint16(u,c,!0),u+=2,null!=a&&(k(d,u,a),u+=a.byteLength);let f=this.replicatedActors.get(e);null==f&&(f={spawnedAt:Date.now(),connections:[]},this.replicatedActors.set(e,f));for(const t of s){const n=d.slice(0),s=new DataView(n),r=this.resolveActorConnection(e)?.id===t.id?w.autonomousProxy:w.simulatedProxy;s.setUint8(h,r),this.getPreSpawnMessageQueue(e,t),this.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 s=0;n.setUint8(s,B.removeActor),s+=1,n.setUint32(s,e.__netid,!0);for(const e of this.session.clients)this.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),s=this.world.actors.find(e=>e.__netid===n);s&&(this.world.removeActor(s),this.replicatedActors?.delete?.(s),this.replicatedProperties?.delete?.(s))}setOwningConnection(e,t){this.actorConnection.set(e.id,t),this.isServer&&this.world.actors.includes(e)&&this.shouldReplicate(e)&&!0===this.getRelevancyOptions(e)?.ownerOnly&&this.sendActorSpawn(e,[t])}clearOwningConnection(e){this.actorConnection.delete(e.id)}getOwningConnection(e){return this.actorConnection.get(e.id)}getActorsByOwningConnection(e){const t=[];return this.actorConnection.forEach((n,s)=>{if(n.id===e.id){const e=this.world.actors.find(e=>e.id===s);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 _)return t.isLocallyControlled;t=t.owner}return!1}isLocalOwner(e){return this.isLocallyControlled(e)}beginSceneTravel(e,t){if(!this.isServer)throw new Error("Only authority can begin scene travel");const n={travelId:e,sceneName:t};this.activeSceneTravel=n;for(const e of this.session.clients)this.suspendAndSendSceneTravel(e,n)}sendSceneTravelReady(e,t){if(!this.isClient||null==this.session.server)return;const n=this.createSceneTravelMessage(B.sceneTravelReady,{travelId:e,sceneName:t});this.session.sendMessage(this.session.server,!0,n)}completeSceneTravel(e,t){if(!this.isServer)throw new Error("Only authority can complete scene travel");const n=this.createSceneTravelMessage(B.sceneTravelComplete,{travelId:e,sceneName:t});for(const e of this.session.clients)this.resetReplicationState(e),this.suspendedReplicationConnections.delete(this.getConnectionKey(e)),this.spawnOnLateClient(e),this.session.sendMessage(e,!0,n);this.activeSceneTravel=void 0}suspendAndSendSceneTravel(e,t){this.suspendedReplicationConnections.add(this.getConnectionKey(e));const n=this.createSceneTravelMessage(B.sceneTravelBegin,t);this.session.sendMessage(e,!0,n)}processSceneTravelBegin(e,t){if(!this.isClient||this.session.server?.id!==e.id)return;const n=this.parseSceneTravelMessage(t);this.activeSceneTravel=n,this.sceneTravelBeginSubject.next(n)}processSceneTravelReady(e,t){if(!this.isServer||!this.session.clients.some(t=>t.id===e.id))return;const n=this.parseSceneTravelMessage(t);this.sceneTravelReadySubject.next({travelId:n.travelId,connection:e})}processSceneTravelComplete(e,t){if(!this.isClient||this.session.server?.id!==e.id)return;const n=this.parseSceneTravelMessage(t);this.activeSceneTravel=void 0,this.sceneTravelCompleteSubject.next(n)}createSceneTravelMessage(e,t){const n=N.encode(JSON.stringify(t)),s=new ArrayBuffer(1+n.byteLength);return new DataView(s).setUint8(0,e),k(s,1,n),s}parseSceneTravelMessage(e){const t=e.slice(1),n=null!=D?D.decode(t):String.fromCharCode(...new Uint8Array(t));return JSON.parse(n)}sendMessage(e,t,n){this.isServer&&this.suspendedReplicationConnections.has(this.getConnectionKey(e))||this.session.sendMessage(e,t,n)}sendServerTime(e){const t=new ArrayBuffer(9),n=new DataView(t);n.setUint8(0,B.serverTime),n.setBigUint64(1,BigInt(Date.now()),!0),this.sendMessage(e,!0,t)}parseServerTime(e){const t=new DataView(e).getBigUint64(1,!0);return Number(t)}getConnectionKey(e){return e.id.toString()}resetReplicationState(e){const t=this.getConnectionKey(e);for(const[n,s]of this.replicatedActors){for(let t=s.connections.length-1;t>=0;t--)s.connections[t].id===e.id&&s.connections.splice(t,1);const r=this.actorPreSpawnMessageBuffer.get(n);r?.delete(t),0===r?.size&&this.actorPreSpawnMessageBuffer.delete(n)}}getPreSpawnMessageQueue(e,t){let n=this.actorPreSpawnMessageBuffer.get(e);null==n&&(n=new Map,this.actorPreSpawnMessageBuffer.set(e,n));const s=this.getConnectionKey(t);let r=n.get(s);return null==r&&(r=[],n.set(s,r)),r}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===B.prop||t===B.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 s=this.createPropertySnapshotBuffer(e,n);this.sendMessage(t,!0,s)}buildPropertySnapshotEntries(e,t){if(!this.isRelevant(e,t))return[];const n=this.replicatedPropertyRegistry.get(e);if(null==n)return[];const s=[];for(const r of n){if(!this.shouldReplicatePropertyToConnection(e,r.replOn,t))continue;if(null!=r.component&&null==r.component.__netid)continue;const n=this.readReplicatedPropertyValue(r),i=this.serializer.encode(n),o=N.encode(i),a=o.buffer.slice(o.byteOffset,o.byteOffset+o.byteLength);s.push({property:r.property,component:r.component,buffer:a})}return s}shouldReplicatePropertyToConnection(e,t,n){const s=this.resolveActorConnection(e);switch(t){case R.all:return!0;case R.owner:return s?.id===n.id;case R.notOwner:return null==s||s.id!==n.id}}readReplicatedPropertyValue(e){const t=e.target[e.property];return S(t)?t.value:t}createPropertySnapshotBuffer(e,t){let n=7;const s=t.map(e=>N.encode(e.property));for(let e=0;e<t.length;e++){const r=t[e];n+=1,null!=r.component&&(n+=1),n+=1+s[e].byteLength+4+r.buffer.byteLength}const r=new ArrayBuffer(n),i=new DataView(r);let o=0;i.setUint8(o,B.propSnapshot),o+=1,i.setUint32(o,e.__netid,!0),o+=4,i.setUint16(o,t.length,!0),o+=2;for(let e=0;e<t.length;e++){const n=t[e],a=s[e],c=null!=n.component;i.setUint8(o,c?$.component:$.actor),o+=1,c&&(i.setUint8(o,n.component.__netid),o+=1),i.setUint8(o,a.byteLength),o+=1,new Uint8Array(r,o,a.byteLength).set(a),o+=a.byteLength,i.setUint32(o,n.buffer.byteLength,!0),o+=4,new Uint8Array(r,o,n.buffer.byteLength).set(new Uint8Array(n.buffer)),o+=n.buffer.byteLength}return r}decodeReplicatedValue(e){let t;if(null!=D)t=D.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,V)}applyReplicatedProperty(e,t,n){S(e[t])?e[t].value=n:e[t]=n,this.synchronizeReplicatedPlayerActor(e)}synchronizeReplicatedPlayerActor(e){if(e instanceof M&&null!=e.playerId){const t=this.players.get(e.playerId);if(null==t)return;return this.players.attachController(e.playerId,e),void e.setLocallyControlled(null!=t.localPlayerIndex)}if(e instanceof U&&null!=e.playerId){null!=this.players.get(e.playerId)&&this.players.attachPlayerState(e.playerId,e)}}callRepNotify(e,t){const n=e[`onRep_${t}`];"function"==typeof n&&n.call(e)}};L=e([c()],L);export{L as NetService};const O=new a,Q=new o,N=new TextEncoder,D="undefined"!=typeof TextDecoder?new TextDecoder:void 0;function j(e,t,n){const s=new Uint8Array(e,t,n);if(null!=D)return D.decode(s);let r="";for(let e=0;e<s.length;e++)r+=String.fromCharCode(s[e]);return r}function k(e,t,n){new Uint8Array(e,t,n.byteLength).set(n)}const V=new m;/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,4 +1,4 @@
1
- import{__decorate as e}from"tslib";import{Service as t}from"typedi";import{randomUUID as s}from"../utils/uuid.js";import{BasePlayerController as n}from"./actors/controller/actor-controller.js";import{inject as i}from"./inject.js";import{NetMode as o}from"./net/net-session.js";import{NetService as r}from"./net/service/net-service.js";import{SceneTransitionService as a}from"./services/scene-transition.js";import{World as l}from"./services/world.js";let c=class{constructor(){this.net=i(r),this.scenes=i(a),this.world=i(l),this.subscriptions=[]}start(e){if(this.stop(),this.gameMode=e,this.subscriptions.push(this.net.sceneTravelBegin$.subscribe(e=>{this.clientTravel=this.handleClientTravel(e).catch(t=>{console.error(`Failed to load scene ${JSON.stringify(e.sceneName)} for network travel`,t)})}),this.net.sceneTravelReady$.subscribe(e=>this.markClientReady(e.travelId,e.connection.id.toString())),this.net.sceneTravelComplete$.subscribe(e=>this.handleClientTravelComplete(e))),null!=this.net.session&&this.subscriptions.push(this.net.session.playerLeft.subscribe(e=>this.markClientReady(this.pending?.travelId,e.id.toString())),this.net.session.playerJoined.subscribe(e=>this.addTravellingClient(e.id.toString()))),this.net.isClient&&null!=this.net.pendingSceneTravel){const e=this.net.pendingSceneTravel;this.clientTravel=this.handleClientTravel(e).catch(t=>{console.error(`Failed to load scene ${JSON.stringify(e.sceneName)} for network travel`,t)})}}stop(){for(const e of this.subscriptions)e.unsubscribe();this.subscriptions.length=0,this.gameMode=void 0,this.pending=void 0}async travelAll(e){if(!this.net.isServer)throw new Error("Only authority can initiate full-session scene travel");if(null!=this.pending)throw new Error("A network scene transition is already in progress");const t=this.requireGameMode(),n=e.trim();if(0===n.length)throw new Error("A destination scene name is required");const i=s();for(const e of this.net.players.values)"disconnected"!==e.status&&(e.status="travelling");if(await t.onTravelStarted(n),this.net.mode!==o.none){const e=this.waitForClients(i,n);this.net.beginSceneTravel(i,n),await e}this.releaseControlledActors(),await this.scenes.transitionTo(n),await this.waitForPendingClients(),await this.restartPlayers(t),this.net.mode!==o.none&&this.net.completeSceneTravel(i,n),this.pending=void 0,await t.onTravelCompleted(n)}waitForClients(e,t){const s=new Set(this.net.session?.clients.map(e=>e.id.toString())??[]);return this.pending={travelId:e,sceneName:t,expectedConnections:s,readyConnections:new Set},0===s.size?Promise.resolve():new Promise(e=>{this.pending.resolve=e})}waitForPendingClients(){const e=this.pending;return null==e||e.readyConnections.size===e.expectedConnections.size?Promise.resolve():new Promise(t=>{e.resolve=t})}markClientReady(e,t){const s=this.pending;if(null!=s&&e===s.travelId&&s.expectedConnections.has(t)&&(s.readyConnections.add(t),s.readyConnections.size===s.expectedConnections.size)){const e=s.resolve;s.resolve=void 0,e?.()}}addTravellingClient(e){this.pending?.expectedConnections.add(e)}async handleClientTravel(e){if(this.net.isClient){for(const e of this.net.players.values)e.status="travelling";await(this.gameMode?.onTravelStarted(e.sceneName)),this.releaseControlledActors(),await this.scenes.transitionTo(e.sceneName),this.net.sendSceneTravelReady(e.travelId,e.sceneName)}}handleClientTravelComplete(e){if(this.net.isClient){for(const e of this.net.players.values)"disconnected"!==e.status&&(e.status="active");this.gameMode?.onTravelCompleted(e.sceneName)}}releaseControlledActors(){for(const e of[...this.world.actors]){if(!(e instanceof n)||null==e.pawn)continue;const t=e.pawn;e.unpossess(),this.world.actors.includes(t)&&this.world.removeActor(t)}}async restartPlayers(e){for(const t of this.net.players.values){if("disconnected"===t.status||null==t.controller)continue;const s=await e.spawnDefaultPawn(t,t.controller);null!=s&&t.controller.possess(s),t.status="active"}}requireGameMode(){if(null==this.gameMode)throw new Error("NetworkTravelCoordinator must be started with a GameMode");return this.gameMode}};c=e([t()],c);export{c as NetworkTravelCoordinator};/*
1
+ import{__decorate as e}from"tslib";import{Service as t}from"typedi";import{randomUUID as s}from"../utils/uuid.js";import{BasePlayerController as n}from"./actors/controller/actor-controller.js";import{inject as i}from"./inject.js";import{NetMode as o}from"./net/net-session.js";import{NetService as r}from"./net/service/net-service.js";import{SceneTransitionService as a}from"./services/scene-transition.js";import{World as l}from"./services/world.js";let c=class{constructor(){this.net=i(r),this.scenes=i(a),this.world=i(l),this.subscriptions=[]}start(e){if(this.stop(),this.gameMode=e,this.subscriptions.push(this.net.sceneTravelBegin$.subscribe(e=>{this.clientTravel=this.handleClientTravel(e).catch(t=>{console.error(`Failed to load scene ${JSON.stringify(e.sceneName)} for network travel`,t)})}),this.net.sceneTravelReady$.subscribe(e=>this.markClientReady(e.travelId,e.connection.id.toString())),this.net.sceneTravelComplete$.subscribe(e=>this.handleClientTravelComplete(e))),null!=this.net.session&&this.subscriptions.push(this.net.session.playerLeft.subscribe(e=>this.markClientReady(this.pending?.travelId,e.id.toString())),this.net.session.playerJoined.subscribe(e=>this.addTravellingClient(e.id.toString()))),this.net.isClient&&null!=this.net.pendingSceneTravel){const e=this.net.pendingSceneTravel;this.clientTravel=this.handleClientTravel(e).catch(t=>{console.error(`Failed to load scene ${JSON.stringify(e.sceneName)} for network travel`,t)})}}stop(){for(const e of this.subscriptions)e.unsubscribe();this.subscriptions.length=0,this.gameMode=void 0,this.pending=void 0}async travelAll(e){if(!this.net.isServer)throw new Error("Only authority can initiate full-session scene travel");if(null!=this.pending)throw new Error("A network scene transition is already in progress");const t=this.requireGameMode(),n=e.trim();if(0===n.length)throw new Error("A destination scene name is required");const i=s();for(const e of this.net.players.values)"disconnected"!==e.status&&this.net.players.setStatus(e.info.id,"travelling");if(await t.onTravelStarted(n),this.net.mode!==o.none){const e=this.waitForClients(i,n);this.net.beginSceneTravel(i,n),await e}this.releaseControlledActors(),await this.scenes.transitionTo(n),await this.waitForPendingClients(),await this.restartPlayers(t),this.net.mode!==o.none&&this.net.completeSceneTravel(i,n),this.pending=void 0,await t.onTravelCompleted(n)}waitForClients(e,t){const s=new Set(this.net.session?.clients.map(e=>e.id.toString())??[]);return this.pending={travelId:e,sceneName:t,expectedConnections:s,readyConnections:new Set},0===s.size?Promise.resolve():new Promise(e=>{this.pending.resolve=e})}waitForPendingClients(){const e=this.pending;return null==e||e.readyConnections.size===e.expectedConnections.size?Promise.resolve():new Promise(t=>{e.resolve=t})}markClientReady(e,t){const s=this.pending;if(null!=s&&e===s.travelId&&s.expectedConnections.has(t)&&(s.readyConnections.add(t),s.readyConnections.size===s.expectedConnections.size)){const e=s.resolve;s.resolve=void 0,e?.()}}addTravellingClient(e){this.pending?.expectedConnections.add(e)}async handleClientTravel(e){if(this.net.isClient){for(const e of this.net.players.values)this.net.players.setStatus(e.info.id,"travelling");await(this.gameMode?.onTravelStarted(e.sceneName)),this.releaseControlledActors(),await this.scenes.transitionTo(e.sceneName),this.net.sendSceneTravelReady(e.travelId,e.sceneName)}}handleClientTravelComplete(e){if(this.net.isClient){for(const e of this.net.players.values)"disconnected"!==e.status&&this.net.players.setStatus(e.info.id,"active");this.gameMode?.onTravelCompleted(e.sceneName)}}releaseControlledActors(){for(const e of[...this.world.actors]){if(!(e instanceof n)||null==e.pawn)continue;const t=e.pawn;e.unpossess(),this.world.actors.includes(t)&&this.world.removeActor(t)}}async restartPlayers(e){for(const t of this.net.players.values){if("disconnected"===t.status||null==t.controller)continue;const s=await e.spawnDefaultPawn(t,t.controller);null!=s&&t.controller.possess(s),this.net.players.setStatus(t.info.id,"active")}}requireGameMode(){if(null==this.gameMode)throw new Error("NetworkTravelCoordinator must be started with a GameMode");return this.gameMode}};c=e([t()],c);export{c as NetworkTravelCoordinator};/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,24 +1,41 @@
1
1
  import type { BaseActor } from './actors/actor.js';
2
+ import type { BasePlayerController, BasePlayerState } from './actors/controller/actor-controller.js';
2
3
  import type { BaseGameState } from './actors/controller/game-state.js';
3
4
  import type { NetConnection, NetPlayerInfo } from './net/net-connection.js';
4
5
  import { type NetSession } from './net/net-session.js';
5
- import { NetPlayerRegistry } from './net/player-session.js';
6
+ import { NetPlayerRegistry, type PlayerSession } from './net/player-session.js';
6
7
  /** Lightweight player/lifecycle state shared by standalone and network games. */
7
8
  export declare class PlayerRuntime {
8
9
  readonly players: NetPlayerRegistry;
9
- gameState?: BaseGameState;
10
+ private currentGameState?;
11
+ private readonly revisionSubject;
12
+ /** Emits after any player association/status or game-state identity change. */
13
+ readonly changed$: import("rxjs").Observable<number>;
10
14
  isAuthority: boolean;
11
15
  private clearOwningConnectionHandler?;
12
16
  private owningConnectionHandler?;
13
17
  private validateGameStateHandler?;
18
+ constructor();
19
+ get revision(): number;
20
+ get gameState(): BaseGameState | undefined;
21
+ getGameState<T extends BaseGameState = BaseGameState>(): T | undefined;
22
+ getLocalPlayer(localPlayerIndex?: number): PlayerSession | undefined;
23
+ getLocalController<T extends BasePlayerController = BasePlayerController>(localPlayerIndex?: number): T | undefined;
24
+ getLocalPlayerState<T extends BasePlayerState = BasePlayerState>(localPlayerIndex?: number): T | undefined;
25
+ requireGameState<T extends BaseGameState = BaseGameState>(): T;
26
+ requireLocalPlayer(localPlayerIndex?: number): PlayerSession;
27
+ requireLocalController<T extends BasePlayerController = BasePlayerController>(localPlayerIndex?: number): T;
28
+ requireLocalPlayerState<T extends BasePlayerState = BasePlayerState>(localPlayerIndex?: number): T;
14
29
  startStandalone(localPlayer: NetPlayerInfo): void;
15
30
  startNetwork(session: NetSession): void;
16
31
  setClearOwningConnectionHandler(handler?: (actor: BaseActor) => void): void;
17
32
  setOwningConnectionHandler(handler?: (actor: BaseActor, connection: NetConnection) => void): void;
18
33
  setOwningConnection(actor: BaseActor, connection: NetConnection): void;
19
34
  setGameState(gameState: BaseGameState): void;
35
+ clearGameState(gameState?: BaseGameState): void;
20
36
  setGameStateValidator(handler?: (gameState: BaseGameState) => void): void;
21
37
  clearOwningConnection(actor: BaseActor): void;
22
38
  stop(): void;
39
+ private notifyChanged;
23
40
  }
24
41
  //# sourceMappingURL=player-runtime.d.ts.map
@@ -1,4 +1,4 @@
1
- import{__decorate as t}from"tslib";import{Service as n}from"typedi";import{NetMode as e}from"./net/net-session.js";import{NetPlayerRegistry as i}from"./net/player-session.js";let o=class{constructor(){this.players=new i,this.isAuthority=!0}startStandalone(t){this.stop(),this.isAuthority=!0,this.players.startLocal(t)}startNetwork(t){this.stop(),this.isAuthority=t.mode<e.client,this.players.start(t)}setClearOwningConnectionHandler(t){this.clearOwningConnectionHandler=t}setOwningConnectionHandler(t){this.owningConnectionHandler=t}setOwningConnection(t,n){this.owningConnectionHandler?.(t,n)}setGameState(t){this.validateGameStateHandler?.(t),this.gameState=t}setGameStateValidator(t){this.validateGameStateHandler=t}clearOwningConnection(t){this.clearOwningConnectionHandler?.(t)}stop(){this.players.stop(),this.gameState=void 0,this.clearOwningConnectionHandler=void 0,this.owningConnectionHandler=void 0,this.validateGameStateHandler=void 0}};o=t([n()],o);export{o as PlayerRuntime};/*
1
+ import{__decorate as t,__metadata as e}from"tslib";import{Service as r}from"typedi";import{NetMode as a}from"./net/net-session.js";import{NetPlayerRegistry as n}from"./net/player-session.js";import{BehaviorSubject as i}from"rxjs";let o=class{constructor(){this.players=new n,this.revisionSubject=new i(0),this.changed$=this.revisionSubject.asObservable(),this.isAuthority=!0,this.players.changed$.subscribe(()=>this.notifyChanged())}get revision(){return this.revisionSubject.value}get gameState(){return this.currentGameState}getGameState(){return this.currentGameState}getLocalPlayer(t=0){return this.players.values.find(e=>e.localPlayerIndex===t)}getLocalController(t=0){return this.getLocalPlayer(t)?.controller}getLocalPlayerState(t=0){return this.getLocalPlayer(t)?.playerState}requireGameState(){const t=this.getGameState();if(null==t)throw new Error("Game state is not available");return t}requireLocalPlayer(t=0){const e=this.getLocalPlayer(t);if(null==e)throw new Error(`Local player ${t} is not available`);return e}requireLocalController(t=0){const e=this.getLocalController(t);if(null==e)throw new Error(`Controller for local player ${t} is not available`);return e}requireLocalPlayerState(t=0){const e=this.getLocalPlayerState(t);if(null==e)throw new Error(`Player state for local player ${t} is not available`);return e}startStandalone(t){this.stop(),this.isAuthority=!0,this.players.startLocal(t)}startNetwork(t){this.stop(),this.isAuthority=t.mode<a.client,this.players.start(t)}setClearOwningConnectionHandler(t){this.clearOwningConnectionHandler=t}setOwningConnectionHandler(t){this.owningConnectionHandler=t}setOwningConnection(t,e){this.owningConnectionHandler?.(t,e)}setGameState(t){this.validateGameStateHandler?.(t),this.currentGameState!==t&&(this.currentGameState=t,this.notifyChanged())}clearGameState(t){null==this.currentGameState||null!=t&&this.currentGameState!==t||(this.currentGameState=void 0,this.notifyChanged())}setGameStateValidator(t){this.validateGameStateHandler=t}clearOwningConnection(t){this.clearOwningConnectionHandler?.(t)}stop(){this.players.stop(),this.clearGameState(),this.clearOwningConnectionHandler=void 0,this.owningConnectionHandler=void 0,this.validateGameStateHandler=void 0}notifyChanged(){this.revisionSubject.next(this.revisionSubject.value+1)}};o=t([r(),e("design:paramtypes",[])],o);export{o as PlayerRuntime};/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=player-runtime.test.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{describe as e,expect as t,it as a}from"vitest";import{PlayerRuntime as o}from"../gameplay/player-runtime.js";e("PlayerRuntime",()=>{a("resolves local gameplay state and emits association changes",()=>{const e=new o,a=[],l=e.changed$.subscribe(e=>a.push(e)),r={},c={},s={};e.startStandalone({id:"local"}),e.setGameState(r),e.players.attachPlayerState("local",s),e.players.attachController("local",c),t(e.getGameState()).toBe(r),t(e.getLocalPlayer()?.info.id).toBe("local"),t(e.getLocalController()).toBe(c),t(e.getLocalPlayerState()).toBe(s),t(e.requireGameState()).toBe(r),t(e.requireLocalPlayer().controller).toBe(c),t(e.requireLocalController()).toBe(c),t(e.requireLocalPlayerState()).toBe(s),t(a.at(-1)).toBe(e.revision),t(e.revision).toBeGreaterThan(0),l.unsubscribe()}),a("clears only the active game state",()=>{const e=new o,a={};e.setGameState(a),e.clearGameState({}),t(e.gameState).toBe(a),e.clearGameState(a),t(e.gameState).toBeUndefined()})});/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hology/core",
3
- "version": "0.0.254",
3
+ "version": "0.0.255",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",