@hology/core 0.0.260 → 0.0.261
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gameplay/actors/actor.d.ts +4 -0
- package/dist/gameplay/actors/actor.js +1 -1
- package/dist/gameplay/actors/builtin/components/actor-transform-persistence.d.ts +29 -0
- package/dist/gameplay/actors/builtin/components/actor-transform-persistence.js +4 -0
- package/dist/gameplay/ai/navigation.js +1 -1
- package/dist/gameplay/index.d.ts +2 -0
- package/dist/gameplay/index.js +1 -1
- package/dist/gameplay/initiate.d.ts +4 -0
- package/dist/gameplay/initiate.js +1 -1
- package/dist/gameplay/net/service/net-decorator.d.ts +9 -5
- package/dist/gameplay/net/service/net-decorator.js +1 -1
- package/dist/gameplay/net/service/net-service.js +1 -1
- package/dist/gameplay/services/actor-persistence.d.ts +69 -0
- package/dist/gameplay/services/actor-persistence.js +4 -0
- package/dist/gameplay/services/render.d.ts +1 -1
- package/dist/gameplay/services/scene-transition.js +1 -1
- package/dist/gameplay/services/world.d.ts +10 -1
- package/dist/gameplay/services/world.js +1 -1
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering.js +1 -1
- package/dist/scene/bootstrap.d.ts +2 -0
- package/dist/scene/bootstrap.js +1 -1
- package/dist/scene/materializer.d.ts +10 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/shader-nodes/pom.js +1 -1
- package/dist/test/actor-persistence-runtime.test.d.ts +2 -0
- package/dist/test/actor-persistence-runtime.test.js +4 -0
- package/dist/test/actor-persistence.test.d.ts +2 -0
- package/dist/test/actor-persistence.test.js +4 -0
- package/dist/test/actor-transform-persistence.test.d.ts +2 -0
- package/dist/test/actor-transform-persistence.test.js +4 -0
- package/dist/test/materializer-prefetch.test.js +1 -1
- package/dist/test/prefab-instance-params.test.js +1 -1
- package/dist/test/world-lifecycle.test.js +1 -1
- package/dist/test/world-prefab-spawn.test.js +1 -1
- package/docs/actor-persistence.md +87 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -12,6 +12,8 @@ export declare enum ActorLifetime {
|
|
|
12
12
|
game = "game"
|
|
13
13
|
}
|
|
14
14
|
export type ActorOptions = {
|
|
15
|
+
/** Opt this class into persistence when an authored or explicit spawn identity exists. */
|
|
16
|
+
persistent?: boolean;
|
|
15
17
|
/** Stable serialized identity. Defaults to the class name for backwards compatibility. */
|
|
16
18
|
typeId?: string;
|
|
17
19
|
/** Tabler icon name used by editor UI, for example `flag` or `camera`. */
|
|
@@ -33,6 +35,8 @@ export type ActorId = number;
|
|
|
33
35
|
* An actor needs to inherit from the Base actor class to be able to be added to a scene.
|
|
34
36
|
*/
|
|
35
37
|
export declare abstract class BaseActor {
|
|
38
|
+
/** Evaluated after authored parameters and initialization. Override for per-instance policy. */
|
|
39
|
+
get persistenceEnabled(): boolean;
|
|
36
40
|
readonly id: ActorId;
|
|
37
41
|
/**
|
|
38
42
|
* The net role is used for networked games to understand what
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t,__metadata as e}from"tslib";import o,{ignore as i}from"@plumier/reflect";import{Subject as r,skipWhile as n}from"rxjs";import{Group as s}from"three";import a,{Service as p}from"typedi";import{ActorComponent as c}from"./component.js";import{initComponentsSync as l}from"./internal/component-init.js";import{activeContainerInstance as m,containerRefMap as d}from"./internal/container-map.js";import{randomString as
|
|
1
|
+
import{__decorate as t,__metadata as e}from"tslib";import o,{ignore as i}from"@plumier/reflect";import{Subject as r,skipWhile as n}from"rxjs";import{Group as s}from"three";import a,{Service as p}from"typedi";import{ActorComponent as c}from"./component.js";import{initComponentsSync as l}from"./internal/component-init.js";import{activeContainerInstance as m,containerRefMap as d}from"./internal/container-map.js";import{randomString as u}from"../../utils/math.js";import{inject as h}from"../inject.js";import{ViewController as f}from"../services/render.js";import{actorConstructContext as y}from"./factory.js";import{NetRole as g}from"../net/service/net-actor-role.js";import{registerActorClass as j}from"./type-registry.js";export{getActorClassById,getActorClassId,getRegisteredActorClasses}from"./type-registry.js";export const $actorOptions=Symbol("actor-options");export var ActorLifetime;!function(t){t.scene="scene",t.game="game"}(ActorLifetime||(ActorLifetime={}));export function getActorOptions(t){return t[$actorOptions]??b}const b={lifetime:ActorLifetime.scene,replicate:!1};export function Actor(t={}){const e=p({transient:!0});return function(i){const r=arguments[1],n=t.typeId??r?.name??i.name,s=j(n,i);null!=s&&s!==i&&console.warn(`Duplicate actor class name ${n}`),i.__actorId=n,i.__isActor=!0,e(i),o.noop()(i),o.parameterProperties()(i);const a=getActorOptions(Object.getPrototypeOf(i)),{typeId:p,...c}=a,l=null==c.relevancy&&null==t.relevancy?void 0:{...c.relevancy,...t.relevancy};i[$actorOptions]={...c,...t,...null==l?{}:{relevancy:l}}}}let A=0;export class BaseActor{get persistenceEnabled(){return!0===getActorOptions(this.constructor).persistent}get position(){return this.object?.position}get quaternion(){return this.object?.quaternion}get rotation(){return this.object?.rotation}onInit(){}onBeginPlay(){}onEndPlay(){}applySceneParameters(t,e){return!1}onUpdate(t){}onLateUpdate(t){}constructor(){this.id=++A,this.netRole=g.authority,this.__isInitialised=!1,this.object=new s,this.disposed=new r,this.attachedComponents=[],y.actor=this,this.onUpdate!==BaseActor.prototype.onUpdate&&h(f).onUpdate(this).pipe(n(()=>!this.__isInitialised)).subscribe(t=>this.onUpdate(t)),this.onLateUpdate!==BaseActor.prototype.onLateUpdate&&h(f).onLateUpdate(this).pipe(n(()=>!this.__isInitialised)).subscribe(t=>this.onLateUpdate(t))}attach(t,e){const o=m.value,i=d.get(this)??m.value??a.of("default"),r=u(),n=y.actor;let s;try{m.value=i,y.actor=this,i.set({id:r,type:t,transient:!0}),s=i.get(r)}finally{y.actor=n,m.value=o}if(s.actor=this,null!=e)for(const t of Object.keys(e))s[t]=e[t];return this.__isInitialised&&(s.onInit(),l(s,this)),this.attachedComponents.push(s),s}getComponent(t){for(const e of Object.values(this))if(e instanceof t)return e;for(const e of this.attachedComponents)if(e instanceof t)return e}}t([i(),e("design:type",Object),e("design:paramtypes",[])],BaseActor.prototype,"position",null),t([i(),e("design:type",Object),e("design:paramtypes",[])],BaseActor.prototype,"quaternion",null),t([i(),e("design:type",Object),e("design:paramtypes",[])],BaseActor.prototype,"rotation",null);export function _setupActorUpdateEventHandlers(){this.onUpdate!==c.prototype.onUpdate&&h(f).onUpdate(this.actor).pipe(n(()=>!this.actor?.__isInitialised)).subscribe(t=>this.onUpdate(t)),this.onLateUpdate!==c.prototype.onLateUpdate&&h(f).onLateUpdate(this.actor).pipe(n(()=>!this.actor?.__isInitialised)).subscribe(t=>this.onLateUpdate(t))}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ActorComponent } from '../../component.js';
|
|
2
|
+
import type { BaseActor } from '../../actor.js';
|
|
3
|
+
import type { PersistentState, PersistenceContext, PersistenceValue } from '../../../services/actor-persistence.js';
|
|
4
|
+
export type ActorTransformState = {
|
|
5
|
+
position: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
z: number;
|
|
9
|
+
};
|
|
10
|
+
yaw: number;
|
|
11
|
+
};
|
|
12
|
+
/** Capture the actor's position and yaw relative to its parent. */
|
|
13
|
+
export declare function captureActorTransform(actor: BaseActor): ActorTransformState;
|
|
14
|
+
/** Apply a saved local transform without emitting gameplay events. */
|
|
15
|
+
export declare function restoreActorTransform(actor: BaseActor, state: PersistenceValue): void;
|
|
16
|
+
/** Attach to a persistent actor to save its position and yaw independently of actor state. */
|
|
17
|
+
export declare class ActorTransformPersistenceComponent extends ActorComponent implements PersistentState<ActorTransformState> {
|
|
18
|
+
readonly persistenceKey = "transform";
|
|
19
|
+
readonly persistenceVersion = 1;
|
|
20
|
+
/** True only when a saved transform was applied during this restore. */
|
|
21
|
+
restored: boolean;
|
|
22
|
+
private readonly physics;
|
|
23
|
+
private readonly worldPosition;
|
|
24
|
+
private readonly worldRotation;
|
|
25
|
+
onCapturePersistence(): ActorTransformState;
|
|
26
|
+
onRestorePersistence(state: ActorTransformState, context: PersistenceContext): void;
|
|
27
|
+
onPersistenceRestored(): void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=actor-transform-persistence.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{__decorate as t}from"tslib";import{Vector3 as o,Quaternion as r}from"three";import{ActorComponent as s,Component as i}from"../../component.js";import{inject as e}from"../../../inject.js";import{PhysicsSystem as n}from"../../../services/physics/physics-system.js";export function captureActorTransform(t){const{x:o,y:r,z:s}=t.position;return{position:{x:o,y:r,z:s},yaw:t.rotation.y}}export function restoreActorTransform(t,o){if(null==o||"object"!=typeof o||Array.isArray(o))throw new Error("Invalid actor transform");const r=o.position,s=o.yaw;if(null==r||"object"!=typeof r||Array.isArray(r)||!c(r.x)||!c(r.y)||!c(r.z)||!c(s))throw new Error("Invalid actor transform");t.position.set(r.x,r.y,r.z),t.rotation.y=s}function c(t){return"number"==typeof t&&Number.isFinite(t)}let a=class extends s{constructor(){super(...arguments),this.persistenceKey="transform",this.persistenceVersion=1,this.restored=!1,this.physics=e(n),this.worldPosition=new o,this.worldRotation=new r}onCapturePersistence(){return captureActorTransform(this.actor)}onRestorePersistence(t,o){if(1!==o.version)throw new Error(`Unsupported transform version ${o.version}`);restoreActorTransform(this.actor,t),this.restored=!0}onPersistenceRestored(){this.restored&&(this.actor.object.updateWorldMatrix(!0,!1),this.actor.object.getWorldPosition(this.worldPosition),this.actor.object.getWorldQuaternion(this.worldRotation),this.physics.setPosition(this.actor,this.worldPosition),this.physics.setRotation(this.actor,this.worldRotation),this.physics.flushModifiedBodyPositionsToColliders())}};a=t([i()],a);export{a as ActorTransformPersistenceComponent};/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as e,__metadata as t}from"tslib";import{NavMeshQuery as n,QueryFilter as s}from"recast-navigation";import{Vector3 as r}from"three";import{
|
|
1
|
+
import{__decorate as e,__metadata as t}from"tslib";import{NavMeshQuery as n,QueryFilter as s}from"recast-navigation";import{Vector3 as r}from"three";import{Service as i}from"typedi";import{inject as o}from"../inject.js";import{World as l}from"../services/world.js";import a,{safeRecastInit as u}from"../actors/builtin/navmesh-actor.js";import{NavigationArea as c,NavigationTraversalFlag as h}from"./navigation-areas.js";export const NavigationQueryProfiles=Object.freeze({Walker:Object.freeze({name:"Walker",includeFlags:h.Walk,excludeFlags:h.Swim}),Swimmer:Object.freeze({name:"Swimmer",includeFlags:h.Walk|h.Swim,excludeFlags:0,areaCosts:{[c.Water]:2}})});let d=class{constructor(){this.world=o(l),this.filters=new Map,u().then(()=>{const e=this.world.actors.find(e=>e instanceof a);null!=e&&(this.navMeshActor=e,this.query=null),this.world.actorAdded.subscribe(e=>{e instanceof a&&(this.navMeshActor=e,this.query=null)}),this.world.actorRemoved.subscribe(e=>{e instanceof a&&e.id===this.navMeshActor?.id&&(this.navMeshActor=null,this.query=null)})})}getFilter(e){let t=this.filters.get(e);if(null!=t)return t;t=new s,t.includeFlags=e.includeFlags,t.excludeFlags=e.excludeFlags;for(const[n,s]of Object.entries(e.areaCosts??{}))null!=s&&t.setAreaCost(Number(n),s);return this.filters.set(e,t),t}ensureQuery(){null==this.query&&null!=this.navMeshActor&&null!=this.navMeshActor.navMesh&&(this.query=new n(this.navMeshActor.navMesh))}findPath(e,t,n=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),f;const s=this.findClosestPoint(e,n);if(null==s)return y("Start point could not be found on NavMesh",e),f;const i=this.findClosestPoint(t,n);if(null==i)return y("End point could not be found on NavMesh",t),f;const{success:o,error:l,path:a}=this.query.computePath(s,i,{filter:this.getFilter(n)}),u=a.at(-1),c=null!=u&&function(e,t){const n=e.x-t.x,s=e.y-t.y,r=e.z-t.z;return n*n+s*s+r*r}(u,i)<=.01;return o&&c?{success:!0,path:a.map(e=>new r(e.x,e.y,e.z))}:(y("Failed to generate path",l),f)}findClosestPoint(e,t=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),null;const{success:n,point:s}=this.query.findClosestPoint(e,{filter:this.getFilter(t)});return n?new r(s.x,s.y,s.z):null}findRandomPoint(e=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),null;const t=this.query.findRandomPoint({filter:this.getFilter(e)});return{success:t.success,randomPoint:(new r).copy(t.randomPoint)}}findRandomPointAroundCircle(e,t,n=NavigationQueryProfiles.Walker){if(this.ensureQuery(),null==this.query)return y("NavMesh has not been generated yet"),null;const s=this.query.findRandomPointAroundCircle(e,t,{filter:this.getFilter(n)});return{success:s.success,randomPoint:(new r).copy(s.randomPoint)}}};d=e([i(),t("design:paramtypes",[])],d);export{d as Navigation};const f={success:!1,path:[]};let m=new Set;function y(e,...t){m.has(e)||(m.add(e),setTimeout(()=>m.delete(e),2e3),console.warn(e,...t))}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/dist/gameplay/index.d.ts
CHANGED
|
@@ -44,4 +44,6 @@ export { EVENT_GRAPH_ACTOR_TYPE, EVENT_GRAPH_ACTOR_CLASS_TYPE, EVENT_GRAPH_ANY_T
|
|
|
44
44
|
export type { EventGraphType, EventGraphTypeSource } from './event-graph/types.js';
|
|
45
45
|
export * from './net/net-connection.js';
|
|
46
46
|
export * from './net/net-session.js';
|
|
47
|
+
export * from './services/actor-persistence.js';
|
|
48
|
+
export * from './actors/builtin/components/actor-transform-persistence.js';
|
|
47
49
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/gameplay/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"reflect-metadata";export{Container as DIContainer}from"typedi";export*from"./initiate.js";export*from"./game-mode.js";export*from"./player-lifecycle-coordinator.js";export*from"./player-sessions.js";export*from"./game-state-context.js";export*from"./network-travel-coordinator.js";export*from"./rendering-host.js";export*from"./inject.js";export{Service,Inject}from"typedi";export{ActorFactory}from"./actors/factory.js";export{Actor,ActorLifetime,BaseActor,getActorClassById,getActorClassId,getRegisteredActorClasses,getActorOptions}from"./actors/actor.js";export{Component,ActorComponent,attach,Attach,getComponentClassById,getComponentClassId,getRegisteredComponentClasses}from"./actors/component.js";export*from"./services/world.js";export*from"./services/scene-transition.js";export*from"./services/game-time-scheduler.js";export*from"./services/camera-shake.js";export*from"./services/render.js";export*from"./services/physics/physics-system.js";export*from"./animation/anim-sm.js";export*from"./animation/root-motion.js";export*from"./animation/retarget.js";export*from"./services/asset-loader.js";export*from"./services/pointer-events.js";export{VisualEffect}from"../effects/vfx/vfx-param.js";export{DataAssetRef}from"../scene/objects/data-asset.js";export{SceneRef,sceneId}from"../scene/scene-ref.js";export*from"../shader/parameter.js";export*from"./ai/index.js";export*from"../effects/vfx/index.js";export*from"./services/shader-provider.js";export{EventGraphCallable,EventGraphEventSource,EventGraphProperty,getEventGraphCallableInfo,getEventGraphCallableInfos,getEventGraphCallableMethods,getEventGraphEventSourceInfo,getEventGraphEventSourceInfos,getEventGraphEventSourceMethods,getEventGraphPropertyInfo,getEventGraphPropertyInfos,getEventGraphProperties,isEventGraphEventSource}from"./event-graph/decorators.js";export{EventGraphCompiler,validateEventGraphDocument}from"./event-graph/document-compiler.js";export{createDefaultEventGraphDocument,hasExecutableEventGraphDocument}from"./event-graph/model.js";export{EVENT_GRAPH_NODE_DEFINITIONS,getEventGraphNodePorts,getEventGraphPortDefinition}from"./event-graph/registry.js";export{EventGraphInstance}from"./event-graph/runtime.js";export{analyzeEventGraphTypes,assertEventGraphTypes,createEventGraphTypeContext,getEventGraphMembersForType}from"./event-graph/type-inference.js";export{EVENT_GRAPH_ACTOR_TYPE,EVENT_GRAPH_ACTOR_CLASS_TYPE,EVENT_GRAPH_ANY_TYPE,EVENT_GRAPH_BOOLEAN_TYPE,EVENT_GRAPH_COMPONENT_TYPE,EVENT_GRAPH_NUMBER_TYPE,EVENT_GRAPH_OBJECT_TYPE,EVENT_GRAPH_STRING_TYPE,EVENT_GRAPH_VOID_TYPE,EVENT_GRAPH_VECTOR2_TYPE,EVENT_GRAPH_VECTOR3_TYPE,eventGraphActorClassType,eventGraphActorType,eventGraphComponentType,eventGraphTypeName,isEventGraphTypeAssignable}from"./event-graph/types.js";export*from"./net/net-connection.js";export*from"./net/net-session.js";/*
|
|
1
|
+
import"reflect-metadata";export{Container as DIContainer}from"typedi";export*from"./initiate.js";export*from"./game-mode.js";export*from"./player-lifecycle-coordinator.js";export*from"./player-sessions.js";export*from"./game-state-context.js";export*from"./network-travel-coordinator.js";export*from"./rendering-host.js";export*from"./inject.js";export{Service,Inject}from"typedi";export{ActorFactory}from"./actors/factory.js";export{Actor,ActorLifetime,BaseActor,getActorClassById,getActorClassId,getRegisteredActorClasses,getActorOptions}from"./actors/actor.js";export{Component,ActorComponent,attach,Attach,getComponentClassById,getComponentClassId,getRegisteredComponentClasses}from"./actors/component.js";export*from"./services/world.js";export*from"./services/scene-transition.js";export*from"./services/game-time-scheduler.js";export*from"./services/camera-shake.js";export*from"./services/render.js";export*from"./services/physics/physics-system.js";export*from"./animation/anim-sm.js";export*from"./animation/root-motion.js";export*from"./animation/retarget.js";export*from"./services/asset-loader.js";export*from"./services/pointer-events.js";export{VisualEffect}from"../effects/vfx/vfx-param.js";export{DataAssetRef}from"../scene/objects/data-asset.js";export{SceneRef,sceneId}from"../scene/scene-ref.js";export*from"../shader/parameter.js";export*from"./ai/index.js";export*from"../effects/vfx/index.js";export*from"./services/shader-provider.js";export{EventGraphCallable,EventGraphEventSource,EventGraphProperty,getEventGraphCallableInfo,getEventGraphCallableInfos,getEventGraphCallableMethods,getEventGraphEventSourceInfo,getEventGraphEventSourceInfos,getEventGraphEventSourceMethods,getEventGraphPropertyInfo,getEventGraphPropertyInfos,getEventGraphProperties,isEventGraphEventSource}from"./event-graph/decorators.js";export{EventGraphCompiler,validateEventGraphDocument}from"./event-graph/document-compiler.js";export{createDefaultEventGraphDocument,hasExecutableEventGraphDocument}from"./event-graph/model.js";export{EVENT_GRAPH_NODE_DEFINITIONS,getEventGraphNodePorts,getEventGraphPortDefinition}from"./event-graph/registry.js";export{EventGraphInstance}from"./event-graph/runtime.js";export{analyzeEventGraphTypes,assertEventGraphTypes,createEventGraphTypeContext,getEventGraphMembersForType}from"./event-graph/type-inference.js";export{EVENT_GRAPH_ACTOR_TYPE,EVENT_GRAPH_ACTOR_CLASS_TYPE,EVENT_GRAPH_ANY_TYPE,EVENT_GRAPH_BOOLEAN_TYPE,EVENT_GRAPH_COMPONENT_TYPE,EVENT_GRAPH_NUMBER_TYPE,EVENT_GRAPH_OBJECT_TYPE,EVENT_GRAPH_STRING_TYPE,EVENT_GRAPH_VOID_TYPE,EVENT_GRAPH_VECTOR2_TYPE,EVENT_GRAPH_VECTOR3_TYPE,eventGraphActorClassType,eventGraphActorType,eventGraphComponentType,eventGraphTypeName,isEventGraphTypeAssignable}from"./event-graph/types.js";export*from"./net/net-connection.js";export*from"./net/net-session.js";export*from"./services/actor-persistence.js";export*from"./actors/builtin/components/actor-transform-persistence.js";/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -11,6 +11,7 @@ import type { NetPlayerInfo } from './net/net-connection.js';
|
|
|
11
11
|
import { RenderingHost, type RenderingHostStageContext } from './rendering-host.js';
|
|
12
12
|
import { type SceneTransitionRequest } from './services/scene-transition.js';
|
|
13
13
|
import { SceneRef } from '../scene/scene-ref.js';
|
|
14
|
+
import { type ActorPersistenceConfig } from './services/actor-persistence.js';
|
|
14
15
|
type GameClass<T> = Constructable<T>;
|
|
15
16
|
export type RenderingConfig = Partial<{
|
|
16
17
|
backend: RenderingBackend;
|
|
@@ -74,6 +75,7 @@ export type InitiateApplicationConfig = ApplicationConfigBase & ({
|
|
|
74
75
|
xr?: never;
|
|
75
76
|
});
|
|
76
77
|
export type LoadGameConfig = {
|
|
78
|
+
persistence?: ActorPersistenceConfig;
|
|
77
79
|
sceneName: string;
|
|
78
80
|
detailTier?: DetailTier;
|
|
79
81
|
multi?: {
|
|
@@ -83,6 +85,7 @@ export type LoadGameConfig = {
|
|
|
83
85
|
localPlayer?: NetPlayerInfo;
|
|
84
86
|
};
|
|
85
87
|
export type InitiateGameConfig = ApplicationConfigBase & {
|
|
88
|
+
persistence?: ActorPersistenceConfig;
|
|
86
89
|
element: HTMLElement;
|
|
87
90
|
sceneName: string;
|
|
88
91
|
rendering?: RenderingConfig;
|
|
@@ -176,6 +179,7 @@ export declare class HologyApplicationRuntime<TGame = unknown> {
|
|
|
176
179
|
private performLoad;
|
|
177
180
|
private createSession;
|
|
178
181
|
private performSceneTransition;
|
|
182
|
+
private restoreSceneActors;
|
|
179
183
|
private copyApplicationServices;
|
|
180
184
|
private disposeSession;
|
|
181
185
|
private enqueue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e from"typedi";import{BehaviorSubject as t,Subject as i}from"rxjs";import{loadScene as n}from"../scene/bootstrap.js";import{ActorFactory as s}from"./actors/factory.js";import{SceneMaterializer as o}from"../scene/materializer.js";import{SceneDataService as r}from"../scene/scene-data-service.js";import{World as a}from"./services/world.js";import{ViewController as c}from"./services/render.js";import{RenderingView as l,GraphicsRuntime as p}from"../rendering.js";import{PhysicsSystem as h}from"./services/physics/physics-system.js";import{MeshComponent as d}from"./actors/builtin/components/mesh-component.js";import{builtInComponents as m}from"./actors/builtin/components/index.js";import{InputService as u}from"./input/index.js";import{RuntimeAssetsService as g}from"../scene/runtime-asset-service.js";import{AssetResourceLoader as w}from"../scene/asset-resource-loader.js";import{AssetLoader as f}from"./services/asset-loader.js";import{RuntimeBundledBackendService as y}from"../scene/runtime-bundled-backend-service.js";import{AssetsProvider as S}from"../scene/assets-provider.js";import{ShaderProvider as v}from"./services/shader-provider.js";import{PointerEvents as b}from"./services/pointer-events.js";import{GpuVfxWorldService as j}from"../effects/vfx/gpu-vfx-world-service.js";import{NetMode as I}from"./net/net-session.js";import{GameStateContext as A}from"./game-state-context.js";import{PlayerSessions as V}from"./player-sessions.js";import{polyfillClient as P}from"./polyfill.js";import{withInjectionContext as R}from"./inject.js";import{RenderingHost as N}from"./rendering-host.js";import{SceneTransitionService as x}from"./services/scene-transition.js";import{SceneRef as z}from"../scene/scene-ref.js";let C=0;export function initiateApplication(t,i){const n="function"==typeof t?t:void 0,s=i??t;var o;P(),"element"in s&&null!=s.element&&(o=s.element,Object.assign(o.style,{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",overflow:"hidden"}));const r=s.renderingHost??(null!=s.renderingView?new N(s.renderingView,!1,("renderingViewFactory"in s?s.renderingViewFactory:void 0)??(()=>new l(s.renderingView.container,s.renderingView.options))):function(e,t,i){const n=new p,s=()=>function(e,t,i,n){return new l(e,{graphicsRuntime:n,backend:t?.backend??"webgpu",enableXR:!0===i?.enabled,maxPixelRatio:t?.maxPixelRatio,resolutionScale:t?.resolutionScale,dynamicResolution:t?.dynamicResolution,upscaling:t?.upscaling,webgpuPostProcessing:t?.webgpuPostProcessing,webgpuDirectionalShadows:t?.webgpuDirectionalShadows,webgpuVfx:t?.webgpuVfx??"auto",msaa:t?.msaa,fpsCap:t?.fpsCap,depthPrepass:{enabled:!0===t?.depthPrepass?.enabled},bloom:{enabled:!1!==t?.bloom?.enabled},reflection:{enabled:!1!==t?.reflection?.enabled},occlusionCulling:{enabled:!1!==t?.occlusionCulling?.enabled},shadows:{enabled:t?.shadows?.enabled??!0,autoUpdate:t?.shadows?.autoUpdate??!0,cascadeUpdateIntervals:[22,40,60,120]}})}(e,t,i,n);return new N(s(),!0,s)}(s.element,s.rendering,s.xr)),a=e.of("hology-application-"+ ++C),c=new HologyApplicationRuntime(s,a,r,n??s.defaultGameClass);return c.startInitialization(),c}export function initiateGame(e,t){const{sceneName:i,detailTier:n,multi:s,localPlayer:o,...r}=t,a=initiateApplication(e,r),c=a.ready.then(()=>a.loadGame({sceneName:i,detailTier:n,multi:s,localPlayer:o}));return new HologyRuntime(a,c)}export class GameInstance{onPreload(){}onStart(){}onUnload(){return this.onShutdown()}onShutdown(){}}export class HologyApplicationRuntime{constructor(e,n,s,o){this.config=e,this.applicationContainer=n,this.renderingHost=s,this.defaultGameClass=o,this.stateSubject=new t({type:"initializing"}),this.state=this.stateSubject.asObservable(),this.shutdownSubject=new i,this.shutdownStarted=this.shutdownSubject.asObservable(),this.transitionTail=Promise.resolve(),this.initializationPromise=Promise.resolve(),this.transitionIntent=0,this.shutdownFlag=!1,this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t})}get renderingView(){return this.renderingHost.activeView}get currentState(){return this.stateSubject.value}startInitialization(){this.initializationPromise=this.initialize()}async initialize(){try{if(this.registerApplicationServices(),await this.renderingHost.applicationView.compileAsync(),this.shutdownFlag)return void this.rejectReady(k());const e=this.getApplicationService(u);this.renderingHost.start(t=>e.update(t)),this.setState({type:"menu"}),this.resolveReady()}catch(e){this.setState({type:"error",operation:"initialize",error:e}),this.rejectReady(e)}}loadGame(e,t){const i="function"==typeof e?e:this.defaultGameClass,n=t??e;if(null==i)return Promise.reject(new Error("loadGame() requires a game class because no default was configured"));const s=++this.transitionIntent;return this.enqueue(async()=>(await this.ready,this.assertRunning(),this.checkIntent(s),null!=this.session&&await this.disposeSession(this.session,!0),this.checkIntent(s),this.performLoad(i,n,s)))}unloadGame(){const e=++this.transitionIntent;return this.enqueue(async()=>{if(await this.ready,!this.shutdownFlag&&e===this.transitionIntent){try{null!=this.session&&await this.disposeSession(this.session,!0)}catch(t){throw this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"error",operation:"unload",error:t}),t}this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"menu"})}})}transitionScene(e){const t="string"==typeof e?{scene:new z(e,e)}:e instanceof z?{scene:e}:e,i=this.transitionIntent;return this.enqueue(async()=>{await this.ready,this.assertRunning(),this.checkIntent(i);const e=this.session;if(null==e)throw new Error("transitionScene() requires an active game session");try{return await this.performSceneTransition(e,t,i),e}catch(e){throw G(e)||this.shutdownFlag||i!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}})}async shutdown(){this.shutdownFlag||(this.shutdownFlag=!0,++this.transitionIntent,this.shutdownSubject.next(),this.shutdownSubject.complete(),await this.initializationPromise.catch(()=>{}),await this.transitionTail.catch(()=>{}),null!=this.session&&await this.disposeSession(this.session,!1),this.getApplicationService(u).stop(),this.getOptionalApplicationService(b)?.stop(),this.getApplicationService(w).disposeAll(),this.getApplicationService(g).dispose(),this.getApplicationService(c).dispose(!1),this.renderingHost.shutdown(),e.reset(this.applicationContainer.id),this.setState({type:"stopped"}),this.stateSubject.complete())}getWorld(){return this.session?.world}getGameInstance(){return this.session?.gameInstance}getApplicationService(e){return this.applicationContainer.get(e)}getSessionService(e){return this.session?.container.get(e)}getService(e){return null!=this.session?this.session.container.get(e):this.applicationContainer.get(e)}registerRenderStage(e){return this.renderingHost.registerStage(e)}registerApplicationServices(){const e=new c(this.renderingView);this.applicationContainer.set(l,this.renderingHost.applicationView),this.applicationContainer.set(c,e);const t=new y(this.config.runtimeDataUrl),i=new g(t),n=new w;n.setDataDir(this.config.dataDir),n.setDirectFileLoading(!0===this.config.directAssetFileLoading),"webgpu"===this.renderingHost.applicationView.requestedBackend?n.initKtx2ForWebGpu():n.initKtx2(this.renderingHost.applicationView.renderer);const s=Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t}));this.applicationContainer.set(y,t),this.applicationContainer.set(S,i),this.applicationContainer.set(g,i),this.applicationContainer.set(w,n),this.applicationContainer.set(f,new f(n,i,s)),this.applicationContainer.set(v,new v(s));const o=R(this.applicationContainer,e=>e.get(u));this.applicationContainer.set(u,o)}async performLoad(t,i,s){const r=this.createSession(i);this.session=r;try{this.loading(i.sceneName,"physics",.08),await r.physics.start(),this.checkIntent(s),this.loading(i.sceneName,"runtime-data",.2),await this.getApplicationService(y).preloadData(),this.checkIntent(s),this.loading(i.sceneName,"scene",.42);const a=await n(r.renderingView,i.sceneName,this.config.dataDir,this.config.shaders,this.config.actors,r.components,r.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:i.detailTier,nodeMode:i.multi?.session.mode??I.none});if(r.materializer.dispose(),r.materializer=a.materializer,r.sceneRoots=a.sceneRoots,r.container.set(o,a.materializer),r.world.materializer=a.materializer,r.world.scene=a.scene,r.physics.scene=a.scene,this.checkIntent(s),null!=i.multi?.session){const{NetService:e}=await import("./net/service/net-service.js"),t=R(r.container,t=>t.get(e));t.session=i.multi.session,t.start(),r.stopPlayers=()=>t.stop()}e.import([d]);for(const e of a.actors)r.world.addActor(e);if(r.physics.addFromScene(a.scene),await r.world.startSceneEventGraphs(a.materializer),this.checkIntent(s),r.gameInstance=R(r.container,e=>(e.set({id:t,type:t}),e.get(t))),this.loading(i.sceneName,"game-preload",.58),r.gameInstance instanceof GameInstance&&await r.gameInstance.onPreload(),this.checkIntent(s),this.loading(i.sceneName,"shaders",.7),await r.renderingView.compileAsync(),this.checkIntent(s),this.loading(i.sceneName,"textures",.82),await r.renderingView.initTexturesAsync(),this.checkIntent(s),"webgpu"===r.renderingView.activeBackend){this.loading(i.sceneName,"vfx-warmup",.94);const e=r.container.get(j).beginPipelineWarmup();try{const e=performance.now();console.info("[Hology GPU VFX warmup] Rendering loading frame",i.sceneName),r.renderingView.render(0),console.info("[Hology GPU VFX warmup] Render returned; waiting for GPU queue",{sceneName:i.sceneName,renderMs:performance.now()-e}),await(r.renderingView.webGpuDevice?.queue.onSubmittedWorkDone()),console.info("[Hology GPU VFX warmup] GPU queue completed",{sceneName:i.sceneName,totalMs:performance.now()-e})}finally{e()}}return this.checkIntent(s),this.renderingHost.activate(r.renderingView),r.gameInstance instanceof GameInstance&&await r.gameInstance.onStart(),this.checkIntent(s),this.setState({type:"playing",sceneName:i.sceneName}),r}catch(e){throw await this.disposeSession(r,!1),G(e)||this.shutdownFlag||s!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}}createSession(t){const i=e.of("hology-session-"+ ++C);this.copyApplicationServices(i);const n=this.renderingHost.createView(),p=new c(n);i.set(l,n),i.set(c,p);const d={...m,...this.config.components??{}},u=new s(i,{inEditor:!1});u.classes=this.config.actors,i.set(s,u);const f=new o(n.scene,new r,this.getApplicationService(g),this.getApplicationService(w),n,Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t})),Object.entries(this.config.actors).map(([e,t])=>({name:e,type:t})),u,Object.entries(d).map(([e,t])=>({name:e,type:t})));f.inEditor=!1,f.netMode=t.multi?.session.mode??I.none,i.set(o,f);const y=R(i,e=>e.get(a));y.scene=n.scene,i.set(a,y);const S=R(i,e=>e.get(h));i.set(h,S);const v=new x;v.bind(async e=>{await this.transitionScene(e)}),i.set(x,v);const b=R(i,e=>e.get(V));i.set(V,b);const j=R(i,e=>e.get(A));return i.set(A,j),null==t.multi?.session&&b.startStandalone(t.localPlayer??{id:"local-player",displayName:"Player"}),{sceneName:t.sceneName,detailTier:t.detailTier,container:i,renderingView:n,viewController:p,actorFactory:u,materializer:f,world:y,physics:S,components:d,sceneTransition:v,sceneRoots:[],netMode:t.multi?.session.mode??I.none,stopPlayers:null==t.multi?.session?()=>{b.stop(),j.stop()}:void 0}}async performSceneTransition(e,t,i){if(e.netMode!==I.none)throw new Error("In-session scene transitions are not yet supported for network sessions");const s=t.scene.id.trim();if(0===s.length)throw new Error("A destination scene id is required");const r=e.sceneName;this.setState({type:"unloading",sceneName:r}),e.world.stopSceneEventGraphs();const a=e.world.removeSceneLifetimeActors(e.sceneRoots);for(const t of e.sceneRoots)e.physics.removeRemoved(t),t.removeFromParent();e.sceneRoots=[],e.materializer.dispose(a),this.checkIntent(i),this.loading(s,"scene",.42);const c=await n(e.renderingView,s,this.config.dataDir,this.config.shaders,this.config.actors,e.components,e.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:e.detailTier,nodeMode:e.netMode});this.checkIntent(i),e.materializer=c.materializer,e.sceneRoots=c.sceneRoots,e.sceneName=s,e.container.set(o,c.materializer),e.world.materializer=c.materializer;for(const t of c.actors)e.world.addActor(t);e.physics.addFromScene(c.scene),await e.world.startSceneEventGraphs(c.materializer),await(t.afterLoad?.(e.world)),this.checkIntent(i),this.loading(s,"shaders",.7),await e.renderingView.compileAsync(),this.checkIntent(i),this.loading(s,"textures",.82),await e.renderingView.initTexturesAsync(),this.checkIntent(i),e.sceneTransition.publish({previousSceneName:r,sceneName:s}),this.setState({type:"playing",sceneName:s})}copyApplicationServices(e){const t=[u,S,g,y,w,f,v];for(const i of t)e.set(i,this.applicationContainer.get(i))}async disposeSession(t,i){if(t.disposed)return;let n;t.disposed=!0,i&&this.setState({type:"unloading",sceneName:t.sceneName});try{t.stopPlayers?.(),t.sceneTransition.dispose(),t.world.stopSceneEventGraphs(),t.gameInstance instanceof GameInstance&&await t.gameInstance.onUnload()}catch(e){n=e}finally{for(const e of[...t.world.actors])t.world.removeActor(e);t.physics.stop(),t.materializer.dispose(),this.renderingHost.activeView===t.renderingView&&this.renderingHost.activate(this.renderingHost.applicationView),t.viewController.dispose(!1),this.renderingHost.releaseView(t.renderingView),e.reset(t.container.id),this.session===t&&(this.session=void 0)}if(null!=n)throw n}enqueue(e){const t=this.transitionTail.then(e,e);return this.transitionTail=t.catch(()=>{}),t}checkIntent(e){if(this.assertRunning(),e!==this.transitionIntent)throw k()}assertRunning(){if(this.shutdownFlag)throw new Error("Hology application has been shut down")}loading(e,t,i){this.setState({type:"loading",sceneName:e,stage:t,progress:i})}setState(e){this.stateSubject.next(e)}getOptionalApplicationService(e){return this.applicationContainer.has(e)?this.applicationContainer.get(e):void 0}}export class HologyRuntime{constructor(e,t){this.application=e,this.ready=t.then(()=>!0),this.shutdownStarted=e.shutdownStarted}get status(){return"playing"===this.application.currentState.type?5:"stopped"===this.application.currentState.type||"error"===this.application.currentState.type?10:0}get gameInstance(){return this.application.getGameInstance()}get isShutdown(){return"stopped"===this.application.currentState.type}getWorld(){return this.application.getWorld()}getService(e){return this.application.getService(e)}shutdown(){return this.application.shutdown()}}function k(){return new DOMException("Superseded by a newer game transition","AbortError")}function G(e){return e instanceof DOMException&&"AbortError"===e.name}/*
|
|
1
|
+
import e from"typedi";import{BehaviorSubject as t,Subject as i}from"rxjs";import{loadScene as n}from"../scene/bootstrap.js";import{ActorFactory as s}from"./actors/factory.js";import{SceneMaterializer as r}from"../scene/materializer.js";import{SceneDataService as o}from"../scene/scene-data-service.js";import{World as a}from"./services/world.js";import{ViewController as c}from"./services/render.js";import{RenderingView as l,GraphicsRuntime as p}from"../rendering.js";import{PhysicsSystem as h}from"./services/physics/physics-system.js";import{MeshComponent as d}from"./actors/builtin/components/mesh-component.js";import{builtInComponents as m}from"./actors/builtin/components/index.js";import{InputService as u}from"./input/index.js";import{RuntimeAssetsService as g}from"../scene/runtime-asset-service.js";import{AssetResourceLoader as w}from"../scene/asset-resource-loader.js";import{AssetLoader as f}from"./services/asset-loader.js";import{RuntimeBundledBackendService as y}from"../scene/runtime-bundled-backend-service.js";import{AssetsProvider as S}from"../scene/assets-provider.js";import{ShaderProvider as v}from"./services/shader-provider.js";import{PointerEvents as b}from"./services/pointer-events.js";import{GpuVfxWorldService as I}from"../effects/vfx/gpu-vfx-world-service.js";import{NetMode as j}from"./net/net-session.js";import{GameStateContext as A}from"./game-state-context.js";import{PlayerSessions as V}from"./player-sessions.js";import{polyfillClient as P}from"./polyfill.js";import{withInjectionContext as C}from"./inject.js";import{RenderingHost as R}from"./rendering-host.js";import{SceneTransitionService as N}from"./services/scene-transition.js";import{SceneRef as x}from"../scene/scene-ref.js";import{ActorPersistence as z}from"./services/actor-persistence.js";let k=0;export function initiateApplication(t,i){const n="function"==typeof t?t:void 0,s=i??t;var r;P(),"element"in s&&null!=s.element&&(r=s.element,Object.assign(r.style,{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",overflow:"hidden"}));const o=s.renderingHost??(null!=s.renderingView?new R(s.renderingView,!1,("renderingViewFactory"in s?s.renderingViewFactory:void 0)??(()=>new l(s.renderingView.container,s.renderingView.options))):function(e,t,i){const n=new p,s=()=>function(e,t,i,n){return new l(e,{graphicsRuntime:n,backend:t?.backend??"webgpu",enableXR:!0===i?.enabled,maxPixelRatio:t?.maxPixelRatio,resolutionScale:t?.resolutionScale,dynamicResolution:t?.dynamicResolution,upscaling:t?.upscaling,webgpuPostProcessing:t?.webgpuPostProcessing,webgpuDirectionalShadows:t?.webgpuDirectionalShadows,webgpuVfx:t?.webgpuVfx??"auto",msaa:t?.msaa,fpsCap:t?.fpsCap,depthPrepass:{enabled:!0===t?.depthPrepass?.enabled},bloom:{enabled:!1!==t?.bloom?.enabled},reflection:{enabled:!1!==t?.reflection?.enabled},occlusionCulling:{enabled:!1!==t?.occlusionCulling?.enabled},shadows:{enabled:t?.shadows?.enabled??!0,autoUpdate:t?.shadows?.autoUpdate??!0,cascadeUpdateIntervals:[22,40,60,120]}})}(e,t,i,n);return new R(s(),!0,s)}(s.element,s.rendering,s.xr)),a=e.of("hology-application-"+ ++k),c=new HologyApplicationRuntime(s,a,o,n??s.defaultGameClass);return c.startInitialization(),c}export function initiateGame(e,t){const{sceneName:i,detailTier:n,multi:s,localPlayer:r,persistence:o,...a}=t,c=initiateApplication(e,a),l=c.ready.then(()=>c.loadGame({sceneName:i,detailTier:n,multi:s,localPlayer:r,persistence:o}));return new HologyRuntime(c,l)}export class GameInstance{onPreload(){}onStart(){}onUnload(){return this.onShutdown()}onShutdown(){}}export class HologyApplicationRuntime{constructor(e,n,s,r){this.config=e,this.applicationContainer=n,this.renderingHost=s,this.defaultGameClass=r,this.stateSubject=new t({type:"initializing"}),this.state=this.stateSubject.asObservable(),this.shutdownSubject=new i,this.shutdownStarted=this.shutdownSubject.asObservable(),this.transitionTail=Promise.resolve(),this.initializationPromise=Promise.resolve(),this.transitionIntent=0,this.shutdownFlag=!1,this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t})}get renderingView(){return this.renderingHost.activeView}get currentState(){return this.stateSubject.value}startInitialization(){this.initializationPromise=this.initialize()}async initialize(){try{if(this.registerApplicationServices(),await this.renderingHost.applicationView.compileAsync(),this.shutdownFlag)return void this.rejectReady(H());const e=this.getApplicationService(u);this.renderingHost.start(t=>e.update(t)),this.setState({type:"menu"}),this.resolveReady()}catch(e){this.setState({type:"error",operation:"initialize",error:e}),this.rejectReady(e)}}loadGame(e,t){const i="function"==typeof e?e:this.defaultGameClass,n=t??e;if(null==i)return Promise.reject(new Error("loadGame() requires a game class because no default was configured"));const s=++this.transitionIntent;return this.enqueue(async()=>(await this.ready,this.assertRunning(),this.checkIntent(s),null!=this.session&&await this.disposeSession(this.session,!0),this.checkIntent(s),this.performLoad(i,n,s)))}unloadGame(){const e=++this.transitionIntent;return this.enqueue(async()=>{if(await this.ready,!this.shutdownFlag&&e===this.transitionIntent){try{null!=this.session&&await this.disposeSession(this.session,!0)}catch(t){throw this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"error",operation:"unload",error:t}),t}this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"menu"})}})}transitionScene(e){const t="string"==typeof e?{scene:new x(e,e)}:e instanceof x?{scene:e}:e,i=this.transitionIntent;return this.enqueue(async()=>{await this.ready,this.assertRunning(),this.checkIntent(i);const e=this.session;if(null==e)throw new Error("transitionScene() requires an active game session");try{return await this.performSceneTransition(e,t,i),e}catch(e){throw G(e)||this.shutdownFlag||i!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}})}async shutdown(){this.shutdownFlag||(this.shutdownFlag=!0,++this.transitionIntent,this.shutdownSubject.next(),this.shutdownSubject.complete(),await this.initializationPromise.catch(()=>{}),await this.transitionTail.catch(()=>{}),null!=this.session&&await this.disposeSession(this.session,!1),this.getApplicationService(u).stop(),this.getOptionalApplicationService(b)?.stop(),this.getApplicationService(w).disposeAll(),this.getApplicationService(g).dispose(),this.getApplicationService(c).dispose(!1),this.renderingHost.shutdown(),e.reset(this.applicationContainer.id),this.setState({type:"stopped"}),this.stateSubject.complete())}getWorld(){return this.session?.world}getGameInstance(){return this.session?.gameInstance}getApplicationService(e){return this.applicationContainer.get(e)}getSessionService(e){return this.session?.container.get(e)}getService(e){return null!=this.session?this.session.container.get(e):this.applicationContainer.get(e)}registerRenderStage(e){return this.renderingHost.registerStage(e)}registerApplicationServices(){const e=new c(this.renderingView);this.applicationContainer.set(l,this.renderingHost.applicationView),this.applicationContainer.set(c,e);const t=new y(this.config.runtimeDataUrl),i=new g(t),n=new w;n.setDataDir(this.config.dataDir),n.setDirectFileLoading(!0===this.config.directAssetFileLoading),"webgpu"===this.renderingHost.applicationView.requestedBackend?n.initKtx2ForWebGpu():n.initKtx2(this.renderingHost.applicationView.renderer);const s=Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t}));this.applicationContainer.set(y,t),this.applicationContainer.set(S,i),this.applicationContainer.set(g,i),this.applicationContainer.set(w,n),this.applicationContainer.set(f,new f(n,i,s)),this.applicationContainer.set(v,new v(s));const r=C(this.applicationContainer,e=>e.get(u));this.applicationContainer.set(u,r)}async performLoad(t,i,s){if(null!=i.persistence&&null!=i.multi?.session&&i.multi.session.mode!==j.none)throw new Error("Actor persistence currently supports single-player sessions only");const o=this.createSession(i);this.session=o;try{this.loading(i.sceneName,"physics",.08),await o.physics.start(),this.checkIntent(s),this.loading(i.sceneName,"runtime-data",.2),await this.getApplicationService(y).preloadData(),this.checkIntent(s),this.loading(i.sceneName,"scene",.42);const a=await n(o.renderingView,i.sceneName,this.config.dataDir,this.config.shaders,this.config.actors,o.components,o.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:i.detailTier,nodeMode:i.multi?.session.mode??j.none,persistence:o.world.persistence});if(o.materializer.dispose(),o.materializer=a.materializer,o.sceneRoots=a.sceneRoots,o.container.set(r,a.materializer),o.world.materializer=a.materializer,o.world.scene=a.scene,o.physics.scene=a.scene,this.checkIntent(s),null!=i.multi?.session){const{NetService:e}=await import("./net/service/net-service.js"),t=C(o.container,t=>t.get(e));t.session=i.multi.session,t.start(),o.stopPlayers=()=>t.stop()}if(e.import([d]),await this.restoreSceneActors(o,a.materializer,a.actors,s),o.physics.addFromScene(a.scene),await o.world.startSceneEventGraphs(a.materializer),this.checkIntent(s),o.gameInstance=C(o.container,e=>(e.set({id:t,type:t}),e.get(t))),this.loading(i.sceneName,"game-preload",.58),o.gameInstance instanceof GameInstance&&await o.gameInstance.onPreload(),this.checkIntent(s),this.loading(i.sceneName,"shaders",.7),await o.renderingView.compileAsync(),this.checkIntent(s),this.loading(i.sceneName,"textures",.82),await o.renderingView.initTexturesAsync(),this.checkIntent(s),"webgpu"===o.renderingView.activeBackend){this.loading(i.sceneName,"vfx-warmup",.94);const e=o.container.get(I).beginPipelineWarmup();try{const e=performance.now();console.info("[Hology GPU VFX warmup] Rendering loading frame",i.sceneName),o.renderingView.render(0),console.info("[Hology GPU VFX warmup] Render returned; waiting for GPU queue",{sceneName:i.sceneName,renderMs:performance.now()-e}),await(o.renderingView.webGpuDevice?.queue.onSubmittedWorkDone()),console.info("[Hology GPU VFX warmup] GPU queue completed",{sceneName:i.sceneName,totalMs:performance.now()-e})}finally{e()}}return this.checkIntent(s),o.gameInstance instanceof GameInstance&&await o.gameInstance.onStart(),this.checkIntent(s),o.viewController.paused=!1,this.renderingHost.activate(o.renderingView),this.setState({type:"playing",sceneName:i.sceneName}),o}catch(e){throw await this.disposeSession(o,!1),G(e)||this.shutdownFlag||s!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}}createSession(t){const i=e.of("hology-session-"+ ++k);this.copyApplicationServices(i);const n=this.renderingHost.createView(),p=new c(n);p.paused=!0,i.set(l,n),i.set(c,p);const d={...m,...this.config.components??{}},u=new z;null!=t.persistence&&u.configure(t.persistence),i.set(z,u);const f=new s(i,{inEditor:!1});f.classes=this.config.actors,i.set(s,f);const y=new r(n.scene,new o,this.getApplicationService(g),this.getApplicationService(w),n,Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t})),Object.entries(this.config.actors).map(([e,t])=>({name:e,type:t})),f,Object.entries(d).map(([e,t])=>({name:e,type:t})));y.inEditor=!1,y.netMode=t.multi?.session.mode??j.none,i.set(r,y);const S=C(i,e=>e.get(a));u.enabled&&(S.persistence=u),S.scene=n.scene,i.set(a,S);const v=C(i,e=>e.get(h));i.set(h,v);const b=new N;b.bind(async e=>{await this.transitionScene(e)}),i.set(N,b);const I=C(i,e=>e.get(V));i.set(V,I);const P=C(i,e=>e.get(A));return i.set(A,P),null==t.multi?.session&&I.startStandalone(t.localPlayer??{id:"local-player",displayName:"Player"}),{sceneName:t.sceneName,detailTier:t.detailTier,container:i,renderingView:n,viewController:p,actorFactory:f,materializer:y,world:S,physics:v,components:d,sceneTransition:b,sceneRoots:[],netMode:t.multi?.session.mode??j.none,stopPlayers:null==t.multi?.session?()=>{I.stop(),P.stop()}:void 0}}async performSceneTransition(e,t,i){if(e.netMode!==j.none)throw new Error("In-session scene transitions are not yet supported for network sessions");const s=t.scene.id.trim();if(0===s.length)throw new Error("A destination scene id is required");const o=e.sceneName,a=e.viewController.paused;e.viewController.paused=!0,this.renderingHost.activate(this.renderingHost.applicationView),e.world.persistence?.captureAll(),this.setState({type:"unloading",sceneName:o}),e.world.stopSceneEventGraphs();const c=e.world.removeSceneLifetimeActors(e.sceneRoots);for(const t of e.sceneRoots)e.physics.removeRemoved(t),t.removeFromParent();e.sceneRoots=[],e.materializer.dispose(c),this.checkIntent(i),this.loading(s,"scene",.42);const l=await n(e.renderingView,s,this.config.dataDir,this.config.shaders,this.config.actors,e.components,e.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:e.detailTier,nodeMode:e.netMode,persistence:e.world.persistence});this.checkIntent(i),e.materializer=l.materializer,e.sceneRoots=l.sceneRoots,e.sceneName=s,e.container.set(r,l.materializer),e.world.materializer=l.materializer,await this.restoreSceneActors(e,l.materializer,l.actors,i),e.physics.addFromScene(l.scene),await e.world.startSceneEventGraphs(l.materializer),await(t.afterLoad?.(e.world)),this.checkIntent(i),this.loading(s,"shaders",.7),await e.renderingView.compileAsync(),this.checkIntent(i),this.loading(s,"textures",.82),await e.renderingView.initTexturesAsync(),this.checkIntent(i),e.sceneTransition.publish({previousSceneName:o,sceneName:s}),e.viewController.paused=a,this.renderingHost.activate(e.renderingView),this.setState({type:"playing",sceneName:s})}async restoreSceneActors(e,t,i,n){if(null==e.world.persistence){for(const t of i)e.world.addActor(t);return}const s=i.filter(t=>!e.world.actors.includes(t));for(const t of s)e.world.addActor(t,void 0,void 0,void 0,!0);const r=t.registerPersistentActors();await e.world.persistence.restore(r,()=>this.checkIntent(n)),this.checkIntent(n);for(const t of s)e.world.beginActorPlay(t)}copyApplicationServices(e){const t=[u,S,g,y,w,f,v];for(const i of t)e.set(i,this.applicationContainer.get(i))}async disposeSession(t,i){if(t.disposed)return;let n;t.disposed=!0,i&&this.setState({type:"unloading",sceneName:t.sceneName});try{t.viewController.paused=!0,t.world.persistence?.captureAll(),t.stopPlayers?.(),t.sceneTransition.dispose(),t.world.stopSceneEventGraphs(),t.gameInstance instanceof GameInstance&&await t.gameInstance.onUnload()}catch(e){n=e}finally{for(const e of[...t.world.actors])t.world.removeActor(e);t.physics.stop(),t.materializer.dispose(),this.renderingHost.activeView===t.renderingView&&this.renderingHost.activate(this.renderingHost.applicationView),t.viewController.dispose(!1),this.renderingHost.releaseView(t.renderingView),e.reset(t.container.id),this.session===t&&(this.session=void 0)}if(null!=n)throw n}enqueue(e){const t=this.transitionTail.then(e,e);return this.transitionTail=t.catch(()=>{}),t}checkIntent(e){if(this.assertRunning(),e!==this.transitionIntent)throw H()}assertRunning(){if(this.shutdownFlag)throw new Error("Hology application has been shut down")}loading(e,t,i){this.setState({type:"loading",sceneName:e,stage:t,progress:i})}setState(e){this.stateSubject.next(e)}getOptionalApplicationService(e){return this.applicationContainer.has(e)?this.applicationContainer.get(e):void 0}}export class HologyRuntime{constructor(e,t){this.application=e,this.ready=t.then(()=>!0),this.shutdownStarted=e.shutdownStarted}get status(){return"playing"===this.application.currentState.type?5:"stopped"===this.application.currentState.type||"error"===this.application.currentState.type?10:0}get gameInstance(){return this.application.getGameInstance()}get isShutdown(){return"stopped"===this.application.currentState.type}getWorld(){return this.application.getWorld()}getService(e){return this.application.getService(e)}shutdown(){return this.application.shutdown()}}function H(){return new DOMException("Superseded by a newer game transition","AbortError")}function G(e){return e instanceof DOMException&&"AbortError"===e.name}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BaseActor } from "../../actors/actor.js";
|
|
2
|
+
import { NetService } from "./net-service.js";
|
|
1
3
|
import { ReplOn } from "./replication.js";
|
|
2
4
|
/**
|
|
3
5
|
* Ensures that this function is only executed if on the server.
|
|
@@ -21,9 +23,11 @@ export declare function RunIfClient(): (originalMethod: Function, context: Class
|
|
|
21
23
|
* Useful for logic that should only run on the client, such as UI updates, visual effects, playing sound.
|
|
22
24
|
*/
|
|
23
25
|
export declare function RunIfNotDedicatedServer(): (originalMethod: Function, context: ClassMethodDecoratorContext) => (this: any, ...args: any[]) => any;
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/** Registers properties declared with TypeScript's legacy decorator syntax. */
|
|
27
|
+
export declare function initializeLegacyReplicatedProperties(actor: BaseActor, netService: NetService): void;
|
|
28
|
+
/** Marks a property to be automatically replicated to clients. */
|
|
29
|
+
export declare function Replicated(replicatedOn?: ReplOn, reliable?: boolean): {
|
|
30
|
+
(target: object, propertyKey: string | symbol): void;
|
|
31
|
+
(target: undefined, context: ClassMemberDecoratorContext): void;
|
|
32
|
+
};
|
|
29
33
|
//# sourceMappingURL=net-decorator.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{BaseActor as e}from"../../actors/actor.js";import{NetService as t}from"./net-service.js";import{inject as n}from"../../inject.js";import{ReplOn as r}from"./replication.js";import{isSignalLike as i}from"./net-utils.js";import{ActorComponent as o}from"../../actors/component.js";export function RunIfServer(){return function(r,i){let o;return i.addInitializer(function(){this instanceof e?o=n(t):console.warn("RunIfServer should not be defined on a non actor")}),function(...e){if(o.isServer)return r.apply(this,e)}}}export function RunIfClient(){return function(r,i){let o;return i.addInitializer(function(){this instanceof e?o=n(t):console.warn("RunIfClient should not be defined on a non actor")}),function(...e){if(!o.isServer)return r.apply(this,e)}}}export function RunIfNotDedicatedServer(){return function(r,i){let o;return i.addInitializer(function(){this instanceof e?o=n(t):console.warn("RunIfClient should not be defined on a non actor")}),function(...e){if(!o.isDedicatedServer)return r.apply(this,e)}}}export function
|
|
1
|
+
import{BaseActor as e}from"../../actors/actor.js";import{NetService as t}from"./net-service.js";import{inject as n}from"../../inject.js";import{ReplOn as r}from"./replication.js";import{isSignalLike as i}from"./net-utils.js";import{ActorComponent as o}from"../../actors/component.js";export function RunIfServer(){return function(r,i){let o;return i.addInitializer(function(){this instanceof e?o=n(t):console.warn("RunIfServer should not be defined on a non actor")}),function(...e){if(o.isServer)return r.apply(this,e)}}}export function RunIfClient(){return function(r,i){let o;return i.addInitializer(function(){this instanceof e?o=n(t):console.warn("RunIfClient should not be defined on a non actor")}),function(...e){if(!o.isServer)return r.apply(this,e)}}}export function RunIfNotDedicatedServer(){return function(r,i){let o;return i.addInitializer(function(){this instanceof e?o=n(t):console.warn("RunIfClient should not be defined on a non actor")}),function(...e){if(!o.isDedicatedServer)return r.apply(this,e)}}}const s=new WeakMap;export function initializeLegacyReplicatedProperties(e,t){const n=(n,r)=>{for(let o=Object.getPrototypeOf(n);null!=o;o=Object.getPrototypeOf(o))for(const{name:c,replicatedOn:a,reliable:f}of s.get(o)??[]){t.registerReplicatedProperty(a,f,e,c,n,r);const o=n[c];if(i(o)){let n=!0;const i=o.subscribe(i=>{n?n=!1:t.isServer&&t.handlePropertySet(a,f,e,c,i,r)});e.disposed.subscribe(i)}else{let i=o;Object.defineProperty(n,c,{configurable:!0,get:()=>i,set:n=>{t.isServer&&t.handlePropertySet(a,f,e,c,n,r),i=n}})}}};n(e);for(const t of e.attachedComponents)n(t,t)}export function Replicated(c=r.all,a=!0){return function(r,f){if(null!=r&&("string"==typeof f||"symbol"==typeof f)){const e=s.get(r)??[];return e.push({name:f.toString(),replicatedOn:c,reliable:a}),void s.set(r,e)}if("object"!=typeof f)return;let l;f.addInitializer(function(){if(!(this instanceof e||this instanceof o))return void console.warn("RunIfClient should not only be used on actors and actor components");let r,s;if(this instanceof e)r=this;else if(this instanceof o&&(r=this.actor,s=this,null==r))return;l=n(t),l.registerReplicatedProperty(c,a,r,f.name.toString(),this,s);const u=this[f.name];if(i(u)){const e=u.subscribe(e=>{if(l.isServer){if(null==r.__netid)return;l.handlePropertySet(c,a,r,f.name.toString(),e,s)}});return void r.disposed.subscribe(()=>{e()})}let p=u;Object.defineProperty(this,f.name,{set:e=>{l.isServer&&l.handlePropertySet(c,a,r,f.name.toString(),e,s),p=e},get:()=>p})})}}export function makePropertyReplicated(e,t,n,r,i){n.registerReplicatedProperty(i,r,e,t,e);let o=e[t];Object.defineProperty(e,t,{set:s=>{n.isServer&&n.handlePropertySet(i,r,e,t,s,null),o=s},get:()=>o})}/*
|
|
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 o}from"rxjs";import{Euler as i,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 h}from"../../actors/component.js";import{$actorComponents as u}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 A}from"./replication.js";import{getRpcMethodId as b,getRpcMethodName as R,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{GameStateContext as I}from"../../game-state-context.js";import{PlayerSessions as B}from"../../player-sessions.js";var $;!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"}($||($={}));var L;!function(e){e[e.actor=0]="actor",e[e.component=1]="component"}(L||(L={}));let O=class{constructor(){this.playerSessions=f(B),this.gameStateContext=f(I),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.gameStateContext.get()}setGameState(e){const t=this.gameStateContext.get();if(null!=t&&t!==e)throw new Error(`A game state is already registered as ${t.constructor.name}`);this.gameStateContext.set(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.playerSessions.startNetwork(this.session),this.playerSessions.setOwningConnectionHandler((e,t)=>this.setOwningConnection(e,t)),this.playerSessions.setClearOwningConnectionHandler(e=>this.clearOwningConnection(e)),this.gameStateContext.setValidator(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),o(e=>{this.configureMaterializedActorRole(e),this.isServer?this.defineNetId(e):this.processQueuedNetStartupActorSpawns(e),e instanceof M?(T(e,"playerId",this,!0,A.all),T(e,"playerState",this,!0,A.all),T(e,"pawn",this,!0,A.all)):e instanceof U?(T(e,"playerId",this,!0,A.all),T(e,"displayName",this,!0,A.all),T(e,"connected",this,!0,A.all)):e instanceof P&&T(e,"playerStates",this,!0,A.all)}),t(8)).subscribe(e=>{this.shouldReplicate(e)&&(T(e,"owner",this,!0,A.all),this.sendActorSpawn(e))}),this.world.actorRemoved.pipe(r(this.destroyed),t(10)).subscribe(e=>{this.sendActorRemove(e),e instanceof P&&this.gameStateContext.clear(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.playerSessions.stop(),this.gameStateContext.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 $.rpc:case $.rpcComponent:case $.rpcBinary:case $.rpcComponentBinary:this.processRpc(e.from,e.buffer);break;case $.prop:case $.propComponent:this.processProp(e.from,e.buffer);break;case $.propSnapshot:this.processPropSnapshot(e.from,e.buffer);break;case $.spawn:await this.processSpawn(e.from,e.buffer);break;case $.removeActor:this.processRemoveActor(e.from,e.buffer);break;case $.serverTime:this.processServerTime(e.from,e.buffer);break;case $.actorReady:this.processActorReady(e.from,e.buffer);break;case $.sceneTravelBegin:this.processSceneTravelBegin(e.from,e.buffer);break;case $.sceneTravelReady:this.processSceneTravelReady(e.from,e.buffer);break;case $.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 o=s.getUint32(n,!0);n+=4;const i=s.getUint8(n);n+=1;const a=r===$.rpcBinary||r===$.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=k(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===o);if(null==p){return this.getActorRefMissingMessageQueue(o).push({from:e,buffer:t}),void console.debug(`Missing actor ${o}. Queueing message for later`)}let h;if(r===$.rpcComponent||r===$.rpcComponentBinary){const e=s.getUint8(n),t=p[u];if(null!=t&&(h=t.find(t=>t.__netid===e)),null==h)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 f=h??p,g=R(f,i);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!=h?` and component net id ${h.__netid}`:""));else console.error(`RPC method id ${i} not found on actor net id ${p.__netid}`+(null!=h?` and component net id ${h.__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 o=s.getUint32(n,!0);n+=4;const i=s.getUint8(n);n+=1;let a="";for(let e=0;e<i;e++)a+=String.fromCharCode(s.getUint8(n+e));n+=i;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===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,a)}let p;if(r===$.propComponent){const e=s.getUint8(n),t=d[u];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 h=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(h,a,e),this.callRepNotify(h,a)}).catch(e=>{console.warn(`Failed to decode asset references for replicated property '${a}' on actor id ${o}`,e)});this.applyReplicatedProperty(h,a,n),this.callRepNotify(h,a)}catch(e){console.warn(`Failed to decode replicated property '${a}' 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 s=new DataView(t);n+=1;const r=s.getUint32(n,!0);n+=4;const o=this.world.actors.find(e=>e.__netid===r);if(null==o){return this.getActorRefMissingMessageQueue(r).push({from:e,buffer:t}),void console.debug(`Missing actor ${r}. Queueing property snapshot for later`)}const i=s.getUint16(n,!0);n+=2;const a=[];for(let c=0;c<i;c++){const i=s.getUint8(n);n+=1;let c=o;if(i===L.component){const e=s.getUint8(n);n+=1;const t=o[u];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(i!==L.actor)return void console.error(`Failed to process property snapshot. Unknown target kind ${i}`);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 h=t.slice(n,n+p);n+=p;try{const n=this.decodeReplicatedValue(h);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 o=n.getUint8(s);s+=1;let i="";for(let e=0;e<o;e++)i+=String.fromCharCode(n.getUint8(s+e));s+=o,Q.set(n.getFloat32(s,!0),n.getFloat32(s+4,!0),n.getFloat32(s+8,!0)),s+=12,N.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 "+i);const d=p(i);if(!d)return void console.error(`Failed to spawn actor: unknown actor type '${i}'`);const h=this.world.actors.find(e=>e.__netid===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(this.spawningActorNetIds.has(r))return void console.log("Actor was already spawned",null!=h);const u=this.findMaterializedNetStartupActor(l,i,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(null==l){this.spawningActorNetIds.add(r);try{const e=await this.world.spawnActorWithNetInfo(d,r,a,c,Q,N);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:k(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,$.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),o=this.getConnectionKey(e),i=r?.get(o);if(null!=i){this.sendPropertySnapshot(s,e);const t=i.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(o),0===r.size&&this.actorPreSpawnMessageBuffer.delete(s)}}sendRpc(e,t,n,s,r,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 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!=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=b(d,s);if(null==p)return void console.warn(`RPC function '${s}' is not registered on ${d.constructor?.name??"target"}`);const h=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!=h){let e=10+h.byteLength;l&&(e+=1);const s=new ArrayBuffer(e),r=new DataView(s);let a=0;r.setUint8(a,l?$.rpcComponentBinary:$.rpcBinary),a+=1,r.setUint32(a,i,!0),a+=4,r.setUint8(a,p),a+=1,r.setUint32(a,h.byteLength,!0),a+=4,x(s,a,h),a+=h.byteLength,l&&(r.setUint8(a,o.__netid),a+=1);for(const e of c)null!=e&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:s});return}const u=this.serializer.encode(r),f=j.encode(u);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?$.rpcComponent:$.rpc),v+=1,m.setUint32(v,i,!0),v+=4,m.setUint8(v,p),v+=1,m.setUint16(v,f.byteLength,!0),v+=2,x(w,v,f),v+=f.byteLength,l&&(m.setUint8(v,o.__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,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 a=this.serializer.encode(r),c=j.encode(a),l=c.buffer.slice(c.byteOffset,c.byteOffset+c.byteLength);i[this.getReplicatedPropertyKey(s,o)]={property:s,replOn:e,reliable:t,buffer:l,component:o},this.sendProperty(e,t,n,s,l,o)}sendProperty(e,t,n,s,r,o){if(!this.isServer)return;const i=[],a=this.resolveActorConnection(n);switch(e){case A.all:i.push(...this.session.clients);break;case A.owner:null!=a&&i.push(a);break;case A.notOwner:null==a?i.push(...this.session.clients):i.push(...this.session.clients.filter(e=>e.id!==a.id))}const c=null!=o&&null!=o.__netid;let l=6+s.length+2+r.byteLength;c&&(l+=1);const d=new ArrayBuffer(l),p=new DataView(d);let h=0;p.setUint8(h,c?$.propComponent:$.prop),h+=1,p.setUint32(h,n.__netid,!0),h+=4,p.setUint8(h,s.length),h+=1;for(let e=0;e<s.length;e++)p.setUint8(h++,s.charCodeAt(e));p.setUint16(h,r.byteLength,!0),h+=2,new Uint8Array(d,h,r.byteLength).set(new Uint8Array(r)),h+=r.byteLength,c&&(p.setUint8(h,o.__netid),h+=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,s,r,o){if(null==n||null==s||null==r)return;let i=this.replicatedPropertyRegistry.get(n);null==i&&(i=[],this.replicatedPropertyRegistry.set(n,i));const a=o??(r instanceof h?r:void 0),c=i.find(e=>e.target===r&&e.property===s);if(null!=c)return c.replOn=e,c.reliable=t,void(c.component=a);i.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 o=e.constructor.__actorId??e.constructor.name;const i=j.encode(o);let a;null!=e.__netSceneId&&(a=j.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+i.length+12+12+1+4+2+c,d=new ArrayBuffer(l),p=new DataView(d);let h=0;p.setUint8(h,$.spawn),h+=1,p.setUint32(h,r,!0),h+=4,p.setUint8(h,i.length),h+=1,new Uint8Array(d).set(i,h),h+=i.length,p.setFloat32(h,e.position.x,!0),h+=4,p.setFloat32(h,e.position.y,!0),h+=4,p.setFloat32(h,e.position.z,!0),h+=4,p.setFloat32(h,e.rotation.x,!0),h+=4,p.setFloat32(h,e.rotation.y,!0),h+=4,p.setFloat32(h,e.rotation.z,!0),h+=4;let u=h;h+=1,p.setUint32(h,e.owner?.__netid??0),h+=4,p.setUint16(h,c,!0),h+=2,null!=a&&(x(d,h,a),h+=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(u,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,$.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($.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($.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($.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=j.encode(JSON.stringify(t)),s=new ArrayBuffer(1+n.byteLength);return new DataView(s).setUint8(0,e),x(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,$.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===$.prop||t===$.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),o=this.serializer.encode(n),i=j.encode(o),a=i.buffer.slice(i.byteOffset,i.byteOffset+i.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 A.all:return!0;case A.owner:return s?.id===n.id;case A.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=>j.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),o=new DataView(r);let i=0;o.setUint8(i,$.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],a=s[e],c=null!=n.component;o.setUint8(i,c?L.component:L.actor),i+=1,c&&(o.setUint8(i,n.component.__netid),i+=1),o.setUint8(i,a.byteLength),i+=1,new Uint8Array(r,i,a.byteLength).set(a),i+=a.byteLength,o.setUint32(i,n.buffer.byteLength,!0),i+=4,new Uint8Array(r,i,n.buffer.byteLength).set(new Uint8Array(n.buffer)),i+=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.playerSessions.get(e.playerId);if(null==t)return;return this.playerSessions.attachController(e.playerId,e),void e.setLocallyControlled(null!=t.localPlayerIndex)}if(e instanceof U&&null!=e.playerId){null!=this.playerSessions.get(e.playerId)&&this.playerSessions.attachPlayerState(e.playerId,e)}}callRepNotify(e,t){const n=e[`onRep_${t}`];"function"==typeof n&&n.call(e)}};O=e([c()],O);export{O as NetService};const Q=new a,N=new i,j=new TextEncoder,D="undefined"!=typeof TextDecoder?new TextDecoder:void 0;function k(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 x(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 o}from"rxjs";import{Euler as i,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 h}from"../../actors/component.js";import{$actorComponents as u}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 A}from"./replication.js";import{getRpcMethodId as b,getRpcMethodName as R,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{initializeLegacyReplicatedProperties as T,makePropertyReplicated as I}from"./net-decorator.js";import{GameStateContext as B}from"../../game-state-context.js";import{PlayerSessions as $}from"../../player-sessions.js";var L;!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"}(L||(L={}));var O;!function(e){e[e.actor=0]="actor",e[e.component=1]="component"}(O||(O={}));let Q=class{constructor(){this.playerSessions=f($),this.gameStateContext=f(B),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.gameStateContext.get()}setGameState(e){const t=this.gameStateContext.get();if(null!=t&&t!==e)throw new Error(`A game state is already registered as ${t.constructor.name}`);this.gameStateContext.set(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.playerSessions.startNetwork(this.session),this.playerSessions.setOwningConnectionHandler((e,t)=>this.setOwningConnection(e,t)),this.playerSessions.setClearOwningConnectionHandler(e=>this.clearOwningConnection(e)),this.gameStateContext.setValidator(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),o(e=>{this.configureMaterializedActorRole(e),this.isServer?this.defineNetId(e):this.processQueuedNetStartupActorSpawns(e),e instanceof M?(I(e,"playerId",this,!0,A.all),I(e,"playerState",this,!0,A.all),I(e,"pawn",this,!0,A.all)):e instanceof U?(I(e,"playerId",this,!0,A.all),I(e,"displayName",this,!0,A.all),I(e,"connected",this,!0,A.all)):e instanceof P&&I(e,"playerStates",this,!0,A.all),T(e,this)}),t(8)).subscribe(e=>{this.shouldReplicate(e)&&(I(e,"owner",this,!0,A.all),this.sendActorSpawn(e))}),this.world.actorRemoved.pipe(r(this.destroyed),t(10)).subscribe(e=>{this.sendActorRemove(e),e instanceof P&&this.gameStateContext.clear(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.playerSessions.stop(),this.gameStateContext.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 L.rpc:case L.rpcComponent:case L.rpcBinary:case L.rpcComponentBinary:this.processRpc(e.from,e.buffer);break;case L.prop:case L.propComponent:this.processProp(e.from,e.buffer);break;case L.propSnapshot:this.processPropSnapshot(e.from,e.buffer);break;case L.spawn:await this.processSpawn(e.from,e.buffer);break;case L.removeActor:this.processRemoveActor(e.from,e.buffer);break;case L.serverTime:this.processServerTime(e.from,e.buffer);break;case L.actorReady:this.processActorReady(e.from,e.buffer);break;case L.sceneTravelBegin:this.processSceneTravelBegin(e.from,e.buffer);break;case L.sceneTravelReady:this.processSceneTravelReady(e.from,e.buffer);break;case L.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 o=s.getUint32(n,!0);n+=4;const i=s.getUint8(n);n+=1;const a=r===L.rpcBinary||r===L.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=x(t,n,e),n+=e;try{c=this.serializer.decode(d,F)}catch(e){return void console.warn("Failed to parse RPC args JSON",d,e)}}if(!a&&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 h;if(r===L.rpcComponent||r===L.rpcComponentBinary){const e=s.getUint8(n),t=p[u];if(null!=t&&(h=t.find(t=>t.__netid===e)),null==h)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 f=h??p,g=R(f,i);if(null!=g)if("function"==typeof f[g])if(a)f[g](l);else{if(F.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!=h?` and component net id ${h.__netid}`:""));else console.error(`RPC method id ${i} not found on actor net id ${p.__netid}`+(null!=h?` and component net id ${h.__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 o=s.getUint32(n,!0);n+=4;const i=s.getUint8(n);n+=1;let a="";for(let e=0;e<i;e++)a+=String.fromCharCode(s.getUint8(n+e));n+=i;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===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,a)}let p;if(r===L.propComponent){const e=s.getUint8(n),t=d[u];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 h=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(h,a,e),this.callRepNotify(h,a)}).catch(e=>{console.warn(`Failed to decode asset references for replicated property '${a}' on actor id ${o}`,e)});this.applyReplicatedProperty(h,a,n),this.callRepNotify(h,a)}catch(e){console.warn(`Failed to decode replicated property '${a}' 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 s=new DataView(t);n+=1;const r=s.getUint32(n,!0);n+=4;const o=this.world.actors.find(e=>e.__netid===r);if(null==o){return this.getActorRefMissingMessageQueue(r).push({from:e,buffer:t}),void console.debug(`Missing actor ${r}. Queueing property snapshot for later`)}const i=s.getUint16(n,!0);n+=2;const a=[];for(let c=0;c<i;c++){const i=s.getUint8(n);n+=1;let c=o;if(i===O.component){const e=s.getUint8(n);n+=1;const t=o[u];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(i!==O.actor)return void console.error(`Failed to process property snapshot. Unknown target kind ${i}`);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 h=t.slice(n,n+p);n+=p;try{const n=this.decodeReplicatedValue(h);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 ${r}`);return}a.push({target:c,property:d,value:n,requiresAssetLookups:F.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 o=n.getUint8(s);s+=1;let i="";for(let e=0;e<o;e++)i+=String.fromCharCode(n.getUint8(s+e));s+=o,N.set(n.getFloat32(s,!0),n.getFloat32(s+4,!0),n.getFloat32(s+8,!0)),s+=12,j.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 "+i);const d=p(i);if(!d)return void console.error(`Failed to spawn actor: unknown actor type '${i}'`);const h=this.world.actors.find(e=>e.__netid===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(this.spawningActorNetIds.has(r))return void console.log("Actor was already spawned",null!=h);const u=this.findMaterializedNetStartupActor(l,i,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(null==l){this.spawningActorNetIds.add(r);try{const e=await this.world.spawnActorWithNetInfo(d,r,a,c,N,j);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:x(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,L.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),o=this.getConnectionKey(e),i=r?.get(o);if(null!=i){this.sendPropertySnapshot(s,e);const t=i.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(o),0===r.size&&this.actorPreSpawnMessageBuffer.delete(s)}}sendRpc(e,t,n,s,r,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 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!=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=b(d,s);if(null==p)return void console.warn(`RPC function '${s}' is not registered on ${d.constructor?.name??"target"}`);const h=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!=h){let e=10+h.byteLength;l&&(e+=1);const s=new ArrayBuffer(e),r=new DataView(s);let a=0;r.setUint8(a,l?L.rpcComponentBinary:L.rpcBinary),a+=1,r.setUint32(a,i,!0),a+=4,r.setUint8(a,p),a+=1,r.setUint32(a,h.byteLength,!0),a+=4,V(s,a,h),a+=h.byteLength,l&&(r.setUint8(a,o.__netid),a+=1);for(const e of c)null!=e&&this.sendWithQueue(n,{connection:e,reliable:t,buffer:s});return}const u=this.serializer.encode(r),f=D.encode(u);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?L.rpcComponent:L.rpc),v+=1,m.setUint32(v,i,!0),v+=4,m.setUint8(v,p),v+=1,m.setUint16(v,f.byteLength,!0),v+=2,V(w,v,f),v+=f.byteLength,l&&(m.setUint8(v,o.__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,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 a=this.serializer.encode(r),c=D.encode(a),l=c.buffer.slice(c.byteOffset,c.byteOffset+c.byteLength);i[this.getReplicatedPropertyKey(s,o)]={property:s,replOn:e,reliable:t,buffer:l,component:o},this.sendProperty(e,t,n,s,l,o)}sendProperty(e,t,n,s,r,o){if(!this.isServer)return;const i=[],a=this.resolveActorConnection(n);switch(e){case A.all:i.push(...this.session.clients);break;case A.owner:null!=a&&i.push(a);break;case A.notOwner:null==a?i.push(...this.session.clients):i.push(...this.session.clients.filter(e=>e.id!==a.id))}const c=null!=o&&null!=o.__netid;let l=6+s.length+2+r.byteLength;c&&(l+=1);const d=new ArrayBuffer(l),p=new DataView(d);let h=0;p.setUint8(h,c?L.propComponent:L.prop),h+=1,p.setUint32(h,n.__netid,!0),h+=4,p.setUint8(h,s.length),h+=1;for(let e=0;e<s.length;e++)p.setUint8(h++,s.charCodeAt(e));p.setUint16(h,r.byteLength,!0),h+=2,new Uint8Array(d,h,r.byteLength).set(new Uint8Array(r)),h+=r.byteLength,c&&(p.setUint8(h,o.__netid),h+=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,s,r,o){if(null==n||null==s||null==r)return;let i=this.replicatedPropertyRegistry.get(n);null==i&&(i=[],this.replicatedPropertyRegistry.set(n,i));const a=o??(r instanceof h?r:void 0),c=i.find(e=>e.target===r&&e.property===s);if(null!=c)return c.replOn=e,c.reliable=t,void(c.component=a);i.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 o=e.constructor.__actorId??e.constructor.name;const i=D.encode(o);let a;null!=e.__netSceneId&&(a=D.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+i.length+12+12+1+4+2+c,d=new ArrayBuffer(l),p=new DataView(d);let h=0;p.setUint8(h,L.spawn),h+=1,p.setUint32(h,r,!0),h+=4,p.setUint8(h,i.length),h+=1,new Uint8Array(d).set(i,h),h+=i.length,p.setFloat32(h,e.position.x,!0),h+=4,p.setFloat32(h,e.position.y,!0),h+=4,p.setFloat32(h,e.position.z,!0),h+=4,p.setFloat32(h,e.rotation.x,!0),h+=4,p.setFloat32(h,e.rotation.y,!0),h+=4,p.setFloat32(h,e.rotation.z,!0),h+=4;let u=h;h+=1,p.setUint32(h,e.owner?.__netid??0),h+=4,p.setUint16(h,c,!0),h+=2,null!=a&&(V(d,h,a),h+=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(u,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,L.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(L.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(L.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(L.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=D.encode(JSON.stringify(t)),s=new ArrayBuffer(1+n.byteLength);return new DataView(s).setUint8(0,e),V(s,1,n),s}parseSceneTravelMessage(e){const t=e.slice(1),n=null!=k?k.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,L.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===L.prop||t===L.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),o=this.serializer.encode(n),i=D.encode(o),a=i.buffer.slice(i.byteOffset,i.byteOffset+i.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 A.all:return!0;case A.owner:return s?.id===n.id;case A.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=>D.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),o=new DataView(r);let i=0;o.setUint8(i,L.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],a=s[e],c=null!=n.component;o.setUint8(i,c?O.component:O.actor),i+=1,c&&(o.setUint8(i,n.component.__netid),i+=1),o.setUint8(i,a.byteLength),i+=1,new Uint8Array(r,i,a.byteLength).set(a),i+=a.byteLength,o.setUint32(i,n.buffer.byteLength,!0),i+=4,new Uint8Array(r,i,n.buffer.byteLength).set(new Uint8Array(n.buffer)),i+=n.buffer.byteLength}return r}decodeReplicatedValue(e){let t;if(null!=k)t=k.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){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.playerSessions.get(e.playerId);if(null==t)return;return this.playerSessions.attachController(e.playerId,e),void e.setLocallyControlled(null!=t.localPlayerIndex)}if(e instanceof U&&null!=e.playerId){null!=this.playerSessions.get(e.playerId)&&this.playerSessions.attachPlayerState(e.playerId,e)}}callRepNotify(e,t){const n=e[`onRep_${t}`];"function"==typeof n&&n.call(e)}};Q=e([c()],Q);export{Q as NetService};const N=new a,j=new i,D=new TextEncoder,k="undefined"!=typeof TextDecoder?new TextDecoder:void 0;function x(e,t,n){const s=new Uint8Array(e,t,n);if(null!=k)return k.decode(s);let r="";for(let e=0;e<s.length;e++)r+=String.fromCharCode(s[e]);return r}function V(e,t,n){new Uint8Array(e,t,n.byteLength).set(n)}const F=new m;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { BaseActor } from '../actors/actor.js';
|
|
2
|
+
export type PersistenceValue = null | boolean | number | string | PersistenceValue[] | {
|
|
3
|
+
[key: string]: PersistenceValue;
|
|
4
|
+
};
|
|
5
|
+
export type PersistenceIdentity = {
|
|
6
|
+
sceneId: string;
|
|
7
|
+
path: string;
|
|
8
|
+
} | {
|
|
9
|
+
dynamicId: string;
|
|
10
|
+
path: string;
|
|
11
|
+
};
|
|
12
|
+
export type PersistenceContext = {
|
|
13
|
+
readonly identity: PersistenceIdentity;
|
|
14
|
+
readonly version: number;
|
|
15
|
+
readonly persistence: ActorPersistence;
|
|
16
|
+
};
|
|
17
|
+
/** Hooks must return detached JSON data and restore without replaying gameplay events. */
|
|
18
|
+
export interface PersistentState<T extends PersistenceValue = PersistenceValue> {
|
|
19
|
+
readonly persistenceVersion: number;
|
|
20
|
+
onCapturePersistence(): T;
|
|
21
|
+
onRestorePersistence(state: T, context: PersistenceContext): void | Promise<void>;
|
|
22
|
+
onPersistenceRestored?(context: PersistenceContext): void | Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export type PersistenceStateEntry = {
|
|
25
|
+
version: number;
|
|
26
|
+
data: PersistenceValue;
|
|
27
|
+
};
|
|
28
|
+
export type PersistenceRecord = {
|
|
29
|
+
removed?: true;
|
|
30
|
+
actor?: PersistenceStateEntry;
|
|
31
|
+
components?: Record<string, PersistenceStateEntry>;
|
|
32
|
+
};
|
|
33
|
+
export type ActorPersistenceData = {
|
|
34
|
+
version: 1;
|
|
35
|
+
scenes: Record<string, Record<string, PersistenceRecord>>;
|
|
36
|
+
dynamic: Record<string, Record<string, PersistenceRecord>>;
|
|
37
|
+
};
|
|
38
|
+
export type ActorPersistenceConfig = {
|
|
39
|
+
data?: ActorPersistenceData;
|
|
40
|
+
};
|
|
41
|
+
/** Storage-independent, session-scoped state. Disabled unless configured by the runtime. */
|
|
42
|
+
export declare class ActorPersistence {
|
|
43
|
+
private data;
|
|
44
|
+
private readonly actors;
|
|
45
|
+
private readonly identities;
|
|
46
|
+
private readonly restoring;
|
|
47
|
+
private _enabled;
|
|
48
|
+
get enabled(): boolean;
|
|
49
|
+
configure(config: ActorPersistenceConfig): void;
|
|
50
|
+
register(actor: BaseActor, identity: PersistenceIdentity, removalTarget?: PersistenceIdentity): boolean;
|
|
51
|
+
getIdentity(actor: BaseActor): PersistenceIdentity | undefined;
|
|
52
|
+
resolve(identity: PersistenceIdentity): BaseActor | undefined;
|
|
53
|
+
isRemoved(identity: PersistenceIdentity): boolean;
|
|
54
|
+
/** Records the outcome now; the game decides when to remove the visual actor. */
|
|
55
|
+
markRemoved(actor: BaseActor): boolean;
|
|
56
|
+
capture(actor: BaseActor): void;
|
|
57
|
+
captureAll(): void;
|
|
58
|
+
/** Captures synchronously, returning data that remains detached during async storage writes. */
|
|
59
|
+
snapshot(): ActorPersistenceData;
|
|
60
|
+
unregister(actor: BaseActor): void;
|
|
61
|
+
/** All actors must be initialized and registered for lookup before calling this batch. */
|
|
62
|
+
restore(actors: readonly BaseActor[], checkCancelled?: () => void): Promise<void>;
|
|
63
|
+
private participants;
|
|
64
|
+
private records;
|
|
65
|
+
private record;
|
|
66
|
+
private setRecord;
|
|
67
|
+
}
|
|
68
|
+
export declare function assertActorPersistenceData(value: unknown): asserts value is ActorPersistenceData;
|
|
69
|
+
//# sourceMappingURL=actor-persistence.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{__decorate as e}from"tslib";import{Service as t}from"typedi";let n=class{constructor(){this.data={version:1,scenes:{},dynamic:{}},this.actors=new Map,this.identities=new Map,this.restoring=new Set,this._enabled=!1}get enabled(){return this._enabled}configure(e){if(this.actors.size)throw new Error("Cannot reconfigure persistence with registered actors");null!=e.data&&assertActorPersistenceData(e.data),this.data=null==e.data?{version:1,scenes:{},dynamic:{}}:i(e.data),this._enabled=!0}register(e,t,n=t){if(!this.enabled||!e.persistenceEnabled)return!1;s(t),s(n);const i=this.actors.get(e);if(null!=i){if(r(i.identity)!==r(t))throw new Error("Actor already has a persistence identity");return!1}const o=r(t);if(this.identities.has(o))throw new Error(`Duplicate persistence identity ${o}`);if(this.isRemoved(t))throw new Error(`Cannot register removed persistent actor ${o}`);return this.participants(e),this.actors.set(e,{identity:{...t},removalTarget:{...n}}),this.identities.set(o,e),!0}getIdentity(e){const t=this.actors.get(e)?.identity;return null==t?void 0:{...t}}resolve(e){return this.isRemoved(e)?void 0:this.identities.get(r(e))}isRemoved(e){const t=this.records(e);if(null==t)return!1;let n=e.path;for(;;){if(Object.hasOwn(t,n)&&!0===t[n].removed)return!0;if(""===n)return!1;const e=n.lastIndexOf("/");n=e<0?"":n.slice(0,e)}}markRemoved(e){const t=this.actors.get(e);return null!=t&&(this.setRecord(t.removalTarget,{removed:!0}),!0)}capture(e){const t=this.actors.get(e);if(null==t||this.restoring.has(e)||this.isRemoved(t.identity))return;const n=i(this.record(t.identity)??{});for(const[r,s]of this.participants(e))if(null!=s.onCapturePersistence)try{const e={version:s.persistenceVersion??1,data:s.onCapturePersistence()};d(e),""===r?n.actor=i(e):(n.components??(n.components={}),Object.defineProperty(n.components,r,{value:i(e),enumerable:!0,configurable:!0,writable:!0}))}catch(e){throw o("capture",t.identity,r,e)}this.setRecord(t.identity,n)}captureAll(){for(const e of this.actors.keys())this.capture(e)}snapshot(){return this.captureAll(),i(this.data)}unregister(e){const t=this.actors.get(e);null!=t&&(this.capture(e),this.actors.delete(e),this.restoring.delete(e),this.identities.delete(r(t.identity)))}async restore(e,t=()=>{}){for(const t of e)this.actors.has(t)&&this.restoring.add(t);const n=[];for(const r of e){const e=this.actors.get(r);if(null==e)continue;const s=this.record(e.identity);for(const[c,a]of this.participants(r)){t();const r=""===c?s?.actor:null!=s?.components&&Object.hasOwn(s.components,c)?s.components[c]:void 0,d={identity:{...e.identity},version:r?.version??a.persistenceVersion??1,persistence:this};try{null!=r&&null!=a.onRestorePersistence&&await a.onRestorePersistence(i(r.data),d),n.push({participant:a,context:d,key:c})}catch(t){throw o("restore",e.identity,c,t)}}}for(const{participant:e,context:r,key:s}of n){t();try{await(e.onPersistenceRestored?.(r))}catch(e){throw o("post-restore",r.identity,s,e)}}t();for(const t of e)this.restoring.delete(t)}participants(e){const t=[],n=new Set,r=new Set,s=i=>{if(!n.has(i)){n.add(i);for(const o of Object.values(i)){if(null==o||!0!==o.constructor?.__isActorComponent||!0===o.constructor.__onlyEditor||n.has(o))continue;const i=o;if(null!=i.persistenceKey){const n=i.persistenceKey;if(!n||r.has(n))throw new Error(`Duplicate or empty component persistence key '${n}' on ${e.constructor.name}`);r.add(n),t.push([n,i])}s(o)}}};s(e);for(const t of e.attachedComponents)n.has(t)||s({component:t});return t.push(["",e]),t}records(e){const t="sceneId"in e?this.data.scenes:this.data.dynamic,n="sceneId"in e?e.sceneId:e.dynamicId;return Object.hasOwn(t,n)?t[n]:void 0}record(e){const t=this.records(e);return null!=t&&Object.hasOwn(t,e.path)?t[e.path]:void 0}setRecord(e,t){const n="sceneId"in e?this.data.scenes:this.data.dynamic,r="sceneId"in e?e.sceneId:e.dynamicId;Object.hasOwn(n,r)||Object.defineProperty(n,r,{value:{},enumerable:!0,writable:!0,configurable:!0}),Object.defineProperty(n[r],e.path,{value:t,enumerable:!0,writable:!0,configurable:!0})}};n=e([t()],n);export{n as ActorPersistence};function r(e){return JSON.stringify("sceneId"in e?["scene",e.sceneId,e.path]:["dynamic",e.dynamicId,e.path])}function s(e){const t="sceneId"in e?e.sceneId:e.dynamicId;if("string"!=typeof t||!t.trim()||"string"!=typeof e.path)throw new Error("Invalid persistence identity")}function i(e){return structuredClone(e)}function o(e,t,n,s){return new Error(`Persistence ${e} failed for ${r(t)} (${n||"actor"})`,{cause:s})}function c(e){return null!=e&&"object"==typeof e&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||null===Object.getPrototypeOf(e))}function a(e,t=new Set){if(!(null===e||"string"==typeof e||"boolean"==typeof e||"number"==typeof e&&Number.isFinite(e))){if(!Array.isArray(e)&&!c(e)||t.has(e))throw new Error("Persistence state must be finite, acyclic JSON data");t.add(e);for(const n of Object.values(e))a(n,t);t.delete(e)}}function d(e){if(!c(e)||!Number.isInteger(e.version)||e.version<1||!Object.hasOwn(e,"data"))throw new Error("Invalid persistence state entry");a(e.data)}export function assertActorPersistenceData(e){if(!c(e)||1!==e.version||!c(e.scenes)||!c(e.dynamic))throw new Error("Invalid actor persistence document");for(const t of[e.scenes,e.dynamic])for(const e of Object.values(t)){if(!c(e))throw new Error("Invalid persistence scope");for(const t of Object.values(e)){if(!c(t)||void 0!==t.removed&&!0!==t.removed)throw new Error("Invalid persistence record");if(null!=t.actor&&d(t.actor),null!=t.components){if(!c(t.components))throw new Error("Invalid persisted components");for(const e of Object.values(t.components))d(e)}}}a(e)}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -4,7 +4,7 @@ import { BehaviorSubject, Observable } from "rxjs";
|
|
|
4
4
|
import * as THREE from "three";
|
|
5
5
|
import type { CameraActor } from '../../gameplay/actors/builtin/camera-actor.js';
|
|
6
6
|
import { BaseActor } from '../../gameplay/actors/actor.js';
|
|
7
|
-
import { PostProcessVolume } from
|
|
7
|
+
import { PostProcessVolume } from '../actors/builtin/post-process-volume-actor.js';
|
|
8
8
|
import type { PostProcessEffect, PostProcessEffectOptions } from '../../rendering/post-process-effect.js';
|
|
9
9
|
import type { PostProcessMaterialSource } from "../../shader/post-process-shader.js";
|
|
10
10
|
import type { ActiveRenderingBackend, ExperimentalWebGpuRenderGraphExtension } from '../../rendering.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as e}from"tslib";import{Subject as t}from"rxjs";import{SceneRef as i}from"../../scene/scene-ref.js";import{Service as s}from"
|
|
1
|
+
import{__decorate as e}from"tslib";import{Subject as t}from"rxjs";import{SceneRef as i}from"../../scene/scene-ref.js";import{Service as s}from"typedi";let n=class{constructor(){this.transitionedSubject=new t,this.transitioned$=this.transitionedSubject.asObservable()}bind(e){this.handler=e}transitionTo(e){return null==this.handler?Promise.reject(new Error("SceneTransitionService is not attached to an active game session")):this.handler("string"==typeof e?{scene:new i(e,e)}:e instanceof i?{scene:e}:e)}publish(e){this.transitionedSubject.next(e)}dispose(){this.handler=void 0,this.transitionedSubject.complete()}};n=e([s()],n);export{n as SceneTransitionService};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -8,6 +8,7 @@ import type { SceneMaterializer } from "../../scene/materializer.js";
|
|
|
8
8
|
import { PrefabInstance } from '../../scene/objects/prefab.js';
|
|
9
9
|
import { BasePlayerController } from "../actors/controller/actor-controller.js";
|
|
10
10
|
import type { VisualEffect } from '../../effects/vfx/vfx-param.js';
|
|
11
|
+
import type { ActorPersistence } from './actor-persistence.js';
|
|
11
12
|
declare class DirectionalLightController {
|
|
12
13
|
private view;
|
|
13
14
|
constructor(view: RenderingView);
|
|
@@ -26,15 +27,22 @@ declare class DirectionalLightController {
|
|
|
26
27
|
get position(): Vector3;
|
|
27
28
|
}
|
|
28
29
|
export type ActorSpawnOptions = {
|
|
30
|
+
/** Stable, game-assigned identity. Persistent restoration requires the async spawn API. */
|
|
31
|
+
persistenceId?: string;
|
|
29
32
|
owner?: BaseActor;
|
|
30
33
|
/** Overrides actor metadata. Runtime spawns default to scene lifetime. */
|
|
31
34
|
lifetime?: ActorLifetime;
|
|
32
35
|
};
|
|
33
36
|
export type PrefabSpawnOptions = {
|
|
37
|
+
/** Stable identity of this runtime prefab instance. The game owns recreating it. */
|
|
38
|
+
persistenceId?: string;
|
|
34
39
|
/** Applies to the entire prefab, including its main actor and every child actor. */
|
|
35
40
|
lifetime?: ActorLifetime;
|
|
36
41
|
};
|
|
37
42
|
export declare class World {
|
|
43
|
+
/** Session-owned; absent when persistence is disabled. */
|
|
44
|
+
persistence?: ActorPersistence;
|
|
45
|
+
private pendingBeginPlay?;
|
|
38
46
|
private actorFactory;
|
|
39
47
|
private view;
|
|
40
48
|
private viewController;
|
|
@@ -59,7 +67,8 @@ export declare class World {
|
|
|
59
67
|
* It sets isLocallyControlled to true
|
|
60
68
|
*/
|
|
61
69
|
spawnLocalPlayerController<T extends BasePlayerController>(type: Constructable<T>, position?: Vector3, rotation?: Euler): Promise<T>;
|
|
62
|
-
addActor(actor: BaseActor, position?: Vector3, rotation?: Euler, lifetime?: ActorLifetime): void;
|
|
70
|
+
addActor(actor: BaseActor, position?: Vector3, rotation?: Euler, lifetime?: ActorLifetime, deferBeginPlay?: boolean): void;
|
|
71
|
+
private assertPersistentSpawn;
|
|
63
72
|
/**
|
|
64
73
|
* Materialize and compile visual effects before gameplay exposes the action
|
|
65
74
|
* that uses them. Call this at a loading boundary rather than on first use.
|