init commit
This commit is contained in:
123
js/instances/Instance.js
Normal file
123
js/instances/Instance.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import { BO3ScriptSignal } from "../core/BO3ScriptSignal.js";
|
||||
import { guidGenerator } from "../core/util.js";
|
||||
let instanceCounter = 0;
|
||||
|
||||
export class Instance {
|
||||
constructor(className = "Instance") {
|
||||
this.ClassName = className;
|
||||
this.Name = `${className}${instanceCounter++}`;
|
||||
this.Parent = null;
|
||||
this.Children = [];
|
||||
this.InstanceId = guidGenerator(); // Unique identifier, used for replication
|
||||
|
||||
// Signals
|
||||
this.AncestryChanged = new BO3ScriptSignal();
|
||||
this.Changed = new BO3ScriptSignal();
|
||||
}
|
||||
|
||||
// Set a property dynamically
|
||||
SetProperty(prop, value) {
|
||||
this[prop] = value;
|
||||
this.Changed.Fire(prop, value);
|
||||
}
|
||||
|
||||
GetDataModel() { // Recursively calls itself on parent until DataModel is found, since DataModel overrides this method to return itself
|
||||
if (this.Parent) return this.Parent.GetDataModel();
|
||||
}
|
||||
|
||||
FindInstanceRecursively(instanceId) {
|
||||
if (this.InstanceId === instanceId) return this;
|
||||
if (this.Children) {
|
||||
for (const child of this.Children) {
|
||||
if (typeof child.FindInstanceRecursively === 'function') {
|
||||
const found = child.FindInstanceRecursively(instanceId);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
OnReplicated() {
|
||||
// Hook for when instance is replicated to client. Override in subclasses.
|
||||
}
|
||||
|
||||
// Parent management
|
||||
SetParent(newParent) {
|
||||
if (this.Parent === newParent) return;
|
||||
|
||||
// Remove from old parent
|
||||
if (this.Parent) {
|
||||
const oldChildren = this.Parent.Children;
|
||||
this.Parent.Children = oldChildren.filter(c => c !== this);
|
||||
}
|
||||
|
||||
const oldParent = this.Parent;
|
||||
this.Parent = newParent;
|
||||
|
||||
// Add to new parent
|
||||
if (newParent) {
|
||||
newParent.Children.push(this);
|
||||
}
|
||||
|
||||
try {
|
||||
this.AncestryChanged.Fire(this, oldParent);
|
||||
} catch {
|
||||
console.warn("AncestryChanged signal error! May be caused by ReplicatorService during replication.");
|
||||
}
|
||||
if (oldParent === null && newParent !== null && this.OnAddedToScene) {
|
||||
// If added to scene (i.e. RenderService), call hook
|
||||
setTimeout(() => {
|
||||
this.OnAddedToScene(newParent);
|
||||
}, 0); // Defer to avoid issues during replication
|
||||
}
|
||||
}
|
||||
|
||||
// Utility: find first child with name
|
||||
FindFirstChild(name) {
|
||||
return this.Children.find(c => c.Name === name) || null;
|
||||
}
|
||||
|
||||
// Utility: create deep clone
|
||||
Clone() {
|
||||
const clone = new Instance(this.ClassName);
|
||||
for (const key of Object.keys(this)) {
|
||||
if (key !== "Parent" && key !== "Children") {
|
||||
clone[key] = structuredClone(this[key]);
|
||||
}
|
||||
}
|
||||
for (const child of this.Children) {
|
||||
const childClone = child.Clone();
|
||||
childClone.SetParent(clone);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
Destroy() {
|
||||
// Remove from parent
|
||||
const dm = this.GetDataModel(); // Get DataModel for replication service, BEFORE removing parent. otherwise can't find it.
|
||||
this.SetParent(null);
|
||||
// Recursively destroy children
|
||||
for (const child of [...this.Children]) {
|
||||
if (typeof child.Destroy === 'function') {
|
||||
child.Destroy();
|
||||
} else {
|
||||
child.SetParent(null);
|
||||
}
|
||||
}
|
||||
// Clear signals
|
||||
this.AncestryChanged.DisconnectAll();
|
||||
this.Changed.DisconnectAll();
|
||||
// Request replicator to remove this instance if needed
|
||||
if (dm) {
|
||||
const replicator = dm.GetService("ReplicatorService");
|
||||
if (replicator) {
|
||||
replicator.requestReplicateDestroy(this);
|
||||
} else {
|
||||
console.warn("Destroy: No ReplicatorService found in DataModel.");
|
||||
}
|
||||
} else {
|
||||
console.warn("Destroy: No DataModel found in ancestry. Are you destroying an unparented instance?");
|
||||
}
|
||||
} // Garbage collected after this
|
||||
}
|
||||
Reference in New Issue
Block a user