- Implement 3D space rtree-based indexing in the Full ObjectStore
- Implement ScriptDialogReply thanks to Hintswen - Add a rudimentary object search - doesn't work fully yet because of the missing messages
This commit is contained in:
2
dist/classes/GameObjectFull.d.ts
vendored
2
dist/classes/GameObjectFull.d.ts
vendored
@@ -7,7 +7,9 @@ import { Tree } from '../enums/Tree';
|
||||
import { NameValue } from './NameValue';
|
||||
import { IGameObject } from './interfaces/IGameObject';
|
||||
import { SoundFlags } from '..';
|
||||
import { ITreeBoundingBox } from './interfaces/ITreeBoundingBox';
|
||||
export declare class GameObjectFull implements IGameObject {
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
ID: number;
|
||||
State: number;
|
||||
FullID: UUID;
|
||||
|
||||
1
dist/classes/GameObjectFull.js
vendored
1
dist/classes/GameObjectFull.js
vendored
@@ -13,6 +13,7 @@ class GameObjectFull {
|
||||
this.SoundFlags = 0;
|
||||
this.SoundRadius = 1.0;
|
||||
this.SoundGain = 1.0;
|
||||
this.ParentID = 0;
|
||||
}
|
||||
hasNameValueEntry(key) {
|
||||
if (this.NameValue['AttachItemID']) {
|
||||
|
||||
2
dist/classes/GameObjectFull.js.map
vendored
2
dist/classes/GameObjectFull.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"GameObjectFull.js","sourceRoot":"","sources":["../../lib/classes/GameObjectFull.ts"],"names":[],"mappings":";;AAAA,uCAAkC;AAGlC,6CAAwC;AAMxC,MAAa,cAAc;IAqDvB;QAEI,IAAI,CAAC,QAAQ,GAAG,iBAAO,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,uBAAU,CAAC,WAAW,EAAE,CAAC;QACzC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,eAAe,GAAG,iBAAO,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;IACzB,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC;YACI,OAAO,IAAI,CAAC;SACf;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC;YACI,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;SAC/C;QACD,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AAnFD,wCAmFC"}
|
||||
{"version":3,"file":"GameObjectFull.js","sourceRoot":"","sources":["../../lib/classes/GameObjectFull.ts"],"names":[],"mappings":";;AAAA,uCAAkC;AAGlC,6CAAwC;AAOxC,MAAa,cAAc;IAsDvB;QAEI,IAAI,CAAC,QAAQ,GAAG,iBAAO,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,uBAAU,CAAC,WAAW,EAAE,CAAC;QACzC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,eAAe,GAAG,iBAAO,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC;YACI,OAAO,IAAI,CAAC;SACf;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC;YACI,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;SAC/C;QACD,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AArFD,wCAqFC"}
|
||||
2
dist/classes/GameObjectLite.d.ts
vendored
2
dist/classes/GameObjectLite.d.ts
vendored
@@ -2,7 +2,9 @@ import { UUID } from './UUID';
|
||||
import { IGameObject } from './interfaces/IGameObject';
|
||||
import { NameValue } from './NameValue';
|
||||
import { PCode } from '../enums/PCode';
|
||||
import { ITreeBoundingBox } from './interfaces/ITreeBoundingBox';
|
||||
export declare class GameObjectLite implements IGameObject {
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
ID: number;
|
||||
FullID: UUID;
|
||||
ParentID: number;
|
||||
|
||||
2
dist/classes/GameObjectLite.js.map
vendored
2
dist/classes/GameObjectLite.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"GameObjectLite.js","sourceRoot":"","sources":["../../lib/classes/GameObjectLite.ts"],"names":[],"mappings":";;AAKA,MAAa,cAAc;IASvB;QAEI,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC9B,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,SAAS,CAAC;IACxD,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC;YACI,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;SAC/C;QACD,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AA3BD,wCA2BC"}
|
||||
{"version":3,"file":"GameObjectLite.js","sourceRoot":"","sources":["../../lib/classes/GameObjectLite.ts"],"names":[],"mappings":";;AAMA,MAAa,cAAc;IAUvB;QAEI,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC9B,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,SAAS,CAAC;IACxD,CAAC;IAED,iBAAiB,CAAC,GAAW;QAEzB,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC;YACI,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;SAC/C;QACD,OAAO,EAAE,CAAC;IACd,CAAC;CACJ;AA5BD,wCA4BC"}
|
||||
5
dist/classes/ObjectStoreFull.d.ts
vendored
5
dist/classes/ObjectStoreFull.d.ts
vendored
@@ -7,6 +7,7 @@ import { IObjectStore } from './interfaces/IObjectStore';
|
||||
import { GameObjectFull } from './GameObjectFull';
|
||||
import { IGameObject } from './interfaces/IGameObject';
|
||||
import { BotOptionFlags } from '..';
|
||||
import { RBush3D } from 'rbush-3d/dist';
|
||||
export declare class ObjectStoreFull implements IObjectStore {
|
||||
private circuit;
|
||||
private agent;
|
||||
@@ -15,7 +16,9 @@ export declare class ObjectStoreFull implements IObjectStore {
|
||||
private objectsByParent;
|
||||
private clientEvents;
|
||||
private options;
|
||||
rtree: RBush3D;
|
||||
constructor(circuit: Circuit, agent: Agent, clientEvents: ClientEvents, options: BotOptionFlags);
|
||||
insertIntoRtree(obj: GameObjectFull): void;
|
||||
deleteObject(objectID: number): void;
|
||||
readExtraParams(buf: Buffer, pos: number, o: GameObjectFull): number;
|
||||
getObjectsByParent(parentID: number): IGameObject[];
|
||||
@@ -23,4 +26,6 @@ export declare class ObjectStoreFull implements IObjectStore {
|
||||
[key: string]: NameValue;
|
||||
};
|
||||
shutdown(): void;
|
||||
private findParent;
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObjectFull[];
|
||||
}
|
||||
|
||||
232
dist/classes/ObjectStoreFull.js
vendored
232
dist/classes/ObjectStoreFull.js
vendored
@@ -10,12 +10,14 @@ const PCode_1 = require("../enums/PCode");
|
||||
const NameValue_1 = require("./NameValue");
|
||||
const GameObjectFull_1 = require("./GameObjectFull");
|
||||
const __1 = require("..");
|
||||
const dist_1 = require("rbush-3d/dist");
|
||||
class ObjectStoreFull {
|
||||
constructor(circuit, agent, clientEvents, options) {
|
||||
this.objects = {};
|
||||
this.objectsByUUID = {};
|
||||
this.objectsByParent = {};
|
||||
agent.localID = 0;
|
||||
this.rtree = new dist_1.RBush3D();
|
||||
this.options = options;
|
||||
this.clientEvents = clientEvents;
|
||||
this.circuit = circuit;
|
||||
@@ -31,7 +33,7 @@ class ObjectStoreFull {
|
||||
switch (packet.message.id) {
|
||||
case Message_1.Message.ObjectUpdate:
|
||||
const objectUpdate = packet.message;
|
||||
objectUpdate.ObjectData.forEach((objData) => {
|
||||
for (const objData of objectUpdate.ObjectData) {
|
||||
const localID = objData.ID;
|
||||
const parentID = objData.ParentID;
|
||||
let addToParentList = true;
|
||||
@@ -131,13 +133,13 @@ class ObjectStoreFull {
|
||||
if (addToParentList) {
|
||||
this.objectsByParent[parentID].push(localID);
|
||||
}
|
||||
if (objData.PCode !== PCode_1.PCode.Avatar && this.options & __1.BotOptionFlags.StoreMyAttachmentsOnly) {
|
||||
if (this.agent.localID !== 0 && obj.ParentID !== this.agent.localID) {
|
||||
this.deleteObject(localID);
|
||||
return;
|
||||
}
|
||||
if (objData.PCode !== PCode_1.PCode.Avatar && this.options & __1.BotOptionFlags.StoreMyAttachmentsOnly && (this.agent.localID !== 0 && obj.ParentID !== this.agent.localID)) {
|
||||
this.deleteObject(localID);
|
||||
}
|
||||
});
|
||||
else {
|
||||
this.insertIntoRtree(obj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Message_1.Message.ObjectUpdateCached:
|
||||
const objectUpdateCached = packet.message;
|
||||
@@ -158,7 +160,7 @@ class ObjectStoreFull {
|
||||
case Message_1.Message.ObjectUpdateCompressed:
|
||||
{
|
||||
const objectUpdateCompressed = packet.message;
|
||||
objectUpdateCompressed.ObjectData.forEach((obj) => {
|
||||
for (const obj of objectUpdateCompressed.ObjectData) {
|
||||
const flags = obj.UpdateFlags;
|
||||
const buf = obj.Data;
|
||||
let pos = 0;
|
||||
@@ -220,84 +222,85 @@ class ObjectStoreFull {
|
||||
}
|
||||
o.ParentID = newParentID;
|
||||
}
|
||||
if (pcode !== PCode_1.PCode.Avatar && newObj && this.options & __1.BotOptionFlags.StoreMyAttachmentsOnly) {
|
||||
if (this.agent.localID !== 0 && o.ParentID !== this.agent.localID) {
|
||||
this.deleteObject(localID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.Tree) {
|
||||
o.TreeSpecies = buf.readUInt8(pos++);
|
||||
}
|
||||
else if (compressedflags & __1.CompressedFlags.ScratchPad) {
|
||||
o.TreeSpecies = 0;
|
||||
const scratchPadSize = buf.readUInt8(pos++);
|
||||
pos = pos + scratchPadSize;
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.HasText) {
|
||||
const result = Utils_1.Utils.BufferToString(buf, pos);
|
||||
pos += result.readLength;
|
||||
o.Text = result.result;
|
||||
o.TextColor = buf.slice(pos, pos + 4);
|
||||
pos = pos + 4;
|
||||
if (pcode !== PCode_1.PCode.Avatar && newObj && this.options & __1.BotOptionFlags.StoreMyAttachmentsOnly && (this.agent.localID !== 0 && o.ParentID !== this.agent.localID)) {
|
||||
this.deleteObject(localID);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
o.Text = '';
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.MediaURL) {
|
||||
const result = Utils_1.Utils.BufferToString(buf, pos);
|
||||
pos += result.readLength;
|
||||
o.MediaURL = result.result;
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.HasParticles) {
|
||||
pos += 86;
|
||||
}
|
||||
pos = this.readExtraParams(buf, pos, o);
|
||||
if (compressedflags & __1.CompressedFlags.HasSound) {
|
||||
o.Sound = new UUID_1.UUID(buf, pos);
|
||||
pos = pos + 16;
|
||||
o.SoundGain = buf.readFloatLE(pos);
|
||||
pos += 4;
|
||||
o.SoundFlags = buf.readUInt8(pos++);
|
||||
o.SoundRadius = buf.readFloatLE(pos);
|
||||
if (compressedflags & __1.CompressedFlags.Tree) {
|
||||
o.TreeSpecies = buf.readUInt8(pos++);
|
||||
}
|
||||
else if (compressedflags & __1.CompressedFlags.ScratchPad) {
|
||||
o.TreeSpecies = 0;
|
||||
const scratchPadSize = buf.readUInt8(pos++);
|
||||
pos = pos + scratchPadSize;
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.HasText) {
|
||||
const result = Utils_1.Utils.BufferToString(buf, pos);
|
||||
pos += result.readLength;
|
||||
o.Text = result.result;
|
||||
o.TextColor = buf.slice(pos, pos + 4);
|
||||
pos = pos + 4;
|
||||
}
|
||||
else {
|
||||
o.Text = '';
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.MediaURL) {
|
||||
const result = Utils_1.Utils.BufferToString(buf, pos);
|
||||
pos += result.readLength;
|
||||
o.MediaURL = result.result;
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.HasParticles) {
|
||||
pos += 86;
|
||||
}
|
||||
pos = this.readExtraParams(buf, pos, o);
|
||||
if (compressedflags & __1.CompressedFlags.HasSound) {
|
||||
o.Sound = new UUID_1.UUID(buf, pos);
|
||||
pos = pos + 16;
|
||||
o.SoundGain = buf.readFloatLE(pos);
|
||||
pos += 4;
|
||||
o.SoundFlags = buf.readUInt8(pos++);
|
||||
o.SoundRadius = buf.readFloatLE(pos);
|
||||
pos = pos + 4;
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.HasNameValues) {
|
||||
const result = Utils_1.Utils.BufferToString(buf, pos);
|
||||
o.NameValue = this.parseNameValues(result.result);
|
||||
pos += result.readLength;
|
||||
}
|
||||
o.PathCurve = buf.readUInt8(pos++);
|
||||
o.PathBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathScaleX = buf.readUInt8(pos++);
|
||||
o.PathScaleY = buf.readUInt8(pos++);
|
||||
o.PathShearX = buf.readUInt8(pos++);
|
||||
o.PathShearY = buf.readUInt8(pos++);
|
||||
o.PathTwist = buf.readUInt8(pos++);
|
||||
o.PathTwistBegin = buf.readUInt8(pos++);
|
||||
o.PathRadiusOffset = buf.readUInt8(pos++);
|
||||
o.PathTaperX = buf.readUInt8(pos++);
|
||||
o.PathTaperY = buf.readUInt8(pos++);
|
||||
o.PathRevolutions = buf.readUInt8(pos++);
|
||||
o.PathSkew = buf.readUInt8(pos++);
|
||||
o.ProfileCurve = buf.readUInt8(pos++);
|
||||
o.ProfileBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileHollow = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
const textureEntryLength = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
pos = pos + textureEntryLength;
|
||||
if (compressedflags & __1.CompressedFlags.TextureAnimation) {
|
||||
pos = pos + 4;
|
||||
}
|
||||
o.IsAttachment = (compressedflags & __1.CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
this.insertIntoRtree(o);
|
||||
}
|
||||
if (compressedflags & __1.CompressedFlags.HasNameValues) {
|
||||
const result = Utils_1.Utils.BufferToString(buf, pos);
|
||||
o.NameValue = this.parseNameValues(result.result);
|
||||
pos += result.readLength;
|
||||
}
|
||||
o.PathCurve = buf.readUInt8(pos++);
|
||||
o.PathBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathScaleX = buf.readUInt8(pos++);
|
||||
o.PathScaleY = buf.readUInt8(pos++);
|
||||
o.PathShearX = buf.readUInt8(pos++);
|
||||
o.PathShearY = buf.readUInt8(pos++);
|
||||
o.PathTwist = buf.readUInt8(pos++);
|
||||
o.PathTwistBegin = buf.readUInt8(pos++);
|
||||
o.PathRadiusOffset = buf.readUInt8(pos++);
|
||||
o.PathTaperX = buf.readUInt8(pos++);
|
||||
o.PathTaperY = buf.readUInt8(pos++);
|
||||
o.PathRevolutions = buf.readUInt8(pos++);
|
||||
o.PathSkew = buf.readUInt8(pos++);
|
||||
o.ProfileCurve = buf.readUInt8(pos++);
|
||||
o.ProfileBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileHollow = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
const textureEntryLength = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
pos = pos + textureEntryLength;
|
||||
if (compressedflags & __1.CompressedFlags.TextureAnimation) {
|
||||
pos = pos + 4;
|
||||
}
|
||||
o.IsAttachment = (compressedflags & __1.CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message_1.Message.ImprovedTerseObjectUpdate:
|
||||
@@ -309,14 +312,31 @@ class ObjectStoreFull {
|
||||
break;
|
||||
case Message_1.Message.KillObject:
|
||||
const killObj = packet.message;
|
||||
killObj.ObjectData.forEach((obj) => {
|
||||
for (const obj of killObj.ObjectData) {
|
||||
const objectID = obj.ID;
|
||||
this.deleteObject(objectID);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
insertIntoRtree(obj) {
|
||||
if (obj.rtreeEntry !== undefined) {
|
||||
this.rtree.remove(obj.rtreeEntry);
|
||||
}
|
||||
const normalizedScale = obj.Scale.multiplyByQuat(obj.Rotation);
|
||||
const bounds = {
|
||||
minX: obj.Position.x - (normalizedScale.x / 2),
|
||||
maxX: obj.Position.x + (normalizedScale.x / 2),
|
||||
minY: obj.Position.y - (normalizedScale.y / 2),
|
||||
maxY: obj.Position.y + (normalizedScale.y / 2),
|
||||
minZ: obj.Position.z - (normalizedScale.z / 2),
|
||||
maxZ: obj.Position.z + (normalizedScale.z / 2),
|
||||
gameObject: obj
|
||||
};
|
||||
obj.rtreeEntry = bounds;
|
||||
this.rtree.insert(bounds);
|
||||
}
|
||||
deleteObject(objectID) {
|
||||
if (this.objects[objectID]) {
|
||||
if (this.objectsByParent[objectID]) {
|
||||
@@ -337,6 +357,9 @@ class ObjectStoreFull {
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
}
|
||||
}
|
||||
if (this.objects[objectID].rtreeEntry !== undefined) {
|
||||
this.rtree.remove(this.objects[objectID].rtreeEntry);
|
||||
}
|
||||
delete this.objects[objectID];
|
||||
}
|
||||
}
|
||||
@@ -395,9 +418,50 @@ class ObjectStoreFull {
|
||||
}
|
||||
shutdown() {
|
||||
this.objects = {};
|
||||
this.rtree.clear();
|
||||
this.objectsByUUID = {};
|
||||
this.objectsByParent = {};
|
||||
}
|
||||
findParent(go) {
|
||||
if (go.ParentID === 0) {
|
||||
return go;
|
||||
}
|
||||
else {
|
||||
return this.findParent(this.objects[go.ParentID]);
|
||||
}
|
||||
}
|
||||
getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ) {
|
||||
const result = this.rtree.search({
|
||||
minX: minX,
|
||||
maxX: maxX,
|
||||
minY: minY,
|
||||
maxY: maxY,
|
||||
minZ: minZ,
|
||||
maxZ: maxZ
|
||||
});
|
||||
const found = {};
|
||||
const objs = [];
|
||||
for (const obj of result) {
|
||||
const o = obj;
|
||||
const go = o.gameObject;
|
||||
try {
|
||||
const parent = this.findParent(go);
|
||||
const uuid = parent.FullID.toString();
|
||||
if (parent !== go) {
|
||||
console.log('Resolved object ' + go.FullID.toString() + ' to parent ' + parent.FullID.toString() + ' which ' + ((found[uuid] === undefined) ? 'does not exist' : 'already exists'));
|
||||
}
|
||||
if (found[uuid] === undefined) {
|
||||
found[uuid] = parent;
|
||||
objs.push(parent);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Failed to find parent for ' + go.FullID.toString());
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
}
|
||||
exports.ObjectStoreFull = ObjectStoreFull;
|
||||
//# sourceMappingURL=ObjectStoreFull.js.map
|
||||
2
dist/classes/ObjectStoreFull.js.map
vendored
2
dist/classes/ObjectStoreFull.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/classes/ObjectStoreLite.d.ts
vendored
2
dist/classes/ObjectStoreLite.d.ts
vendored
@@ -6,6 +6,7 @@ import { IObjectStore } from './interfaces/IObjectStore';
|
||||
import { GameObjectLite } from './GameObjectLite';
|
||||
import { NameValue } from './NameValue';
|
||||
import { BotOptionFlags } from '..';
|
||||
import { GameObjectFull } from './GameObjectFull';
|
||||
export declare class ObjectStoreLite implements IObjectStore {
|
||||
private circuit;
|
||||
private agent;
|
||||
@@ -22,4 +23,5 @@ export declare class ObjectStoreLite implements IObjectStore {
|
||||
[key: string]: NameValue;
|
||||
};
|
||||
shutdown(): void;
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObjectFull[];
|
||||
}
|
||||
|
||||
8
dist/classes/ObjectStoreLite.js
vendored
8
dist/classes/ObjectStoreLite.js
vendored
@@ -116,7 +116,7 @@ class ObjectStoreLite {
|
||||
case Message_1.Message.ObjectUpdateCompressed:
|
||||
{
|
||||
const objectUpdateCompressed = packet.message;
|
||||
objectUpdateCompressed.ObjectData.forEach((obj) => {
|
||||
for (const obj of objectUpdateCompressed.ObjectData) {
|
||||
const flags = obj.UpdateFlags;
|
||||
const buf = obj.Data;
|
||||
let pos = 0;
|
||||
@@ -223,7 +223,8 @@ class ObjectStoreLite {
|
||||
pos = pos + 4;
|
||||
}
|
||||
o.IsAttachment = (compressedflags & __1.CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
});
|
||||
}
|
||||
;
|
||||
break;
|
||||
}
|
||||
case Message_1.Message.ImprovedTerseObjectUpdate:
|
||||
@@ -324,6 +325,9 @@ class ObjectStoreLite {
|
||||
this.objectsByUUID = {};
|
||||
this.objectsByParent = {};
|
||||
}
|
||||
getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ) {
|
||||
throw new Error('GetObjectsInArea not available with the Lite object store.');
|
||||
}
|
||||
}
|
||||
exports.ObjectStoreLite = ObjectStoreLite;
|
||||
//# sourceMappingURL=ObjectStoreLite.js.map
|
||||
2
dist/classes/ObjectStoreLite.js.map
vendored
2
dist/classes/ObjectStoreLite.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
||||
import { CommandsBase } from './CommandsBase';
|
||||
import { UUID } from '../UUID';
|
||||
import { ChatType } from '../../enums/ChatType';
|
||||
import { ScriptDialogEvent } from '../..';
|
||||
export declare class CommunicationsCommands extends CommandsBase {
|
||||
sendInstantMessage(to: UUID | string, message: string): Promise<void>;
|
||||
nearbyChat(message: string, type: ChatType, channel?: number): Promise<void>;
|
||||
@@ -15,4 +16,5 @@ export declare class CommunicationsCommands extends CommandsBase {
|
||||
typeLocalMessage(message: string, thinkingTime?: number, charactersPerSecond?: number): Promise<void>;
|
||||
startGroupChatSession(sessionID: UUID | string, message: string): Promise<void>;
|
||||
sendGroupMessage(groupID: UUID | string, message: string): Promise<number>;
|
||||
respondToScriptDialog(event: ScriptDialogEvent, buttonIndex: number): Promise<void>;
|
||||
}
|
||||
|
||||
16
dist/classes/commands/CommunicationsCommands.js
vendored
16
dist/classes/commands/CommunicationsCommands.js
vendored
@@ -17,6 +17,7 @@ const ChatFromViewer_1 = require("../messages/ChatFromViewer");
|
||||
const ChatType_1 = require("../../enums/ChatType");
|
||||
const InstantMessageDialog_1 = require("../../enums/InstantMessageDialog");
|
||||
const __1 = require("../..");
|
||||
const ScriptDialogReply_1 = require("../messages/ScriptDialogReply");
|
||||
class CommunicationsCommands extends CommandsBase_1.CommandsBase {
|
||||
sendInstantMessage(to, message) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
@@ -350,6 +351,21 @@ class CommunicationsCommands extends CommandsBase_1.CommandsBase {
|
||||
});
|
||||
});
|
||||
}
|
||||
respondToScriptDialog(event, buttonIndex) {
|
||||
const dialog = new ScriptDialogReply_1.ScriptDialogReplyMessage();
|
||||
dialog.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
dialog.Data = {
|
||||
ObjectID: event.ObjectID,
|
||||
ChatChannel: event.ChatChannel,
|
||||
ButtonIndex: buttonIndex,
|
||||
ButtonLabel: Utils_1.Utils.StringToBuffer(event.Buttons[buttonIndex])
|
||||
};
|
||||
const sequenceNo = this.circuit.sendMessage(dialog, __1.PacketFlags.Reliable);
|
||||
return this.circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
}
|
||||
exports.CommunicationsCommands = CommunicationsCommands;
|
||||
//# sourceMappingURL=CommunicationsCommands.js.map
|
||||
File diff suppressed because one or more lines are too long
2
dist/classes/commands/RegionCommands.d.ts
vendored
2
dist/classes/commands/RegionCommands.d.ts
vendored
@@ -1,6 +1,8 @@
|
||||
import { CommandsBase } from './CommandsBase';
|
||||
import { UUID } from '../UUID';
|
||||
import * as Long from 'long';
|
||||
import { IGameObject } from '../interfaces/IGameObject';
|
||||
export declare class RegionCommands extends CommandsBase {
|
||||
getRegionHandle(regionID: UUID): Promise<Long>;
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): IGameObject[];
|
||||
}
|
||||
|
||||
3
dist/classes/commands/RegionCommands.js
vendored
3
dist/classes/commands/RegionCommands.js
vendored
@@ -26,6 +26,9 @@ class RegionCommands extends CommandsBase_1.CommandsBase {
|
||||
});
|
||||
});
|
||||
}
|
||||
getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ) {
|
||||
return this.currentRegion.objects.getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ);
|
||||
}
|
||||
}
|
||||
exports.RegionCommands = RegionCommands;
|
||||
//# sourceMappingURL=RegionCommands.js.map
|
||||
2
dist/classes/commands/RegionCommands.js.map
vendored
2
dist/classes/commands/RegionCommands.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"RegionCommands.js","sourceRoot":"","sources":["../../../lib/classes/commands/RegionCommands.ts"],"names":[],"mappings":";;AAAA,iDAA4C;AAG5C,yEAA2E;AAC3E,iDAA4C;AAC5C,+DAA0D;AAE1D,6BAAkC;AAElC,MAAa,cAAe,SAAQ,2BAAY;IAE5C,eAAe,CAAC,QAAc;QAE1B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAEzC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;YAC3C,MAAM,GAAG,GAA+B,IAAI,gDAA0B,EAAE,CAAC;YACzE,GAAG,CAAC,YAAY,GAAG;gBACf,QAAQ,EAAE,QAAQ;aACrB,CAAC;YACF,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,eAAW,CAAC,QAAQ,CAAC,CAAC;YAC/C,OAAO,CAAC,cAAc,CAAgC,iBAAO,CAAC,sBAAsB,EAAE,KAAK,EAAE,CAAC,SAAwC,EAAkB,EAAE;gBAEtJ,IAAI,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,QAAQ,CAAC,QAAQ,EAAE,EACpE;oBACI,OAAO,+BAAc,CAAC,MAAM,CAAC;iBAChC;qBAED;oBACI,OAAO,+BAAc,CAAC,OAAO,CAAC;iBACjC;YACL,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAA0C,EAAE,EAAE;gBAEnD,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACjD,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ;AA5BD,wCA4BC"}
|
||||
{"version":3,"file":"RegionCommands.js","sourceRoot":"","sources":["../../../lib/classes/commands/RegionCommands.ts"],"names":[],"mappings":";;AAAA,iDAA4C;AAG5C,yEAA2E;AAC3E,iDAA4C;AAC5C,+DAA0D;AAE1D,6BAAkC;AAGlC,MAAa,cAAe,SAAQ,2BAAY;IAE5C,eAAe,CAAC,QAAc;QAE1B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAEzC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;YAC3C,MAAM,GAAG,GAA+B,IAAI,gDAA0B,EAAE,CAAC;YACzE,GAAG,CAAC,YAAY,GAAG;gBACf,QAAQ,EAAE,QAAQ;aACrB,CAAC;YACF,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,eAAW,CAAC,QAAQ,CAAC,CAAC;YAC/C,OAAO,CAAC,cAAc,CAAgC,iBAAO,CAAC,sBAAsB,EAAE,KAAK,EAAE,CAAC,SAAwC,EAAkB,EAAE;gBAEtJ,IAAI,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,QAAQ,CAAC,QAAQ,EAAE,EACpE;oBACI,OAAO,+BAAc,CAAC,MAAM,CAAC;iBAChC;qBAED;oBACI,OAAO,+BAAc,CAAC,OAAO,CAAC;iBACjC;YACL,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAA0C,EAAE,EAAE;gBAEnD,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACjD,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IACD,gBAAgB,CAAC,IAAY,EAAE,IAAY,EAAE,IAAY,EAAE,IAAY,EAAE,IAAY,EAAE,IAAY;QAE/F,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3F,CAAC;CACJ;AAhCD,wCAgCC"}
|
||||
2
dist/classes/interfaces/IGameObject.d.ts
vendored
2
dist/classes/interfaces/IGameObject.d.ts
vendored
@@ -1,4 +1,6 @@
|
||||
import { ITreeBoundingBox } from './ITreeBoundingBox';
|
||||
export interface IGameObject {
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
hasNameValueEntry(key: string): boolean;
|
||||
getNameValueEntry(key: string): string;
|
||||
}
|
||||
|
||||
4
dist/classes/interfaces/IObjectStore.d.ts
vendored
4
dist/classes/interfaces/IObjectStore.d.ts
vendored
@@ -1,5 +1,9 @@
|
||||
import { IGameObject } from './IGameObject';
|
||||
import { RBush3D } from 'rbush-3d/dist';
|
||||
import { GameObjectFull } from '../GameObjectFull';
|
||||
export interface IObjectStore {
|
||||
rtree?: RBush3D;
|
||||
getObjectsByParent(parentID: number): IGameObject[];
|
||||
shutdown(): void;
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObjectFull[];
|
||||
}
|
||||
|
||||
5
dist/classes/interfaces/ITreeBoundingBox.d.ts
vendored
Normal file
5
dist/classes/interfaces/ITreeBoundingBox.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import { BBox } from 'rbush-3d/dist';
|
||||
import { IGameObject } from './IGameObject';
|
||||
export interface ITreeBoundingBox extends BBox {
|
||||
gameObject: IGameObject;
|
||||
}
|
||||
3
dist/classes/interfaces/ITreeBoundingBox.js
vendored
Normal file
3
dist/classes/interfaces/ITreeBoundingBox.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=ITreeBoundingBox.js.map
|
||||
1
dist/classes/interfaces/ITreeBoundingBox.js.map
vendored
Normal file
1
dist/classes/interfaces/ITreeBoundingBox.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ITreeBoundingBox.js","sourceRoot":"","sources":["../../../lib/classes/interfaces/ITreeBoundingBox.ts"],"names":[],"mappings":""}
|
||||
@@ -17,9 +17,15 @@ loginParameters.start = parameters.start;
|
||||
//const options = nmv.BotOptionFlags.None;
|
||||
|
||||
// If you don't intend to use the object store (i.e you have no interest in inworld objects, textures, etc,
|
||||
// using None or LiteObjectStore will drastically reduce the footprint
|
||||
// using nmv.BotOptionFlags.LiteObjectStore will drastically reduce the footprint and CPU usage.
|
||||
//
|
||||
const options = nmv.BotOptionFlags.LiteObjectStore | nmv.BotOptionFlags.StoreMyAttachmentsOnly;
|
||||
// The full object store has a full searchable rtree index, the lite does not.
|
||||
//
|
||||
// For the minimum footprint, use :
|
||||
//
|
||||
// const options = nmv.BotOptionFlags.LiteObjectStore | nmv.BotOptionFlags.StoreMyAttachmentsOnly;
|
||||
|
||||
const options = nmv.BotOptionFlags.None;
|
||||
|
||||
const bot = new nmv.Bot(loginParameters, options);
|
||||
|
||||
@@ -281,6 +287,18 @@ async function connect()
|
||||
console.log('Map location request failed. You probably do not have map rights on Casper.');
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
console.log('Finding objects');
|
||||
var tmr = new Date().getTime();
|
||||
const objs = bot.clientCommands.region.getObjectsInArea(191, 192, 56, 57, 144, 145);
|
||||
var tmr2 = new Date().getTime();
|
||||
console.log('Found ' + objs.length + ' objects in ' + (tmr2 - tmr) + 'ms');
|
||||
for (const o of objs)
|
||||
{
|
||||
console.log(o.FullID.toString());
|
||||
}
|
||||
}, 10000)
|
||||
|
||||
//await bot.clientCommands.friends.grantFriendRights('d1cd5b71-6209-4595-9bf0-771bf689ce00', nmv.RightsFlags.CanModifyObjects | nmv.RightsFlags.CanSeeOnline | nmv.RightsFlags.CanSeeOnMap );
|
||||
}
|
||||
catch (error)
|
||||
|
||||
@@ -6,9 +6,11 @@ import {Tree} from '../enums/Tree';
|
||||
import {NameValue} from './NameValue';
|
||||
import {IGameObject} from './interfaces/IGameObject';
|
||||
import {SoundFlags} from '..';
|
||||
import {ITreeBoundingBox} from './interfaces/ITreeBoundingBox';
|
||||
|
||||
export class GameObjectFull implements IGameObject
|
||||
{
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
ID: number;
|
||||
State: number;
|
||||
FullID: UUID;
|
||||
@@ -71,6 +73,7 @@ export class GameObjectFull implements IGameObject
|
||||
this.SoundFlags = 0;
|
||||
this.SoundRadius = 1.0;
|
||||
this.SoundGain = 1.0;
|
||||
this.ParentID = 0;
|
||||
}
|
||||
|
||||
hasNameValueEntry(key: string): boolean
|
||||
|
||||
@@ -2,9 +2,11 @@ import {UUID} from './UUID';
|
||||
import {IGameObject} from './interfaces/IGameObject';
|
||||
import {NameValue} from './NameValue';
|
||||
import {PCode} from '../enums/PCode';
|
||||
import {ITreeBoundingBox} from './interfaces/ITreeBoundingBox';
|
||||
|
||||
export class GameObjectLite implements IGameObject
|
||||
{
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
ID: number;
|
||||
FullID: UUID;
|
||||
ParentID: number;
|
||||
|
||||
@@ -21,6 +21,9 @@ import {IObjectStore} from './interfaces/IObjectStore';
|
||||
import {GameObjectFull} from './GameObjectFull';
|
||||
import {IGameObject} from './interfaces/IGameObject';
|
||||
import {BotOptionFlags, CompressedFlags} from '..';
|
||||
import {BBox, RBush3D} from 'rbush-3d/dist';
|
||||
import {ITreeBoundingBox} from './interfaces/ITreeBoundingBox';
|
||||
import {GameObjectLite} from './GameObjectLite';
|
||||
|
||||
export class ObjectStoreFull implements IObjectStore
|
||||
{
|
||||
@@ -31,10 +34,12 @@ export class ObjectStoreFull implements IObjectStore
|
||||
private objectsByParent: { [key: number]: number[] } = {};
|
||||
private clientEvents: ClientEvents;
|
||||
private options: BotOptionFlags;
|
||||
rtree: RBush3D;
|
||||
|
||||
constructor(circuit: Circuit, agent: Agent, clientEvents: ClientEvents, options: BotOptionFlags)
|
||||
{
|
||||
agent.localID = 0;
|
||||
this.rtree = new RBush3D();
|
||||
this.options = options;
|
||||
this.clientEvents = clientEvents;
|
||||
this.circuit = circuit;
|
||||
@@ -52,7 +57,7 @@ export class ObjectStoreFull implements IObjectStore
|
||||
{
|
||||
case Message.ObjectUpdate:
|
||||
const objectUpdate = packet.message as ObjectUpdateMessage;
|
||||
objectUpdate.ObjectData.forEach((objData) =>
|
||||
for (const objData of objectUpdate.ObjectData)
|
||||
{
|
||||
const localID = objData.ID;
|
||||
const parentID = objData.ParentID;
|
||||
@@ -177,16 +182,16 @@ export class ObjectStoreFull implements IObjectStore
|
||||
this.objectsByParent[parentID].push(localID);
|
||||
}
|
||||
|
||||
if (objData.PCode !== PCode.Avatar && this.options & BotOptionFlags.StoreMyAttachmentsOnly)
|
||||
if (objData.PCode !== PCode.Avatar && this.options & BotOptionFlags.StoreMyAttachmentsOnly && (this.agent.localID !== 0 && obj.ParentID !== this.agent.localID))
|
||||
{
|
||||
if (this.agent.localID !== 0 && obj.ParentID !== this.agent.localID)
|
||||
{
|
||||
// Drop object
|
||||
this.deleteObject(localID);
|
||||
return;
|
||||
}
|
||||
// Drop object
|
||||
this.deleteObject(localID);
|
||||
}
|
||||
});
|
||||
else
|
||||
{
|
||||
this.insertIntoRtree(obj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Message.ObjectUpdateCached:
|
||||
const objectUpdateCached = packet.message as ObjectUpdateCachedMessage;
|
||||
@@ -208,7 +213,7 @@ export class ObjectStoreFull implements IObjectStore
|
||||
case Message.ObjectUpdateCompressed:
|
||||
{
|
||||
const objectUpdateCompressed = packet.message as ObjectUpdateCompressedMessage;
|
||||
objectUpdateCompressed.ObjectData.forEach((obj) =>
|
||||
for (const obj of objectUpdateCompressed.ObjectData)
|
||||
{
|
||||
const flags = obj.UpdateFlags;
|
||||
const buf = obj.Data;
|
||||
@@ -282,115 +287,115 @@ export class ObjectStoreFull implements IObjectStore
|
||||
}
|
||||
o.ParentID = newParentID;
|
||||
}
|
||||
if (pcode !== PCode.Avatar && newObj && this.options & BotOptionFlags.StoreMyAttachmentsOnly)
|
||||
if (pcode !== PCode.Avatar && newObj && this.options & BotOptionFlags.StoreMyAttachmentsOnly && (this.agent.localID !== 0 && o.ParentID !== this.agent.localID))
|
||||
{
|
||||
if (this.agent.localID !== 0 && o.ParentID !== this.agent.localID)
|
||||
{
|
||||
// Drop object
|
||||
this.deleteObject(localID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (compressedflags & CompressedFlags.Tree)
|
||||
{
|
||||
o.TreeSpecies = buf.readUInt8(pos++);
|
||||
}
|
||||
else if (compressedflags & CompressedFlags.ScratchPad)
|
||||
{
|
||||
o.TreeSpecies = 0;
|
||||
const scratchPadSize = buf.readUInt8(pos++);
|
||||
// Ignore this data
|
||||
pos = pos + scratchPadSize;
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasText)
|
||||
{
|
||||
// Read null terminated string
|
||||
const result = Utils.BufferToString(buf, pos);
|
||||
|
||||
pos += result.readLength;
|
||||
o.Text = result.result;
|
||||
o.TextColor = buf.slice(pos, pos + 4);
|
||||
pos = pos + 4;
|
||||
// Drop object
|
||||
this.deleteObject(localID);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
o.Text = '';
|
||||
}
|
||||
if (compressedflags & CompressedFlags.MediaURL)
|
||||
{
|
||||
const result = Utils.BufferToString(buf, pos);
|
||||
if (compressedflags & CompressedFlags.Tree)
|
||||
{
|
||||
o.TreeSpecies = buf.readUInt8(pos++);
|
||||
}
|
||||
else if (compressedflags & CompressedFlags.ScratchPad)
|
||||
{
|
||||
o.TreeSpecies = 0;
|
||||
const scratchPadSize = buf.readUInt8(pos++);
|
||||
// Ignore this data
|
||||
pos = pos + scratchPadSize;
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasText)
|
||||
{
|
||||
// Read null terminated string
|
||||
const result = Utils.BufferToString(buf, pos);
|
||||
|
||||
pos += result.readLength;
|
||||
o.MediaURL = result.result;
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasParticles)
|
||||
{
|
||||
// TODO: Particle system block
|
||||
pos += 86;
|
||||
}
|
||||
pos += result.readLength;
|
||||
o.Text = result.result;
|
||||
o.TextColor = buf.slice(pos, pos + 4);
|
||||
pos = pos + 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
o.Text = '';
|
||||
}
|
||||
if (compressedflags & CompressedFlags.MediaURL)
|
||||
{
|
||||
const result = Utils.BufferToString(buf, pos);
|
||||
|
||||
// Extra params
|
||||
pos = this.readExtraParams(buf, pos, o);
|
||||
pos += result.readLength;
|
||||
o.MediaURL = result.result;
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasParticles)
|
||||
{
|
||||
// TODO: Particle system block
|
||||
pos += 86;
|
||||
}
|
||||
|
||||
if (compressedflags & CompressedFlags.HasSound)
|
||||
{
|
||||
o.Sound = new UUID(buf, pos);
|
||||
pos = pos + 16;
|
||||
o.SoundGain = buf.readFloatLE(pos);
|
||||
pos += 4;
|
||||
o.SoundFlags = buf.readUInt8(pos++);
|
||||
o.SoundRadius = buf.readFloatLE(pos);
|
||||
// Extra params
|
||||
pos = this.readExtraParams(buf, pos, o);
|
||||
|
||||
if (compressedflags & CompressedFlags.HasSound)
|
||||
{
|
||||
o.Sound = new UUID(buf, pos);
|
||||
pos = pos + 16;
|
||||
o.SoundGain = buf.readFloatLE(pos);
|
||||
pos += 4;
|
||||
o.SoundFlags = buf.readUInt8(pos++);
|
||||
o.SoundRadius = buf.readFloatLE(pos);
|
||||
pos = pos + 4;
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasNameValues)
|
||||
{
|
||||
const result = Utils.BufferToString(buf, pos);
|
||||
o.NameValue = this.parseNameValues(result.result);
|
||||
pos += result.readLength;
|
||||
}
|
||||
o.PathCurve = buf.readUInt8(pos++);
|
||||
o.PathBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathScaleX = buf.readUInt8(pos++);
|
||||
o.PathScaleY = buf.readUInt8(pos++);
|
||||
o.PathShearX = buf.readUInt8(pos++);
|
||||
o.PathShearY = buf.readUInt8(pos++);
|
||||
o.PathTwist = buf.readUInt8(pos++);
|
||||
o.PathTwistBegin = buf.readUInt8(pos++);
|
||||
o.PathRadiusOffset = buf.readUInt8(pos++);
|
||||
o.PathTaperX = buf.readUInt8(pos++);
|
||||
o.PathTaperY = buf.readUInt8(pos++);
|
||||
o.PathRevolutions = buf.readUInt8(pos++);
|
||||
o.PathSkew = buf.readUInt8(pos++);
|
||||
o.ProfileCurve = buf.readUInt8(pos++);
|
||||
o.ProfileBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileHollow = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
const textureEntryLength = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
// TODO: Properly parse textureentry;
|
||||
pos = pos + textureEntryLength;
|
||||
|
||||
if (compressedflags & CompressedFlags.TextureAnimation)
|
||||
{
|
||||
// TODO: Properly parse textureAnim
|
||||
pos = pos + 4;
|
||||
}
|
||||
|
||||
o.IsAttachment = (compressedflags & CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
|
||||
this.insertIntoRtree(o);
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasNameValues)
|
||||
{
|
||||
const result = Utils.BufferToString(buf, pos);
|
||||
o.NameValue = this.parseNameValues(result.result);
|
||||
pos += result.readLength;
|
||||
}
|
||||
o.PathCurve = buf.readUInt8(pos++);
|
||||
o.PathBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.PathScaleX = buf.readUInt8(pos++);
|
||||
o.PathScaleY = buf.readUInt8(pos++);
|
||||
o.PathShearX = buf.readUInt8(pos++);
|
||||
o.PathShearY = buf.readUInt8(pos++);
|
||||
o.PathTwist = buf.readUInt8(pos++);
|
||||
o.PathTwistBegin = buf.readUInt8(pos++);
|
||||
o.PathRadiusOffset = buf.readUInt8(pos++);
|
||||
o.PathTaperX = buf.readUInt8(pos++);
|
||||
o.PathTaperY = buf.readUInt8(pos++);
|
||||
o.PathRevolutions = buf.readUInt8(pos++);
|
||||
o.PathSkew = buf.readUInt8(pos++);
|
||||
o.ProfileCurve = buf.readUInt8(pos++);
|
||||
o.ProfileBegin = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileEnd = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
o.ProfileHollow = buf.readUInt16LE(pos);
|
||||
pos = pos + 2;
|
||||
const textureEntryLength = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
// TODO: Properly parse textureentry;
|
||||
pos = pos + textureEntryLength;
|
||||
|
||||
if (compressedflags & CompressedFlags.TextureAnimation)
|
||||
{
|
||||
// TODO: Properly parse textureAnim
|
||||
pos = pos + 4;
|
||||
}
|
||||
|
||||
o.IsAttachment = (compressedflags & CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message.ImprovedTerseObjectUpdate:
|
||||
const objectUpdateTerse = packet.message as ImprovedTerseObjectUpdateMessage;
|
||||
// TODO: ImprovedTerseObjectUPdate
|
||||
// TODO: ImprovedTerseObjectUpdate
|
||||
break;
|
||||
case Message.MultipleObjectUpdate:
|
||||
const multipleObjectUpdate = packet.message as MultipleObjectUpdateMessage;
|
||||
@@ -399,16 +404,37 @@ export class ObjectStoreFull implements IObjectStore
|
||||
break;
|
||||
case Message.KillObject:
|
||||
const killObj = packet.message as KillObjectMessage;
|
||||
killObj.ObjectData.forEach((obj) =>
|
||||
for (const obj of killObj.ObjectData)
|
||||
{
|
||||
const objectID = obj.ID;
|
||||
this.deleteObject(objectID);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
insertIntoRtree(obj: GameObjectFull)
|
||||
{
|
||||
if (obj.rtreeEntry !== undefined)
|
||||
{
|
||||
this.rtree.remove(obj.rtreeEntry);
|
||||
}
|
||||
const normalizedScale = obj.Scale.multiplyByQuat(obj.Rotation);
|
||||
const bounds: ITreeBoundingBox = {
|
||||
minX: obj.Position.x - (normalizedScale.x / 2),
|
||||
maxX: obj.Position.x + (normalizedScale.x / 2),
|
||||
minY: obj.Position.y - (normalizedScale.y / 2),
|
||||
maxY: obj.Position.y + (normalizedScale.y / 2),
|
||||
minZ: obj.Position.z - (normalizedScale.z / 2),
|
||||
maxZ: obj.Position.z + (normalizedScale.z / 2),
|
||||
gameObject: obj
|
||||
};
|
||||
|
||||
obj.rtreeEntry = bounds;
|
||||
this.rtree.insert(bounds);
|
||||
}
|
||||
|
||||
deleteObject(objectID: number)
|
||||
{
|
||||
if (this.objects[objectID])
|
||||
@@ -440,6 +466,10 @@ export class ObjectStoreFull implements IObjectStore
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
}
|
||||
}
|
||||
if (this.objects[objectID].rtreeEntry !== undefined)
|
||||
{
|
||||
this.rtree.remove(this.objects[objectID].rtreeEntry);
|
||||
}
|
||||
delete this.objects[objectID];
|
||||
}
|
||||
}
|
||||
@@ -518,7 +548,63 @@ export class ObjectStoreFull implements IObjectStore
|
||||
shutdown()
|
||||
{
|
||||
this.objects = {};
|
||||
this.rtree.clear();
|
||||
this.objectsByUUID = {};
|
||||
this.objectsByParent = {};
|
||||
}
|
||||
|
||||
private findParent(go: GameObjectFull): GameObjectFull
|
||||
{
|
||||
if (go.ParentID === 0)
|
||||
{
|
||||
return go;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.findParent(this.objects[go.ParentID]);
|
||||
}
|
||||
}
|
||||
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObjectFull[]
|
||||
{
|
||||
const result = this.rtree.search({
|
||||
minX: minX,
|
||||
maxX: maxX,
|
||||
minY: minY,
|
||||
maxY: maxY,
|
||||
minZ: minZ,
|
||||
maxZ: maxZ
|
||||
});
|
||||
const found: {[key: string]: GameObjectFull} = {};
|
||||
const objs: GameObjectFull[] = [];
|
||||
for (const obj of result)
|
||||
{
|
||||
const o = obj as ITreeBoundingBox;
|
||||
const go = o.gameObject as GameObjectFull;
|
||||
try
|
||||
{
|
||||
const parent = this.findParent(go);
|
||||
const uuid = parent.FullID.toString();
|
||||
|
||||
if (parent !== go)
|
||||
{
|
||||
console.log('Resolved object ' + go.FullID.toString() + ' to parent ' + parent.FullID.toString() + ' which ' + ((found[uuid] === undefined) ? 'does not exist' : 'already exists'));
|
||||
}
|
||||
|
||||
|
||||
if (found[uuid] === undefined)
|
||||
{
|
||||
found[uuid] = parent;
|
||||
objs.push(parent);
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.log('Failed to find parent for ' + go.FullID.toString());
|
||||
console.error(error);
|
||||
// Unable to find parent, full object probably not fully loaded yet
|
||||
}
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import {IObjectStore} from './interfaces/IObjectStore';
|
||||
import {GameObjectLite} from './GameObjectLite';
|
||||
import {NameValue} from './NameValue';
|
||||
import {BotOptionFlags, CompressedFlags} from '..';
|
||||
import {IGameObject} from './interfaces/IGameObject';
|
||||
import {GameObjectFull} from './GameObjectFull';
|
||||
|
||||
export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
@@ -165,7 +167,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
case Message.ObjectUpdateCompressed:
|
||||
{
|
||||
const objectUpdateCompressed = packet.message as ObjectUpdateCompressedMessage;
|
||||
objectUpdateCompressed.ObjectData.forEach((obj) =>
|
||||
for (const obj of objectUpdateCompressed.ObjectData)
|
||||
{
|
||||
const flags = obj.UpdateFlags;
|
||||
const buf = obj.Data;
|
||||
@@ -313,8 +315,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
|
||||
o.IsAttachment = (compressedflags & CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -451,4 +452,9 @@ export class ObjectStoreLite implements IObjectStore
|
||||
this.objectsByUUID = {};
|
||||
this.objectsByParent = {};
|
||||
}
|
||||
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObjectFull[]
|
||||
{
|
||||
throw new Error('GetObjectsInArea not available with the Lite object store.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import {ChatFromViewerMessage} from '../messages/ChatFromViewer';
|
||||
import {ChatType} from '../../enums/ChatType';
|
||||
import {InstantMessageDialog} from '../../enums/InstantMessageDialog';
|
||||
import Timer = NodeJS.Timer;
|
||||
import {GroupChatSessionJoinEvent, PacketFlags} from '../..';
|
||||
import {GroupChatSessionJoinEvent, PacketFlags, ScriptDialogEvent} from '../..';
|
||||
import {ScriptDialogReplyMessage} from '../messages/ScriptDialogReply';
|
||||
|
||||
export class CommunicationsCommands extends CommandsBase
|
||||
{
|
||||
@@ -402,4 +403,21 @@ export class CommunicationsCommands extends CommandsBase
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
respondToScriptDialog(event: ScriptDialogEvent, buttonIndex: number): Promise<void>
|
||||
{
|
||||
const dialog: ScriptDialogReplyMessage = new ScriptDialogReplyMessage();
|
||||
dialog.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
dialog.Data = {
|
||||
ObjectID: event.ObjectID,
|
||||
ChatChannel: event.ChatChannel,
|
||||
ButtonIndex: buttonIndex,
|
||||
ButtonLabel: Utils.StringToBuffer(event.Buttons[buttonIndex])
|
||||
};
|
||||
const sequenceNo = this.circuit.sendMessage(dialog, PacketFlags.Reliable);
|
||||
return this.circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {Message} from '../../enums/Message';
|
||||
import {FilterResponse} from '../../enums/FilterResponse';
|
||||
import {RegionIDAndHandleReplyMessage} from '../messages/RegionIDAndHandleReply';
|
||||
import {PacketFlags} from '../..';
|
||||
import {IGameObject} from '../interfaces/IGameObject';
|
||||
|
||||
export class RegionCommands extends CommandsBase
|
||||
{
|
||||
@@ -35,4 +36,8 @@ export class RegionCommands extends CommandsBase
|
||||
});
|
||||
});
|
||||
}
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): IGameObject[]
|
||||
{
|
||||
return this.currentRegion.objects.getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import {ITreeBoundingBox} from './ITreeBoundingBox';
|
||||
|
||||
export interface IGameObject
|
||||
{
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
hasNameValueEntry(key: string): boolean;
|
||||
getNameValueEntry(key: string): string;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import {IGameObject} from './IGameObject';
|
||||
import {RBush3D} from 'rbush-3d/dist';
|
||||
import {GameObjectFull} from '../GameObjectFull';
|
||||
|
||||
export interface IObjectStore
|
||||
{
|
||||
rtree?: RBush3D;
|
||||
getObjectsByParent(parentID: number): IGameObject[];
|
||||
shutdown(): void;
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObjectFull[];
|
||||
}
|
||||
|
||||
7
lib/classes/interfaces/ITreeBoundingBox.ts
Normal file
7
lib/classes/interfaces/ITreeBoundingBox.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {BBox} from 'rbush-3d/dist';
|
||||
import {IGameObject} from './IGameObject';
|
||||
|
||||
export interface ITreeBoundingBox extends BBox
|
||||
{
|
||||
gameObject: IGameObject;
|
||||
}
|
||||
28
package-lock.json
generated
28
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@caspertech/node-metaverse",
|
||||
"version": "0.4.6",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -49,8 +49,7 @@
|
||||
"@types/node": {
|
||||
"version": "10.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.6.tgz",
|
||||
"integrity": "sha512-fnA7yvqg3oKQDb3skBif9w5RRKVKAaeKeNuLzZL37XcSiWL4IoSXQnnbchR3UnBu2EMLHBip7ZVEkqoIVBP8QQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-fnA7yvqg3oKQDb3skBif9w5RRKVKAaeKeNuLzZL37XcSiWL4IoSXQnnbchR3UnBu2EMLHBip7ZVEkqoIVBP8QQ=="
|
||||
},
|
||||
"@types/request": {
|
||||
"version": "2.47.1",
|
||||
@@ -64,6 +63,14 @@
|
||||
"@types/tough-cookie": "*"
|
||||
}
|
||||
},
|
||||
"@types/tape": {
|
||||
"version": "4.2.32",
|
||||
"resolved": "http://registry.npmjs.org/@types/tape/-/tape-4.2.32.tgz",
|
||||
"integrity": "sha512-xil0KO5wkPoixdBWGIGolPv9dekf6dVkjjJLAFYchfKcd4DICou67rgGCIO7wAh3i5Ff/6j9IDgZz+GU9cMaqQ==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/tough-cookie": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz",
|
||||
@@ -724,6 +731,21 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
},
|
||||
"quickselect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz",
|
||||
"integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ=="
|
||||
},
|
||||
"rbush-3d": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/rbush-3d/-/rbush-3d-0.0.4.tgz",
|
||||
"integrity": "sha512-s02wJ4Oawn3xdfIUN1hO+dhIcI3zL4vFs+yTlFn/U1jpHh3zaIcaOhLVL6SxyhB4vR/wad77HVJ9dG/ZyLZ6tQ==",
|
||||
"requires": {
|
||||
"@types/node": "^10.5.1",
|
||||
"@types/tape": "^4.2.32",
|
||||
"quickselect": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.88.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"@caspertech/llsd": "^1.0.0",
|
||||
"ipaddr.js": "^1.8.1",
|
||||
"long": "^4.0.0",
|
||||
"rbush-3d": "0.0.4",
|
||||
"request": "^2.88.0",
|
||||
"rxjs": "^6.3.3",
|
||||
"uuid": "^3.3.2",
|
||||
|
||||
Reference in New Issue
Block a user