Implement GLTF overrides
This commit is contained in:
@@ -40,22 +40,54 @@ import Timer = NodeJS.Timer;
|
||||
import { GenericStreamingMessageMessage } from './messages/GenericStreamingMessage';
|
||||
import { LLSDNotationParser } from './llsd/LLSDNotationParser';
|
||||
import { LLSDMap } from './llsd/LLSDMap';
|
||||
import { LLGLTFMaterialOverride, LLGLTFTextureTransformOverride } from './LLGLTFMaterialOverride';
|
||||
import * as Long from 'long';
|
||||
|
||||
export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
protected circuit?: Circuit;
|
||||
protected agent: Agent;
|
||||
protected objects: { [key: number]: GameObject } = {};
|
||||
protected objectsByUUID: { [key: string]: number } = {};
|
||||
protected objectsByParent: { [key: number]: number[] } = {};
|
||||
protected objects = new Map<number, GameObject>();
|
||||
protected objectsByUUID = new Map<string, number>();
|
||||
protected objectsByParent = new Map<number, number[]>();
|
||||
protected clientEvents: ClientEvents;
|
||||
protected options: BotOptionFlags;
|
||||
protected requestedObjects: { [key: number]: boolean } = {};
|
||||
protected fullStore = false;
|
||||
protected requestedObjects = new Set<number>();
|
||||
protected deadObjects: number[] = [];
|
||||
protected persist = false;
|
||||
protected pendingObjectProperties: { [key: string]: any } = {};
|
||||
protected cachedMaterialOverrides = new Map<number, Map<number, LLGLTFMaterialOverride>>();
|
||||
protected pendingObjectProperties = new Map<string, {
|
||||
ObjectID: UUID;
|
||||
CreatorID: UUID;
|
||||
OwnerID: UUID;
|
||||
GroupID: UUID;
|
||||
CreationDate: Long;
|
||||
BaseMask: number;
|
||||
OwnerMask: number;
|
||||
GroupMask: number;
|
||||
EveryoneMask: number;
|
||||
NextOwnerMask: number;
|
||||
OwnershipCost: number;
|
||||
SaleType: number;
|
||||
SalePrice: number;
|
||||
AggregatePerms: number;
|
||||
AggregatePermTextures: number;
|
||||
AggregatePermTexturesOwner: number;
|
||||
Category: number;
|
||||
InventorySerial: number;
|
||||
ItemID: UUID;
|
||||
FolderID: UUID;
|
||||
FromTaskID: UUID;
|
||||
LastOwnerID: UUID;
|
||||
Name: Buffer;
|
||||
Description: Buffer;
|
||||
TouchName: Buffer;
|
||||
SitName: Buffer;
|
||||
TextureID: Buffer;
|
||||
}>;
|
||||
private physicsSubscription: Subscription;
|
||||
private selectedPrimsWithoutUpdate: { [key: number]: boolean } = {};
|
||||
private selectedPrimsWithoutUpdate = new Map<number, boolean>();
|
||||
private selectedChecker?: Timer;
|
||||
|
||||
rtree?: RBush3D;
|
||||
@@ -64,6 +96,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
agent.localID = 0;
|
||||
this.options = options;
|
||||
this.fullStore = false;
|
||||
this.clientEvents = clientEvents;
|
||||
this.circuit = circuit;
|
||||
this.agent = agent;
|
||||
@@ -81,6 +114,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
case Message.GenericStreamingMessage:
|
||||
{
|
||||
if (!this.fullStore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const genMsg = packet.message as GenericStreamingMessageMessage;
|
||||
if (genMsg.MethodData.Method === 0x4175)
|
||||
{
|
||||
@@ -88,16 +125,141 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const result = LLSDNotationParser.parse(genMsg.DataBlock.Data.toString('utf-8'));
|
||||
if (result instanceof LLSDMap)
|
||||
{
|
||||
const arr = result.get('te');
|
||||
if (Array.isArray(arr))
|
||||
const localID = result.get('id');
|
||||
if (typeof localID !== 'number')
|
||||
{
|
||||
if (arr.length === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const tes = result.get('te');
|
||||
const ods = result.get('od');
|
||||
|
||||
// TODO: figure out what to do with this..
|
||||
console.log(JSON.stringify(result, null, 4));
|
||||
const overrides = new Map<number, LLGLTFMaterialOverride>();
|
||||
|
||||
if (Array.isArray(tes) && Array.isArray(ods) && tes.length === ods.length)
|
||||
{
|
||||
for (let x = 0; x < tes.length; x++)
|
||||
{
|
||||
const te = tes[x];
|
||||
if (typeof te !== 'number')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const params = ods[x];
|
||||
if (!(params instanceof LLSDMap))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const textureIDs = params.get('tex');
|
||||
const baseColor = params.get('bc');
|
||||
const emissiveColor = params.get('ec');
|
||||
const metallicFactor = params.get('mf');
|
||||
const roughnessFactor = params.get('rf');
|
||||
const alphaMode = params.get('am');
|
||||
const alphaCutoff = params.get('ac');
|
||||
const doubleSided = params.get('ds');
|
||||
const textureTransforms = params.get('ti');
|
||||
|
||||
const override = new LLGLTFMaterialOverride();
|
||||
overrides.set(te, override);
|
||||
|
||||
if (textureIDs !== undefined && Array.isArray(textureIDs) && textureIDs.length === 4)
|
||||
{
|
||||
override.textures = [];
|
||||
for (const tex of textureIDs)
|
||||
{
|
||||
if (typeof tex === 'string')
|
||||
{
|
||||
override.textures.push(tex);
|
||||
}
|
||||
else if (tex instanceof UUID)
|
||||
{
|
||||
override.textures.push(tex.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNumberArray(array: unknown[]): array is number[]
|
||||
{
|
||||
return array.every(element => typeof element === 'number');
|
||||
}
|
||||
|
||||
if (baseColor !== undefined && Array.isArray(baseColor) && baseColor.length === 4 && isNumberArray(baseColor))
|
||||
{
|
||||
override.baseColor = baseColor;
|
||||
}
|
||||
|
||||
if (emissiveColor !== undefined && Array.isArray(emissiveColor) && emissiveColor.length === 3 && isNumberArray(emissiveColor))
|
||||
{
|
||||
override.emissiveColor = emissiveColor;
|
||||
}
|
||||
|
||||
if (metallicFactor !== undefined && typeof metallicFactor === 'number')
|
||||
{
|
||||
override.metallicFactor = metallicFactor;
|
||||
}
|
||||
|
||||
if (roughnessFactor !== undefined && typeof roughnessFactor === 'number')
|
||||
{
|
||||
override.roughnessFactor = roughnessFactor;
|
||||
}
|
||||
|
||||
if (alphaMode !== undefined && typeof alphaMode === 'number')
|
||||
{
|
||||
override.alphaMode = alphaMode;
|
||||
}
|
||||
|
||||
if (alphaCutoff !== undefined && typeof alphaCutoff === 'number')
|
||||
{
|
||||
override.alphaCutoff = alphaCutoff;
|
||||
}
|
||||
|
||||
if (doubleSided !== undefined && typeof doubleSided === 'boolean')
|
||||
{
|
||||
override.doubleSided = doubleSided;
|
||||
}
|
||||
|
||||
function isLLGLTFTextureTransformOverride(objToCheck: unknown): objToCheck is LLGLTFTextureTransformOverride
|
||||
{
|
||||
const isArrayOfNumbers = (value: unknown): value is number[] =>
|
||||
{
|
||||
return Array.isArray(value) && value.every(item => typeof item === 'number');
|
||||
};
|
||||
|
||||
// Validate the object structure and types
|
||||
return (
|
||||
typeof objToCheck === 'object' &&
|
||||
objToCheck !== null &&
|
||||
'offset' in objToCheck && isArrayOfNumbers((objToCheck as LLGLTFTextureTransformOverride).offset) &&
|
||||
'scale' in objToCheck && isArrayOfNumbers((objToCheck as LLGLTFTextureTransformOverride).scale) &&
|
||||
'rotation' in objToCheck && typeof (objToCheck as LLGLTFTextureTransformOverride).rotation === 'number'
|
||||
);
|
||||
}
|
||||
|
||||
if (textureTransforms !== undefined && Array.isArray(textureTransforms) && textureTransforms.length === 4)
|
||||
{
|
||||
override.textureTransforms = [];
|
||||
for (const transform of textureTransforms)
|
||||
{
|
||||
if (isLLGLTFTextureTransformOverride(transform))
|
||||
{
|
||||
override.textureTransforms.push(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
const obj = this.objects.get(localID);
|
||||
const textureEntry = obj?.TextureEntry;
|
||||
if (textureEntry)
|
||||
{
|
||||
textureEntry.gltfMaterialOverrides = overrides;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cachedMaterialOverrides.set(localID, overrides);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,15 +270,15 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const objProp = packet.message as ObjectPropertiesMessage;
|
||||
for (const obj of objProp.ObjectData)
|
||||
{
|
||||
const obje = this.objectsByUUID[obj.ObjectID.toString()];
|
||||
if (obje !== undefined && this.objects[obje] !== undefined)
|
||||
const obje = this.objectsByUUID.get(obj.ObjectID.toString());
|
||||
const o = this.objects.get(obje ?? 0);
|
||||
if (obje !== undefined && o !== undefined)
|
||||
{
|
||||
const o = this.objects[obje];
|
||||
this.applyObjectProperties(o, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.pendingObjectProperties[obj.ObjectID.toString()] = obj;
|
||||
this.pendingObjectProperties.set(obj.ObjectID.toString(), obj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -156,13 +318,14 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
this.physicsSubscription = this.clientEvents.onPhysicsDataEvent.subscribe((evt: ObjectPhysicsDataEvent) =>
|
||||
{
|
||||
if (this.objects[evt.localID])
|
||||
const obj = this.objects.get(evt.localID);
|
||||
if (obj)
|
||||
{
|
||||
this.objects[evt.localID].physicsShapeType = evt.physicsShapeType;
|
||||
this.objects[evt.localID].density = evt.density;
|
||||
this.objects[evt.localID].restitution = evt.restitution;
|
||||
this.objects[evt.localID].gravityMultiplier = evt.gravityMultiplier;
|
||||
this.objects[evt.localID].friction = evt.friction;
|
||||
obj.physicsShapeType = evt.physicsShapeType;
|
||||
obj.density = evt.density;
|
||||
obj.restitution = evt.restitution;
|
||||
obj.gravityMultiplier = evt.gravityMultiplier;
|
||||
obj.friction = evt.friction;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -224,10 +387,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
private applyObjectProperties(o: GameObject, obj: any): void
|
||||
{
|
||||
if (this.selectedPrimsWithoutUpdate[o.ID])
|
||||
{
|
||||
delete this.selectedPrimsWithoutUpdate[o.ID];
|
||||
}
|
||||
this.selectedPrimsWithoutUpdate.delete(o.ID);
|
||||
// const n = Utils.BufferToStringSimple(obj.Name); // Currently unused
|
||||
o.creatorID = obj.CreatorID;
|
||||
o.creationDate = obj.CreationDate;
|
||||
@@ -278,7 +438,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
protected async requestMissingObject(localID: number, attempt = 0): Promise<void>
|
||||
{
|
||||
if (this.requestedObjects[localID])
|
||||
if (this.requestedObjects.has(localID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -286,7 +446,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.requestedObjects[localID] = true;
|
||||
this.requestedObjects.add(localID);
|
||||
const rmo = new RequestMultipleObjectsMessage();
|
||||
rmo.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
@@ -324,11 +484,11 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
return FilterResponse.NoMatch;
|
||||
});
|
||||
delete this.requestedObjects[localID];
|
||||
this.requestedObjects.delete(localID);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
delete this.requestedObjects[localID];
|
||||
this.requestedObjects.delete(localID);
|
||||
if (attempt < 5)
|
||||
{
|
||||
await this.requestMissingObject(localID, ++attempt);
|
||||
@@ -371,17 +531,19 @@ export class ObjectStoreLite implements IObjectStore
|
||||
let addToParentList = true;
|
||||
let newObject = false;
|
||||
|
||||
if (this.objects[localID])
|
||||
let obj = this.objects.get(localID);
|
||||
if (obj)
|
||||
{
|
||||
if (this.objects[localID].ParentID !== parentID && this.objectsByParent[parentID])
|
||||
const p = this.objectsByParent.get(parentID);
|
||||
if (obj.ParentID !== parentID && p !== undefined)
|
||||
{
|
||||
const ind = this.objectsByParent[parentID].indexOf(localID);
|
||||
const ind = p.indexOf(localID);
|
||||
if (ind !== -1)
|
||||
{
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
p.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
else if (this.objectsByParent[parentID])
|
||||
else if (p)
|
||||
{
|
||||
addToParentList = false;
|
||||
}
|
||||
@@ -389,27 +551,28 @@ export class ObjectStoreLite implements IObjectStore
|
||||
else
|
||||
{
|
||||
newObject = true;
|
||||
this.objects[localID] = new GameObject();
|
||||
this.objects[localID].region = this.agent.currentRegion;
|
||||
const newObj = new GameObject();
|
||||
newObj.region = this.agent.currentRegion;
|
||||
this.objects.set(localID, newObj);
|
||||
}
|
||||
|
||||
const obj = this.objects[localID];
|
||||
obj.deleted = false;
|
||||
obj.ID = objData.ID;
|
||||
obj.FullID = objData.FullID;
|
||||
obj.ParentID = objData.ParentID;
|
||||
obj.OwnerID = objData.OwnerID;
|
||||
obj.PCode = objData.PCode;
|
||||
obj = this.objects.get(localID);
|
||||
obj!.deleted = false;
|
||||
obj!.ID = objData.ID;
|
||||
obj!.FullID = objData.FullID;
|
||||
obj!.ParentID = objData.ParentID;
|
||||
obj!.OwnerID = objData.OwnerID;
|
||||
obj!.PCode = objData.PCode;
|
||||
|
||||
this.objects[localID].NameValue = this.parseNameValues(Utils.BufferToStringSimple(objData.NameValue));
|
||||
obj!.NameValue = this.parseNameValues(Utils.BufferToStringSimple(objData.NameValue));
|
||||
|
||||
this.objects[localID].IsAttachment = this.objects[localID].NameValue['AttachItemID'] !== undefined;
|
||||
if (obj.IsAttachment && obj.State !== undefined)
|
||||
obj!.IsAttachment = obj!.NameValue.AttachItemID !== undefined;
|
||||
if (obj!.IsAttachment && obj!.State !== undefined)
|
||||
{
|
||||
this.objects[localID].attachmentPoint = this.decodeAttachPoint(obj.State);
|
||||
obj!.attachmentPoint = this.decodeAttachPoint(obj!.State);
|
||||
}
|
||||
|
||||
if (objData.PCode === PCode.Avatar && this.objects[localID].FullID.toString() === this.agent.agentID.toString())
|
||||
if (objData.PCode === PCode.Avatar && obj!.FullID.toString() === this.agent.agentID.toString())
|
||||
{
|
||||
this.agent.localID = localID;
|
||||
|
||||
@@ -421,21 +584,25 @@ export class ObjectStoreLite implements IObjectStore
|
||||
if (parent !== this.agent.localID)
|
||||
{
|
||||
let foundAvatars = false;
|
||||
for (const objID of this.objectsByParent[parent])
|
||||
const p = this.objectsByParent.get(parent);
|
||||
if (p !== undefined)
|
||||
{
|
||||
if (this.objects[objID])
|
||||
for (const objID of p)
|
||||
{
|
||||
const o = this.objects[objID];
|
||||
if (o.PCode === PCode.Avatar)
|
||||
const childObj = this.objects.get(objID);
|
||||
if (childObj)
|
||||
{
|
||||
foundAvatars = true;
|
||||
if (childObj.PCode === PCode.Avatar)
|
||||
{
|
||||
foundAvatars = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.objects[parent])
|
||||
const parentObj = this.objects.get(parent);
|
||||
if (parentObj)
|
||||
{
|
||||
const o = this.objects[parent];
|
||||
if (o.PCode === PCode.Avatar)
|
||||
if (parentObj.PCode === PCode.Avatar)
|
||||
{
|
||||
foundAvatars = true;
|
||||
}
|
||||
@@ -449,19 +616,21 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
}
|
||||
|
||||
this.objectsByUUID[objData.FullID.toString()] = localID;
|
||||
if (!this.objectsByParent[parentID])
|
||||
this.objectsByUUID.set(objData.FullID.toString(), localID);
|
||||
let objByParent = this.objectsByParent.get(parentID);
|
||||
if (!objByParent)
|
||||
{
|
||||
this.objectsByParent[parentID] = [];
|
||||
objByParent = [];
|
||||
this.objectsByParent.set(parentID, objByParent);
|
||||
}
|
||||
if (addToParentList)
|
||||
{
|
||||
this.objectsByParent[parentID].push(localID);
|
||||
objByParent.push(localID);
|
||||
}
|
||||
|
||||
if (objData.PCode !== PCode.Avatar && this.options & BotOptionFlags.StoreMyAttachmentsOnly)
|
||||
{
|
||||
if (this.agent.localID !== 0 && obj.ParentID !== this.agent.localID)
|
||||
if (this.agent.localID !== 0 && obj!.ParentID !== this.agent.localID)
|
||||
{
|
||||
// Drop object
|
||||
this.deleteObject(localID);
|
||||
@@ -469,9 +638,9 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
}
|
||||
|
||||
this.notifyObjectUpdate(newObject, obj);
|
||||
this.notifyObjectUpdate(newObject, obj!);
|
||||
|
||||
if (objData.ParentID !== undefined && objData.ParentID !== 0 && !this.objects[objData.ParentID])
|
||||
if (objData.ParentID !== undefined && objData.ParentID !== 0 && !this.objects.get(objData.ParentID))
|
||||
{
|
||||
this.requestMissingObject(objData.ParentID);
|
||||
}
|
||||
@@ -480,7 +649,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
protected notifyTerseUpdate(obj: GameObject): void
|
||||
{
|
||||
if (this.objects[obj.ID])
|
||||
if (this.objects.get(obj.ID))
|
||||
{
|
||||
if (obj.PCode === PCode.Avatar)
|
||||
{
|
||||
@@ -531,15 +700,16 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj.ParentID === 0 || (obj.ParentID !== undefined && this.objects[obj.ParentID] !== undefined && this.objects[obj.ParentID].PCode === PCode.Avatar))
|
||||
const parentObj = this.objects.get(obj.ParentID ?? 0);
|
||||
if (obj.ParentID === 0 || (obj.ParentID !== undefined && parentObj !== undefined && parentObj.PCode === PCode.Avatar))
|
||||
{
|
||||
if (newObject)
|
||||
{
|
||||
if (obj.IsAttachment && obj.ParentID !== undefined)
|
||||
{
|
||||
if (this.objects[obj.ParentID] !== undefined && this.objects[obj.ParentID].PCode === PCode.Avatar)
|
||||
if (parentObj !== undefined && parentObj.PCode === PCode.Avatar)
|
||||
{
|
||||
const avatar = this.agent.currentRegion.agents[this.objects[obj.ParentID].FullID.toString()];
|
||||
const avatar = this.agent.currentRegion.agents[parentObj.FullID.toString()];
|
||||
|
||||
let invItemID = UUID.zero();
|
||||
if (obj.NameValue['AttachItemID'])
|
||||
@@ -581,9 +751,9 @@ export class ObjectStoreLite implements IObjectStore
|
||||
obj.createdSelected = newObj.createSelected;
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
// noinspection JSBitwiseOperatorUsage
|
||||
if (obj.Flags !== undefined && obj.Flags & PrimFlags.CreateSelected && !this.pendingObjectProperties[obj.FullID.toString()])
|
||||
if (obj.Flags !== undefined && obj.Flags & PrimFlags.CreateSelected && !this.pendingObjectProperties.get(obj.FullID.toString()))
|
||||
{
|
||||
this.selectedPrimsWithoutUpdate[obj.ID] = true;
|
||||
this.selectedPrimsWithoutUpdate.set(obj.ID, true);
|
||||
}
|
||||
this.clientEvents.onNewObjectEvent.next(newObj);
|
||||
}
|
||||
@@ -595,10 +765,11 @@ export class ObjectStoreLite implements IObjectStore
|
||||
updObj.object = obj;
|
||||
this.clientEvents.onObjectUpdatedEvent.next(updObj);
|
||||
}
|
||||
if (this.pendingObjectProperties[obj.FullID.toString()])
|
||||
const pendingProp = this.pendingObjectProperties.get(obj.FullID.toString());
|
||||
if (pendingProp)
|
||||
{
|
||||
this.applyObjectProperties(obj, this.pendingObjectProperties[obj.FullID.toString()]);
|
||||
delete this.pendingObjectProperties[obj.FullID.toString()];
|
||||
this.applyObjectProperties(obj, pendingProp);
|
||||
this.pendingObjectProperties.delete(obj.FullID.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -637,18 +808,18 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const localID = buf.readUInt32LE(pos);
|
||||
pos += 4;
|
||||
const pcode = buf.readUInt8(pos++);
|
||||
let newObj = false;
|
||||
if (!this.objects[localID])
|
||||
const newObj = false;
|
||||
let o = this.objects.get(localID);
|
||||
if (!o)
|
||||
{
|
||||
newObj = true;
|
||||
this.objects[localID] = new GameObject();
|
||||
this.objects[localID].region = this.agent.currentRegion;
|
||||
o = new GameObject();
|
||||
o.region = this.agent.currentRegion;
|
||||
this.objects.set(localID, o);
|
||||
}
|
||||
const o = this.objects[localID];
|
||||
o.deleted = false;
|
||||
o.ID = localID;
|
||||
o.PCode = pcode;
|
||||
this.objectsByUUID[fullID.toString()] = localID;
|
||||
this.objectsByUUID.set(fullID.toString(), localID);
|
||||
o.FullID = fullID;
|
||||
|
||||
|
||||
@@ -675,26 +846,29 @@ export class ObjectStoreLite implements IObjectStore
|
||||
let add = true;
|
||||
if (!newObj && o.ParentID !== undefined)
|
||||
{
|
||||
if (newParentID !== o.ParentID)
|
||||
const p = this.objectsByParent.get(o.ParentID);
|
||||
if (newParentID !== o.ParentID && p)
|
||||
{
|
||||
const index = this.objectsByParent[o.ParentID].indexOf(localID);
|
||||
if (index !== -1)
|
||||
const ind = p.indexOf(localID);
|
||||
if (ind !== -1)
|
||||
{
|
||||
this.objectsByParent[o.ParentID].splice(index, 1);
|
||||
p.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
else if (this.objectsByParent[o.ParentID])
|
||||
else if (p)
|
||||
{
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
if (!this.objectsByParent[newParentID])
|
||||
let objByParent = this.objectsByParent.get(newParentID);
|
||||
if (!objByParent)
|
||||
{
|
||||
this.objectsByParent[newParentID] = [];
|
||||
objByParent = [];
|
||||
this.objectsByParent.set(newParentID, objByParent);
|
||||
}
|
||||
this.objectsByParent[newParentID].push(localID);
|
||||
objByParent.push(localID);
|
||||
}
|
||||
if (pcode !== PCode.Avatar && newObj && this.options & BotOptionFlags.StoreMyAttachmentsOnly)
|
||||
{
|
||||
@@ -705,9 +879,12 @@ export class ObjectStoreLite implements IObjectStore
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (o.ParentID !== undefined && o.ParentID !== 0 && !this.objects[o.ParentID])
|
||||
if (o.ParentID !== undefined && o.ParentID !== 0 && !this.objects.has(o.ParentID))
|
||||
{
|
||||
this.requestMissingObject(o.ParentID);
|
||||
this.requestMissingObject(o.ParentID).catch((e) =>
|
||||
{
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
if (compressedflags & CompressedFlags.Tree)
|
||||
{
|
||||
@@ -789,7 +966,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
for (const obj of killObj.ObjectData)
|
||||
{
|
||||
const objectID = obj.ID;
|
||||
if (this.objects[objectID])
|
||||
if (this.objects.has(objectID))
|
||||
{
|
||||
this.deleteObject(objectID);
|
||||
}
|
||||
@@ -811,10 +988,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
deleteObject(objectID: number): void
|
||||
{
|
||||
if (this.objects[objectID])
|
||||
const obj = this.objects.get(objectID);
|
||||
if (obj)
|
||||
{
|
||||
const objectUUID = this.objects[objectID].FullID;
|
||||
const obj = this.objects[objectID];
|
||||
const objectUUID = obj.FullID;
|
||||
obj.deleted = true;
|
||||
|
||||
if (this.persist)
|
||||
@@ -825,9 +1002,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
if (obj.IsAttachment && obj.ParentID !== undefined)
|
||||
{
|
||||
if (this.objects[obj.ParentID] !== undefined && this.objects[obj.ParentID].PCode === PCode.Avatar)
|
||||
const parent = this.objects.get(obj.ParentID);
|
||||
if (parent !== undefined && parent.PCode === PCode.Avatar)
|
||||
{
|
||||
this.agent.currentRegion.agents[this.objects[obj.ParentID].FullID.toString()]?.removeAttachment(obj);
|
||||
this.agent.currentRegion.agents[parent.FullID.toString()]?.removeAttachment(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,31 +1015,31 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
|
||||
// First, kill all children (not the people kind)
|
||||
if (this.objectsByParent[objectID])
|
||||
const objsByParent = this.objectsByParent.get(objectID);
|
||||
if (objsByParent)
|
||||
{
|
||||
for (const childObjID of this.objectsByParent[objectID])
|
||||
for (const childObjID of objsByParent)
|
||||
{
|
||||
this.deleteObject(childObjID);
|
||||
}
|
||||
}
|
||||
delete this.objectsByParent[objectID];
|
||||
this.objectsByParent.delete(objectID);
|
||||
|
||||
// Now delete this object
|
||||
const uuid = obj.FullID.toString();
|
||||
|
||||
if (this.objectsByUUID[uuid])
|
||||
{
|
||||
delete this.objectsByUUID[uuid];
|
||||
}
|
||||
this.objectsByUUID.delete(uuid);
|
||||
|
||||
if (obj.ParentID !== undefined)
|
||||
{
|
||||
const parentID = obj.ParentID;
|
||||
if (this.objectsByParent[parentID])
|
||||
const objsByParentParent = this.objectsByParent.get(parentID);
|
||||
if (objsByParentParent)
|
||||
{
|
||||
const ind = this.objectsByParent[parentID].indexOf(objectID);
|
||||
const ind = objsByParentParent.indexOf(objectID);
|
||||
if (ind !== -1)
|
||||
{
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
objsByParentParent.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -869,12 +1047,13 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
this.rtree.remove(obj.rtreeEntry);
|
||||
}
|
||||
delete this.objects[objectID];
|
||||
this.objects.delete(objectID);
|
||||
this.cachedMaterialOverrides.delete(objectID);
|
||||
}
|
||||
}
|
||||
getObjectsByParent(parentID: number): GameObject[]
|
||||
{
|
||||
const list = this.objectsByParent[parentID];
|
||||
const list = this.objectsByParent.get(parentID);
|
||||
if (list === undefined)
|
||||
{
|
||||
return [];
|
||||
@@ -882,9 +1061,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const result: GameObject[] = [];
|
||||
for (const localID of list)
|
||||
{
|
||||
if (this.objects[localID])
|
||||
const obj = this.objects.get(localID);
|
||||
if (obj)
|
||||
{
|
||||
result.push(this.objects[localID]);
|
||||
result.push(obj);
|
||||
}
|
||||
}
|
||||
result.sort((a: GameObject, b: GameObject) =>
|
||||
@@ -933,25 +1113,26 @@ export class ObjectStoreLite implements IObjectStore
|
||||
delete this.selectedChecker;
|
||||
}
|
||||
this.physicsSubscription.unsubscribe();
|
||||
this.objects = {};
|
||||
this.objects.clear();
|
||||
if (this.rtree)
|
||||
{
|
||||
this.rtree.clear();
|
||||
}
|
||||
this.objectsByUUID = {};
|
||||
this.objectsByParent = {};
|
||||
this.objectsByUUID.clear();
|
||||
this.objectsByParent.clear()
|
||||
delete this.circuit;
|
||||
}
|
||||
|
||||
protected findParent(go: GameObject): GameObject
|
||||
{
|
||||
if (go.ParentID !== undefined && go.ParentID !== 0 && this.objects[go.ParentID])
|
||||
const parentObj = this.objects.get(go.ParentID ?? 0);
|
||||
if (go.ParentID !== undefined && go.ParentID !== 0 && parentObj)
|
||||
{
|
||||
return this.findParent(this.objects[go.ParentID]);
|
||||
return this.findParent(parentObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (go.ParentID !== undefined && go.ParentID !== 0 && !this.objects[go.ParentID])
|
||||
if (go.ParentID !== undefined && go.ParentID !== 0 && !parentObj)
|
||||
{
|
||||
this.requestMissingObject(go.ParentID).catch((e: unknown) =>
|
||||
{
|
||||
@@ -988,10 +1169,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
const results = [];
|
||||
const found: { [key: string]: GameObject } = {};
|
||||
for (const k of Object.keys(this.objects))
|
||||
for (const localID of this.objects.keys())
|
||||
{
|
||||
const go = this.objects[parseInt(k, 10)];
|
||||
if (go.PCode !== PCode.Avatar && (go.IsAttachment === undefined || !go.IsAttachment))
|
||||
const go = this.objects.get(localID);
|
||||
if (go && go.PCode !== PCode.Avatar && (go.IsAttachment === undefined || !go.IsAttachment))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1091,21 +1272,23 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
fullID = fullID.toString();
|
||||
}
|
||||
if (!this.objectsByUUID[fullID])
|
||||
const localID = this.objectsByUUID.get(fullID);
|
||||
const go = this.objects.get(localID ?? 0);
|
||||
if (localID === undefined || go === undefined)
|
||||
{
|
||||
throw new Error('No object found with that UUID');
|
||||
}
|
||||
const localID: number = this.objectsByUUID[fullID];
|
||||
return this.objects[localID];
|
||||
return go;
|
||||
}
|
||||
|
||||
getObjectByLocalID(localID: number): GameObject
|
||||
{
|
||||
if (!this.objects[localID])
|
||||
const go = this.objects.get(localID);
|
||||
if (!go)
|
||||
{
|
||||
throw new Error('No object found with that UUID');
|
||||
}
|
||||
return this.objects[localID];
|
||||
return go;
|
||||
}
|
||||
|
||||
insertIntoRtree(obj: GameObject): void
|
||||
|
||||
Reference in New Issue
Block a user