- Add "GET" method to Caps
- New events: ObjectPhysicsDataEvent, ParcelPropertiesEvent, NewObjectEvent, ObjectUpdateEvent, ObjectKilledEvent - Added getXML function to Color4, Vector2, Vector3, Vector4, GameObject, Region, Quaternion, UUID for opensim-compatible XML export - Added TextureAnim and ParticleSystem decoding to the "full" ObjectStore - Object store will automatically request missing "parent" prims - "setPersist" - When persist is TRUE, the ObjectStore will not forget about "killed" prims - useful for region scanning - Support for Flexible params, Light params, LightImage params, Mesh data, Sculpt maps - Fixed object scale being incorrectly calculated - Add terrain decoding (this was a ballache) - Add parcel map decoding - Add support for region windlight settings (region.environment) - Add support for materials (normal / specular maps) - Add getBuffer, getLong and bitwiseOr to UUID - Added a circular-reference-safe JSONStringify to Utils - Add XferFile capability to Circuit PUBLIC API: AssetCommands: - Rework "downloadAsset" to detect failures - NEW: downloadInventoryAsset() - uses TransferRequest for prim inventory items - NEW: getMaterials() - resolves material UUIDs RegionCommands: - NEW: getTerrainTextures() - NEW: exportSettings() - OpenSim XML export of region settings - NEW: async getTerrain() - Get binary terrain heightmap, 256x256 float32 - resolveObjects() - now fetches task inventory contents too. - resolveObjects() - fix calculation of land impact - NEW: getObjectByLocalID(localID: number, timeout: number) - NEW: getObjectByUUID(uuid: UUID, timeout: number) - NEW: getParcels(); - NEW: pruneObjects - removes missing GameObjects from a list - NEW: setPersist - prevent objectstore from forgetting about killed gameobjects
This commit is contained in:
@@ -203,7 +203,7 @@ export class Bot
|
||||
return this.agent.agentID;
|
||||
}
|
||||
|
||||
async connectToSim(requested: boolean)
|
||||
async connectToSim(requested: boolean = true)
|
||||
{
|
||||
this.agent.setCurrentRegion(this.currentRegion);
|
||||
const circuit = this.currentRegion.circuit;
|
||||
|
||||
@@ -21,7 +21,7 @@ import {Utils} from './Utils';
|
||||
import {ClientEvents} from './ClientEvents';
|
||||
import Timer = NodeJS.Timer;
|
||||
import {ControlFlags, GroupChatSessionAgentListEvent, AgentFlags, PacketFlags, AssetType} from '..';
|
||||
import {GameObject} from './GameObject';
|
||||
import {GameObject} from './public/GameObject';
|
||||
|
||||
export class Agent
|
||||
{
|
||||
|
||||
153
lib/classes/BitPack.ts
Normal file
153
lib/classes/BitPack.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
export class BitPack
|
||||
{
|
||||
static MAX_BITS = 8;
|
||||
static ON = [1];
|
||||
static OFF = [0];
|
||||
|
||||
private bitPos = 0;
|
||||
|
||||
constructor(private Data: Buffer, private bytePos: number)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
get BytePos(): number
|
||||
{
|
||||
if (this.bytePos !== 0 && this.bitPos === 0)
|
||||
{
|
||||
return this.bytePos - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.bytePos;
|
||||
}
|
||||
}
|
||||
|
||||
get BitPos(): number
|
||||
{
|
||||
return this.bitPos;
|
||||
}
|
||||
|
||||
UnpackFloat(): number
|
||||
{
|
||||
const output = this.UnpackBitsBuffer(32);
|
||||
return output.readFloatLE(0);
|
||||
}
|
||||
|
||||
UnpackBits(count: number): number
|
||||
{
|
||||
const output = this.UnpackBitsBuffer(count);
|
||||
return output.readInt32LE(0);
|
||||
}
|
||||
|
||||
UnpackUBits(count: number)
|
||||
{
|
||||
const output = this.UnpackBitsBuffer(count);
|
||||
return output.readUInt32LE(0);
|
||||
}
|
||||
|
||||
UnpsckShort(): number
|
||||
{
|
||||
return this.UnpackBits(16);
|
||||
}
|
||||
|
||||
UnpackUShort(): number
|
||||
{
|
||||
return this.UnpackUBits(16);
|
||||
}
|
||||
|
||||
UnpackInt(): number
|
||||
{
|
||||
return this.UnpackBits(32);
|
||||
}
|
||||
|
||||
UnpackUInt(): number
|
||||
{
|
||||
return this.UnpackUBits(32);
|
||||
}
|
||||
|
||||
UnpackByte(): number
|
||||
{
|
||||
const output = this.UnpackBitsBuffer(8);
|
||||
return output[0];
|
||||
}
|
||||
|
||||
UnpackFixed(signed: boolean, intBits: number, fracBits: number): number
|
||||
{
|
||||
let maxVal;
|
||||
let totalBits = intBits + fracBits;
|
||||
|
||||
if (signed)
|
||||
{
|
||||
totalBits++;
|
||||
}
|
||||
maxVal = 1 << intBits;
|
||||
let fixedVal = 0;
|
||||
if (totalBits <= 8)
|
||||
{
|
||||
fixedVal = this.UnpackByte();
|
||||
}
|
||||
else if (totalBits <= 16)
|
||||
{
|
||||
fixedVal = this.UnpackUBits(16);
|
||||
}
|
||||
else if (totalBits <= 31)
|
||||
{
|
||||
fixedVal = this.UnpackUBits(32);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
fixedVal /= (1 << fracBits);
|
||||
if (signed)
|
||||
{
|
||||
fixedVal -= maxVal;
|
||||
}
|
||||
return fixedVal;
|
||||
}
|
||||
|
||||
UnpackBitsBuffer(totalCount: number): Buffer
|
||||
{
|
||||
const newBuf = Buffer.alloc(4, 0);
|
||||
let count = 0;
|
||||
let curBytePos = 0;
|
||||
let curBitPos = 0;
|
||||
|
||||
while (totalCount > 0)
|
||||
{
|
||||
if (totalCount > BitPack.MAX_BITS)
|
||||
{
|
||||
count = BitPack.MAX_BITS;
|
||||
totalCount -= BitPack.MAX_BITS;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = totalCount;
|
||||
totalCount = 0;
|
||||
}
|
||||
while (count > 0)
|
||||
{
|
||||
newBuf[curBytePos] <<= 1;
|
||||
if ((this.Data[this.bytePos] & (0x80 >> this.bitPos++)) !== 0)
|
||||
{
|
||||
++newBuf[curBytePos];
|
||||
}
|
||||
--count;
|
||||
++curBitPos;
|
||||
if (this.bitPos >= BitPack.MAX_BITS)
|
||||
{
|
||||
this.bitPos = 0;
|
||||
++this.bytePos;
|
||||
}
|
||||
if (curBitPos >= BitPack.MAX_BITS)
|
||||
{
|
||||
curBitPos = 0;
|
||||
++curBytePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newBuf;
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,28 @@ export class Caps
|
||||
});
|
||||
}
|
||||
|
||||
requestGet(url: string): Promise<string>
|
||||
{
|
||||
return new Promise<string>((resolve, reject) =>
|
||||
{
|
||||
request({
|
||||
'uri': url,
|
||||
'rejectUnauthorized': false,
|
||||
'method': 'GET'
|
||||
}, (err, res, body) =>
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
reject(err);
|
||||
}
|
||||
else
|
||||
{
|
||||
resolve(body);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
waitForSeedCapability(): Promise<void>
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
@@ -246,13 +268,54 @@ export class Caps
|
||||
});
|
||||
}
|
||||
|
||||
capsRequestXML(capability: string, data: any): Promise<any>
|
||||
capsGetXML(capability: string): Promise<any>
|
||||
{
|
||||
return new Promise<any>((resolve, reject) =>
|
||||
{
|
||||
this.getCapability(capability).then((url) =>
|
||||
{
|
||||
this.request(url, LLSD.LLSD.formatXML(data), 'application/llsd+xml').then((body: string) =>
|
||||
this.requestGet(url).then((body: string) =>
|
||||
{
|
||||
let result: any = null;
|
||||
try
|
||||
{
|
||||
result = LLSD.LLSD.parseXML(body);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.error('Error parsing LLSD');
|
||||
console.error(body);
|
||||
reject(err);
|
||||
}
|
||||
resolve(result);
|
||||
}).catch((err) =>
|
||||
{
|
||||
console.error(err);
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
capsRequestXML(capability: string, data: any, debug = false): Promise<any>
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
console.log(data);
|
||||
}
|
||||
return new Promise<any>((resolve, reject) =>
|
||||
{
|
||||
this.getCapability(capability).then((url) =>
|
||||
{
|
||||
const xml = LLSD.LLSD.formatXML(data);
|
||||
if (debug)
|
||||
{
|
||||
console.log(xml);
|
||||
}
|
||||
this.request(url, xml, 'application/llsd+xml').then((body: string) =>
|
||||
{
|
||||
let result: any = null;
|
||||
try
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {UUID} from './UUID';
|
||||
import {Socket} from 'dgram';
|
||||
import * as dgram from 'dgram';
|
||||
import {Socket} from 'dgram';
|
||||
import {Packet} from './Packet';
|
||||
import {MessageBase} from './MessageBase';
|
||||
import {PacketAckMessage} from './messages/PacketAck';
|
||||
@@ -8,13 +8,17 @@ import {Message} from '../enums/Message';
|
||||
import {StartPingCheckMessage} from './messages/StartPingCheck';
|
||||
import {CompletePingCheckMessage} from './messages/CompletePingCheck';
|
||||
import {Subscription} from 'rxjs/internal/Subscription';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import Timer = NodeJS.Timer;
|
||||
import {filter} from 'rxjs/operators';
|
||||
import {ClientEvents} from './ClientEvents';
|
||||
import {FilterResponse} from '../enums/FilterResponse';
|
||||
import {Subject} from 'rxjs/internal/Subject';
|
||||
import {PacketFlags} from '..';
|
||||
import {AssetType, PacketFlags, Utils} from '..';
|
||||
import {TimeoutError} from './TimeoutError';
|
||||
import {RequestXferMessage} from './messages/RequestXfer';
|
||||
import {SendXferPacketMessage} from './messages/SendXferPacket';
|
||||
import {ConfirmXferPacketMessage} from './messages/ConfirmXferPacket';
|
||||
import Timer = NodeJS.Timer;
|
||||
import {AbortXferMessage} from './messages/AbortXfer';
|
||||
|
||||
export class Circuit
|
||||
{
|
||||
@@ -79,6 +83,130 @@ export class Circuit
|
||||
return packet.sequenceNumber;
|
||||
}
|
||||
|
||||
XferFile(fileName: string, deleteOnCompletion: boolean, useBigPackets: boolean, vFileID: UUID, vFileType: AssetType, fromCache: boolean): Promise<Buffer>
|
||||
{
|
||||
return new Promise<Buffer>((resolve, reject) =>
|
||||
{
|
||||
let subscription: null | Subscription = null;
|
||||
let timeout: Timer | null = null;
|
||||
const progress = setInterval(() =>
|
||||
{
|
||||
console.log( ' ... Got ' + Object.keys(receivedChunks).length + ' packets');
|
||||
}, 5000);
|
||||
const resetTimeout = function ()
|
||||
{
|
||||
if (timeout !== null)
|
||||
{
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() =>
|
||||
{
|
||||
if (subscription !== null)
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
clearInterval(progress);
|
||||
reject(new Error('Xfer Timeout'));
|
||||
}, 10000);
|
||||
};
|
||||
resetTimeout();
|
||||
const xferRequest = new RequestXferMessage();
|
||||
const transferID = UUID.random().getLong();
|
||||
xferRequest.XferID = {
|
||||
ID: transferID,
|
||||
Filename: Utils.StringToBuffer(fileName),
|
||||
FilePath: (fromCache) ? 4 : 0,
|
||||
DeleteOnCompletion: deleteOnCompletion,
|
||||
UseBigPackets: useBigPackets,
|
||||
VFileID: vFileID,
|
||||
VFileType: vFileType
|
||||
};
|
||||
this.sendMessage(xferRequest, PacketFlags.Reliable);
|
||||
let finished = false;
|
||||
let finishID = 0;
|
||||
const receivedChunks: { [key: number]: Buffer } = {};
|
||||
|
||||
subscription = this.subscribeToMessages([
|
||||
Message.SendXferPacket,
|
||||
Message.AbortXfer
|
||||
], (packet: Packet) =>
|
||||
{
|
||||
switch (packet.message.id)
|
||||
{
|
||||
case Message.AbortXfer:
|
||||
{
|
||||
const message = packet.message as AbortXferMessage;
|
||||
if (message.XferID.ID.compare(transferID) === 0)
|
||||
{
|
||||
if (timeout !== null)
|
||||
{
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
if (subscription !== null)
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
clearInterval(progress);
|
||||
reject(new Error('Xfer Aborted'));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message.SendXferPacket:
|
||||
{
|
||||
const message = packet.message as SendXferPacketMessage;
|
||||
if (message.XferID.ID.compare(transferID) === 0)
|
||||
{
|
||||
resetTimeout();
|
||||
const packetNum = message.XferID.Packet & 0x7FFFFFFF;
|
||||
const finishedNow = message.XferID.Packet & 0x80000000;
|
||||
receivedChunks[packetNum] = message.DataPacket.Data;
|
||||
const confirm = new ConfirmXferPacketMessage();
|
||||
confirm.XferID = {
|
||||
ID: transferID,
|
||||
Packet: packetNum
|
||||
};
|
||||
this.sendMessage(confirm, PacketFlags.Reliable);
|
||||
|
||||
if (finishedNow)
|
||||
{
|
||||
finished = true;
|
||||
finishID = packetNum;
|
||||
}
|
||||
|
||||
if (finished)
|
||||
{
|
||||
// Check if we have all the pieces
|
||||
for (let x = 0; x <= finishID; x++)
|
||||
{
|
||||
if (!receivedChunks[x])
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
const conc: Buffer[] = [];
|
||||
for (let x = 0; x <= finishID; x++)
|
||||
{
|
||||
conc.push(receivedChunks[x]);
|
||||
}
|
||||
if (timeout !== null)
|
||||
{
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
if (subscription !== null)
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
clearInterval(progress);
|
||||
resolve(Buffer.concat(conc));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
resend(sequenceNumber: number)
|
||||
{
|
||||
if (!this.active)
|
||||
|
||||
@@ -14,9 +14,15 @@ import {
|
||||
ScriptDialogEvent,
|
||||
EventQueueStateChangeEvent,
|
||||
FriendOnlineEvent,
|
||||
FriendRightsEvent, FriendRemovedEvent
|
||||
FriendRightsEvent,
|
||||
FriendRemovedEvent,
|
||||
ObjectPhysicsDataEvent,
|
||||
ParcelPropertiesEvent
|
||||
} from '..';
|
||||
import {Subject} from 'rxjs/internal/Subject';
|
||||
import {NewObjectEvent} from '../events/NewObjectEvent';
|
||||
import {ObjectUpdatedEvent} from '../events/ObjectUpdatedEvent';
|
||||
import {ObjectKilledEvent} from '../events/ObjectKilledEvent';
|
||||
|
||||
|
||||
export class ClientEvents
|
||||
@@ -39,4 +45,9 @@ export class ClientEvents
|
||||
onFriendOnline: Subject<FriendOnlineEvent> = new Subject<FriendOnlineEvent>();
|
||||
onFriendRights: Subject<FriendRightsEvent> = new Subject<FriendRightsEvent>();
|
||||
onFriendRemoved: Subject<FriendRemovedEvent> = new Subject<FriendRemovedEvent>();
|
||||
onPhysicsDataEvent: Subject<ObjectPhysicsDataEvent> = new Subject<ObjectPhysicsDataEvent>();
|
||||
onParcelPropertiesEvent: Subject<ParcelPropertiesEvent> = new Subject<ParcelPropertiesEvent>();
|
||||
onNewObjectEvent: Subject<NewObjectEvent> = new Subject<NewObjectEvent>();
|
||||
onObjectUpdatedEvent: Subject<ObjectUpdatedEvent> = new Subject<ObjectUpdatedEvent>();
|
||||
onObjectKilledEvent: Subject<ObjectKilledEvent> = new Subject<ObjectKilledEvent>();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
|
||||
export class Color4
|
||||
{
|
||||
static black: Color4 = new Color4(0.0, 0.0, 0.0, 1.0);
|
||||
static white: Color4 = new Color4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
constructor(public red: number | Buffer, public green: number, public blue: number | boolean, public alpha: number | boolean = 0)
|
||||
static getXML(doc: XMLElementOrXMLNode, c?: Color4)
|
||||
{
|
||||
if (c === undefined)
|
||||
{
|
||||
c = Color4.white;
|
||||
}
|
||||
doc.ele('R', c.red);
|
||||
doc.ele('G', c.green);
|
||||
doc.ele('B', c.blue);
|
||||
doc.ele('A', c.alpha);
|
||||
}
|
||||
|
||||
constructor(public red: number | Buffer | number[], public green: number = 0, public blue: number | boolean = 0, public alpha: number | boolean = 0)
|
||||
{
|
||||
if (red instanceof Buffer && typeof blue === 'boolean')
|
||||
{
|
||||
@@ -41,5 +55,12 @@ export class Color4
|
||||
this.alpha = 1.0 - this.alpha;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(red))
|
||||
{
|
||||
this.green = red[1];
|
||||
this.blue = red[2];
|
||||
this.alpha = red[3];
|
||||
this.red = red[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
EventQueueStateChangeEvent,
|
||||
GroupChatEvent,
|
||||
GroupChatSessionAgentListEvent,
|
||||
GroupChatSessionJoinEvent,
|
||||
TeleportEvent
|
||||
GroupChatSessionJoinEvent, ObjectPhysicsDataEvent, ParcelPropertiesEvent,
|
||||
TeleportEvent, Vector3
|
||||
} from '..';
|
||||
|
||||
export class EventQueueClient
|
||||
@@ -102,112 +102,74 @@ export class EventQueueClient
|
||||
|
||||
break;
|
||||
case 'ParcelProperties':
|
||||
/*
|
||||
{
|
||||
"body": {
|
||||
"AgeVerificationBlock": [
|
||||
{
|
||||
{
|
||||
const body = event['body'];
|
||||
const pprop = new ParcelPropertiesEvent();
|
||||
pprop.RegionDenyAgeUnverified = body['AgeVerificationBlock'][0]['RegionDenyAgeUnverified'];
|
||||
pprop.MediaDesc = body['MediaData'][0]['MediaDesc'];
|
||||
pprop.MediaHeight = body['MediaData'][0]['MediaHeight'];
|
||||
pprop.MediaLoop = body['MediaData'][0]['MediaLoop'];
|
||||
pprop.MediaType = body['MediaData'][0]['MediaType'];
|
||||
pprop.MediaWidth = body['MediaData'][0]['MediaWidth'];
|
||||
pprop.ObscureMedia = body['MediaData'][0]['ObscureMedia'];
|
||||
pprop.ObscureMusic = body['MediaData'][0]['ObscureMusic'];
|
||||
pprop.AABBMax = new Vector3([parseInt(body['ParcelData'][0]['AABBMax'][0], 10), parseInt( body['ParcelData'][0]['AABBMax'][1], 10), parseInt(body['ParcelData'][0]['AABBMax'][2], 10)]);
|
||||
pprop.AABBMin = new Vector3([parseInt(body['ParcelData'][0]['AABBMin'][0], 10), parseInt(body['ParcelData'][0]['AABBMin'][1], 10), parseInt( body['ParcelData'][0]['AABBMin'][2], 10)]);
|
||||
pprop.AnyAVSounds = body['ParcelData'][0]['AnyAVSounds'];
|
||||
pprop.Area = body['ParcelData'][0]['Area'];
|
||||
pprop.AuctionID = Buffer.from(body['ParcelData'][0]['AuctionID'].toArray()).readUInt32LE(0);
|
||||
pprop.AuthBuyerID = new UUID(String(body['ParcelData'][0]['AuthBuyerID']));
|
||||
|
||||
"RegionDenyAgeUnverified": true
|
||||
}
|
||||
],
|
||||
"MediaData": [
|
||||
{
|
||||
"MediaDesc": "",
|
||||
"MediaHeight": 0,
|
||||
"MediaLoop": 0,
|
||||
"MediaType": "text/html",
|
||||
"MediaWidth": 0,
|
||||
"ObscureMedia": 0,
|
||||
"ObscureMusic": 0
|
||||
}
|
||||
],
|
||||
"ParcelData": [
|
||||
{
|
||||
"AABBMax": [
|
||||
256,
|
||||
256,
|
||||
50
|
||||
],
|
||||
"AABBMin": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"AnyAVSounds": true,
|
||||
"Area": 65536,
|
||||
"AuctionID": "AAAAAA==",
|
||||
"AuthBuyerID": "00000000-0000-0000-0000-000000000000",
|
||||
"Bitmap": "/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=",
|
||||
"Category": 0,
|
||||
"ClaimDate": 1333505995,
|
||||
"ClaimPrice": 0,
|
||||
"Desc": "adoption parent furry parent teen twin cub neko pets adult elf vamp toddleedoo baby child panel brother sister numbers meshmerized gacha adoptions adopt family mesh skin shape camp ngi youthspot foster kids mall zoo train kid primbay\ndupli
|
||||
city onlinker",
|
||||
"GroupAVSounds": true,
|
||||
"GroupID": "f2b75b49-8ebc-2a9c-f345-aa2f91adc908",
|
||||
"GroupPrims": 18677,
|
||||
"IsGroupOwned": true,
|
||||
"LandingType": 2,
|
||||
"LocalID": 15,
|
||||
"MaxPrims": 30000,
|
||||
"MediaAutoScale": 1,
|
||||
"MediaID": "6bd35c06-2b24-a83e-03f6-f547c65c8556",
|
||||
"MediaURL": "",
|
||||
"MusicURL": "http://142.4.209.63:8071",
|
||||
"Name": "Next Gen Inc. Adoption Agency on the :::: KiD GRiD :::",
|
||||
"OtherCleanTime": 0,
|
||||
"OtherCount": 4096,
|
||||
"OtherPrims": 312,
|
||||
"OwnerID": "f2b75b49-8ebc-2a9c-f345-aa2f91adc908",
|
||||
"OwnerPrims": 3,
|
||||
"ParcelFlags": "NiAUSw==",
|
||||
"ParcelPrimBonus": 1,
|
||||
"PassHours": 10,
|
||||
"PassPrice": 10,
|
||||
"PublicCount": 0,
|
||||
"RegionDenyAnonymous": true,
|
||||
"RegionDenyIdentified": true,
|
||||
"RegionDenyTransacted": true,
|
||||
"RegionPushOverride": true,
|
||||
"RentPrice": 0,
|
||||
"RequestResult": 0,
|
||||
"SalePrice": 1,
|
||||
"SeeAVs": true,
|
||||
"SelectedPrims": 1,
|
||||
"SelfCount": 0,
|
||||
"SequenceID": 0,
|
||||
"SimWideMaxPrims": 30000,
|
||||
"SimWideTotalPrims": 18993,
|
||||
"SnapSelection": true,
|
||||
"SnapshotID": "09c4101a-9406-2501-b9b7-dbb60260fd7a",
|
||||
"Status": 0,
|
||||
"TotalPrims": 18993,
|
||||
"UserLocation": [
|
||||
131.48399353027344,
|
||||
171.41600036621094,
|
||||
21.544700622558594
|
||||
],
|
||||
"UserLookAt": [
|
||||
0.0325143001973629,
|
||||
-0.9994710087776184,
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"RegionAllowAccessBlock": [
|
||||
{
|
||||
"RegionAllowAccessOverride": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": "ParcelProperties"
|
||||
}
|
||||
|
||||
*/
|
||||
pprop.Bitmap = Buffer.from(body['ParcelData'][0]['Bitmap'].toArray());
|
||||
pprop.Category = body['ParcelData'][0]['Category'];
|
||||
pprop.ClaimDate = body['ParcelData'][0]['ClaimDate'];
|
||||
pprop.ClaimPrice = body['ParcelData'][0]['ClaimPrice'];
|
||||
pprop.Desc = body['ParcelData'][0]['Desc'];
|
||||
pprop.GroupAVSounds = body['ParcelData'][0]['GroupAVSounds'];
|
||||
pprop.GroupID = new UUID(String(body['ParcelData'][0]['GroupID']));
|
||||
pprop.GroupPrims = body['ParcelData'][0]['GroupPrims'];
|
||||
pprop.IsGroupOwned = body['ParcelData'][0]['IsGroupOwned'];
|
||||
pprop.LandingType = body['ParcelData'][0]['LandingType'];
|
||||
pprop.LocalID = body['ParcelData'][0]['LocalID'];
|
||||
pprop.MaxPrims = body['ParcelData'][0]['MaxPrims'];
|
||||
pprop.MediaAutoScale = body['ParcelData'][0]['MediaAutoScale'];
|
||||
pprop.MediaID = new UUID(String(body['ParcelData'][0]['MediaID']));
|
||||
pprop.MediaURL = body['ParcelData'][0]['MediaURL'];
|
||||
pprop.MusicURL = body['ParcelData'][0]['MusicURL'];
|
||||
pprop.Name = body['ParcelData'][0]['Name'];
|
||||
pprop.OtherCleanTime = body['ParcelData'][0]['OtherCleanTime'];
|
||||
pprop.OtherCount = body['ParcelData'][0]['OtherCount'];
|
||||
pprop.OtherPrims = body['ParcelData'][0]['OtherPrims'];
|
||||
pprop.OwnerID = body['ParcelData'][0]['OwnerID'];
|
||||
pprop.OwnerPrims = body['ParcelData'][0]['OwnerPrims'];
|
||||
pprop.ParcelFlags = Buffer.from(body['ParcelData'][0]['ParcelFlags'].toArray()).readUInt32LE(0);
|
||||
pprop.ParcelPrimBonus = body['ParcelData'][0]['ParcelPrimBonus'];
|
||||
pprop.PassHours = body['ParcelData'][0]['PassHours'];
|
||||
pprop.PassPrice = body['ParcelData'][0]['PassPrice'];
|
||||
pprop.PublicCount = body['ParcelData'][0]['PublicCount'];
|
||||
pprop.RegionDenyAnonymous = body['ParcelData'][0]['RegionDenyAnonymous'];
|
||||
pprop.RegionDenyIdentified = body['ParcelData'][0]['RegionDenyIdentified'];
|
||||
pprop.RegionPushOverride = body['ParcelData'][0]['RegionPushOverride'];
|
||||
pprop.RegionDenyTransacted = body['ParcelData'][0]['RegionDenyTransacted'];
|
||||
pprop.RentPrice = body['ParcelData'][0]['RentPrice'];
|
||||
pprop.RequestResult = body['ParcelData'][0]['RequestResult'];
|
||||
pprop.SalePrice = body['ParcelData'][0]['SalePrice'];
|
||||
pprop.SeeAvs = body['ParcelData'][0]['SeeAVs'];
|
||||
pprop.SelectedPrims = body['ParcelData'][0]['SelectedPrims'];
|
||||
pprop.SelfCount = body['ParcelData'][0]['SelfCount'];
|
||||
pprop.SequenceID = body['ParcelData'][0]['SequenceID'];
|
||||
pprop.SimWideMaxPrims = body['ParcelData'][0]['SimWideMaxPrims'];
|
||||
pprop.SimWideTotalPrims = body['ParcelData'][0]['SimWideTotalPrims'];
|
||||
pprop.SnapSelection = body['ParcelData'][0]['SnapSelection'];
|
||||
pprop.SnapshotID = new UUID(body['ParcelData'][0]['SnapshotID'].toString());
|
||||
pprop.Status = body['ParcelData'][0]['Status'];
|
||||
pprop.TotalPrims = body['ParcelData'][0]['TotalPrims'];
|
||||
pprop.UserLocation = new Vector3([parseInt(body['ParcelData'][0]['UserLocation'][0], 10), parseInt(body['ParcelData'][0]['UserLocation'][1], 10), parseInt(body['ParcelData'][0]['UserLocation'][2], 10)]);
|
||||
pprop.UserLookAt = new Vector3([parseInt(body['ParcelData'][0]['UserLookAt'][0], 10), parseInt(body['ParcelData'][0]['UserLookAt'][1], 10), parseInt(body['ParcelData'][0]['UserLookAt'][2], 10)]);
|
||||
pprop.RegionAllowAccessOverride = body['RegionAllowAccessBlock'][0]['RegionAllowAccessOverride'];
|
||||
this.clientEvents.onParcelPropertiesEvent.next(pprop);
|
||||
break;
|
||||
}
|
||||
case 'AgentGroupDataUpdate':
|
||||
/*
|
||||
{
|
||||
@@ -368,6 +330,20 @@ export class EventQueueClient
|
||||
}
|
||||
case 'ObjectPhysicsProperties':
|
||||
{
|
||||
const objData = event['body']['ObjectData'];
|
||||
for (const obj of objData)
|
||||
{
|
||||
const objPhysEvent = new ObjectPhysicsDataEvent();
|
||||
objPhysEvent.localID = obj.LocalID;
|
||||
objPhysEvent.density = obj.Density;
|
||||
objPhysEvent.friction = obj.Friction;
|
||||
objPhysEvent.gravityMultiplier = obj.GravityMultiplier;
|
||||
objPhysEvent.physicsShapeType = obj.PhysicsShapeType;
|
||||
objPhysEvent.restitution = obj.Restitution;
|
||||
|
||||
this.clientEvents.onPhysicsDataEvent.next(objPhysEvent);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'TeleportFinish':
|
||||
|
||||
@@ -5,7 +5,7 @@ import {AssetType, InventoryItemFlags} from '..';
|
||||
|
||||
export class InventoryItem
|
||||
{
|
||||
assetID: UUID = UUID.zero();
|
||||
assetID: UUID = UUID.zero();;
|
||||
inventoryType: InventoryType;
|
||||
name: string;
|
||||
salePrice: number;
|
||||
@@ -26,5 +26,17 @@ export class InventoryItem
|
||||
owner: UUID;
|
||||
creator: UUID;
|
||||
group: UUID;
|
||||
groupOwned?: boolean
|
||||
} = {
|
||||
baseMask: 0,
|
||||
groupMask: 0,
|
||||
nextOwnerMask: 0,
|
||||
ownerMask: 0,
|
||||
everyoneMask: 0,
|
||||
lastOwner: UUID.zero(),
|
||||
owner: UUID.zero(),
|
||||
creator: UUID.zero(),
|
||||
group: UUID.zero(),
|
||||
groupOwned: false
|
||||
};
|
||||
}
|
||||
@@ -9,16 +9,15 @@ import {UUID} from './UUID';
|
||||
import {Quaternion} from './Quaternion';
|
||||
import {Vector3} from './Vector3';
|
||||
import {Utils} from './Utils';
|
||||
import {PCode} from '../enums/PCode';
|
||||
import {ClientEvents} from './ClientEvents';
|
||||
import {IObjectStore} from './interfaces/IObjectStore';
|
||||
import {BotOptionFlags, CompressedFlags} from '..';
|
||||
import {BotOptionFlags, CompressedFlags, PacketFlags, PCode} from '..';
|
||||
import {RBush3D} from 'rbush-3d/dist';
|
||||
import {Vector4} from './Vector4';
|
||||
import {TextureEntry} from './TextureEntry';
|
||||
import {Color4} from './Color4';
|
||||
import {ParticleSystem} from './ParticleSystem';
|
||||
import {GameObject} from './GameObject';
|
||||
import {GameObject} from './public/GameObject';
|
||||
import {ObjectStoreLite} from './ObjectStoreLite';
|
||||
|
||||
export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
@@ -38,7 +37,7 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
const localID = objData.ID;
|
||||
const parentID = objData.ParentID;
|
||||
let addToParentList = true;
|
||||
|
||||
let newObject = false;
|
||||
if (this.objects[localID])
|
||||
{
|
||||
if (this.objects[localID].ParentID !== parentID && this.objectsByParent[parentID])
|
||||
@@ -49,15 +48,18 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (this.objectsByParent[parentID])
|
||||
{
|
||||
addToParentList = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newObject = true;
|
||||
this.objects[localID] = new GameObject();
|
||||
this.objects[localID].region = this.agent.currentRegion;
|
||||
}
|
||||
this.objects[localID].deleted = false;
|
||||
|
||||
const obj = this.objects[localID];
|
||||
obj.ID = objData.ID;
|
||||
@@ -67,6 +69,7 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
obj.PCode = objData.PCode;
|
||||
obj.Material = objData.Material;
|
||||
obj.ClickAction = objData.ClickAction;
|
||||
|
||||
obj.Scale = objData.Scale;
|
||||
obj.ObjectData = objData.ObjectData;
|
||||
const data: Buffer = objData.ObjectData;
|
||||
@@ -158,6 +161,7 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
break;
|
||||
}
|
||||
obj.ParentID = objData.ParentID;
|
||||
|
||||
obj.Flags = objData.UpdateFlags;
|
||||
obj.PathCurve = objData.PathCurve;
|
||||
obj.ProfileCurve = objData.ProfileCurve;
|
||||
@@ -179,11 +183,21 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
obj.ProfileHollow = objData.ProfileHollow;
|
||||
obj.TextureEntry = new TextureEntry(objData.TextureEntry);
|
||||
obj.TextureAnim = objData.TextureAnim;
|
||||
|
||||
if (obj.TextureAnim.length >= 16)
|
||||
{
|
||||
this.readTextureAnim(obj);
|
||||
}
|
||||
|
||||
const pcodeData = objData.Data;
|
||||
obj.Text = Utils.BufferToStringSimple(objData.Text);
|
||||
obj.TextColor = new Color4(objData.TextColor, 0, false, true);
|
||||
obj.MediaURL = Utils.BufferToStringSimple(objData.MediaURL);
|
||||
obj.PSBlock = objData.PSBlock;
|
||||
if (obj.PSBlock.length > 0)
|
||||
{
|
||||
obj.Particles = new ParticleSystem(obj.PSBlock, 0);
|
||||
}
|
||||
obj.Sound = objData.Sound;
|
||||
obj.OwnerID = objData.OwnerID;
|
||||
obj.SoundGain = objData.Gain;
|
||||
@@ -202,6 +216,9 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
obj.TreeSpecies = pcodeData[0];
|
||||
}
|
||||
break;
|
||||
case PCode.Prim:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -248,14 +265,7 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
this.readExtraParams(objData.ExtraParams, 0, this.objects[localID]);
|
||||
this.objects[localID].NameValue = this.parseNameValues(Utils.BufferToStringSimple(objData.NameValue));
|
||||
|
||||
if (this.objects[localID].NameValue['AttachItemID'])
|
||||
{
|
||||
this.objects[localID].IsAttachment = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.objects[localID].IsAttachment = false;
|
||||
}
|
||||
this.objects[localID].IsAttachment = this.objects[localID].NameValue['AttachItemID'] !== undefined;
|
||||
|
||||
this.objectsByUUID[objData.FullID.toString()] = localID;
|
||||
if (!this.objectsByParent[parentID])
|
||||
@@ -275,6 +285,14 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
else
|
||||
{
|
||||
this.insertIntoRtree(obj);
|
||||
if (objData.ParentID !== undefined && objData.ParentID !== 0 && !this.objects[objData.ParentID])
|
||||
{
|
||||
this.requestMissingObject(objData.ParentID);
|
||||
}
|
||||
if (obj.ParentID === 0)
|
||||
{
|
||||
this.notifyObjectUpdate(newObject, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,7 +321,24 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
}
|
||||
|
||||
protected objectUpdateCompressed(objectUpdateCompressed: ObjectUpdateCompressedMessage)
|
||||
private readTextureAnim(obj: GameObject)
|
||||
{
|
||||
let animPos = 0;
|
||||
if (obj.TextureAnim !== undefined)
|
||||
{
|
||||
obj.TextureAnimFlags = obj.TextureAnim.readUInt8(animPos++);
|
||||
obj.TextureAnimFace = obj.TextureAnim.readUInt8(animPos++);
|
||||
obj.TextureAnimSizeX = obj.TextureAnim.readUInt8(animPos++);
|
||||
obj.TextureAnimSizeY = obj.TextureAnim.readUInt8(animPos++);
|
||||
obj.TextureAnimStart = obj.TextureAnim.readFloatLE(animPos);
|
||||
animPos = animPos + 4;
|
||||
obj.TextureAnimLength = obj.TextureAnim.readFloatLE(animPos);
|
||||
animPos = animPos + 4;
|
||||
obj.TextureAnimRate = obj.TextureAnim.readFloatLE(animPos);
|
||||
}
|
||||
}
|
||||
|
||||
protected async objectUpdateCompressed(objectUpdateCompressed: ObjectUpdateCompressedMessage)
|
||||
{
|
||||
for (const obj of objectUpdateCompressed.ObjectData)
|
||||
{
|
||||
@@ -321,6 +356,7 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
newObj = true;
|
||||
this.objects[localID] = new GameObject();
|
||||
this.objects[localID].region = this.agent.currentRegion;
|
||||
}
|
||||
const o = this.objects[localID];
|
||||
o.ID = localID;
|
||||
@@ -328,6 +364,7 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
o.FullID = fullID;
|
||||
o.Flags = flags;
|
||||
o.PCode = pcode;
|
||||
o.deleted = false;
|
||||
o.State = buf.readUInt8(pos++);
|
||||
o.CRC = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
@@ -349,36 +386,38 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
o.AngularVelocity = new Vector3(buf, pos, false);
|
||||
pos = pos + 12;
|
||||
}
|
||||
let newParentID = 0;
|
||||
if (compressedflags & CompressedFlags.HasParent)
|
||||
{
|
||||
const newParentID = buf.readUInt32LE(pos);
|
||||
newParentID = buf.readUInt32LE(pos);
|
||||
pos += 4;
|
||||
let add = true;
|
||||
if (!newObj)
|
||||
{
|
||||
if (newParentID !== o.ParentID)
|
||||
{
|
||||
const index = this.objectsByParent[o.ParentID].indexOf(localID);
|
||||
if (index !== -1)
|
||||
{
|
||||
this.objectsByParent[o.ParentID].splice(index, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
if (!this.objectsByParent[newParentID])
|
||||
{
|
||||
this.objectsByParent[newParentID] = [];
|
||||
}
|
||||
this.objectsByParent[newParentID].push(localID);
|
||||
}
|
||||
o.ParentID = newParentID;
|
||||
}
|
||||
o.ParentID = newParentID;
|
||||
let add = true;
|
||||
if (!newObj && o.ParentID !== undefined)
|
||||
{
|
||||
if (newParentID !== o.ParentID)
|
||||
{
|
||||
const index = this.objectsByParent[o.ParentID].indexOf(localID);
|
||||
if (index !== -1)
|
||||
{
|
||||
this.objectsByParent[o.ParentID].splice(index, 1);
|
||||
}
|
||||
}
|
||||
else if (this.objectsByParent[o.ParentID])
|
||||
{
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
if (!this.objectsByParent[newParentID])
|
||||
{
|
||||
this.objectsByParent[newParentID] = [];
|
||||
}
|
||||
this.objectsByParent[newParentID].push(localID);
|
||||
}
|
||||
|
||||
if (pcode !== PCode.Avatar && newObj && this.options & BotOptionFlags.StoreMyAttachmentsOnly && (this.agent.localID !== 0 && o.ParentID !== this.agent.localID))
|
||||
{
|
||||
// Drop object
|
||||
@@ -387,6 +426,10 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o.ParentID !== undefined && o.ParentID !== 0 && !this.objects[o.ParentID])
|
||||
{
|
||||
this.requestMissingObject(o.ParentID);
|
||||
}
|
||||
if (compressedflags & CompressedFlags.Tree)
|
||||
{
|
||||
o.TreeSpecies = buf.readUInt8(pos++);
|
||||
@@ -421,7 +464,8 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasParticles)
|
||||
{
|
||||
o.Particles = new ParticleSystem(buf.slice(pos, pos + 86), 0);
|
||||
o.PSBlock = buf.slice(pos, pos + 86);
|
||||
o.Particles = new ParticleSystem(o.PSBlock, 0);
|
||||
pos += 86;
|
||||
}
|
||||
|
||||
@@ -474,13 +518,23 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
|
||||
if (compressedflags & CompressedFlags.TextureAnimation)
|
||||
{
|
||||
// TODO: Properly parse textureAnim
|
||||
const textureAnimLength = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
o.TextureAnim = buf.slice(pos, pos + textureAnimLength);
|
||||
if (o.TextureAnim.length >= 16)
|
||||
{
|
||||
this.readTextureAnim(o);
|
||||
}
|
||||
}
|
||||
|
||||
o.IsAttachment = (compressedflags & CompressedFlags.HasNameValues) !== 0 && o.ParentID !== 0;
|
||||
|
||||
this.insertIntoRtree(o);
|
||||
|
||||
if (o.ParentID === 0)
|
||||
{
|
||||
this.notifyObjectUpdate(newObj, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -543,19 +597,8 @@ export class ObjectStoreFull extends ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Received terse update for object ' + localID + ' which is not in the store, so requesting the object');
|
||||
// We don't know about this object, so request it
|
||||
const rmo = new RequestMultipleObjectsMessage();
|
||||
rmo.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
rmo.ObjectData = [];
|
||||
rmo.ObjectData.push({
|
||||
CacheMissType: 0,
|
||||
ID: localID
|
||||
});
|
||||
this.circuit.sendMessage(rmo, 0);
|
||||
this.requestMissingObject(localID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,26 @@ import {Agent} from './Agent';
|
||||
import {UUID} from './UUID';
|
||||
import {ExtraParamType} from '../enums/ExtraParamType';
|
||||
import {Utils} from './Utils';
|
||||
import {PCode} from '../enums/PCode';
|
||||
import {ClientEvents} from './ClientEvents';
|
||||
import {KillObjectMessage} from './messages/KillObject';
|
||||
import {IObjectStore} from './interfaces/IObjectStore';
|
||||
import {NameValue} from './NameValue';
|
||||
import {BotOptionFlags, CompressedFlags} from '..';
|
||||
import {GameObject} from './GameObject';
|
||||
import {BotOptionFlags, CompressedFlags, ObjectPhysicsDataEvent, PacketFlags, PCode, Vector3} from '..';
|
||||
import {GameObject} from './public/GameObject';
|
||||
import {RBush3D} from 'rbush-3d/dist';
|
||||
import {ITreeBoundingBox} from './interfaces/ITreeBoundingBox';
|
||||
import {FilterResponse} from '../enums/FilterResponse';
|
||||
import {ObjectSelectMessage} from './messages/ObjectSelect';
|
||||
import {ObjectDeselectMessage} from './messages/ObjectDeselect';
|
||||
import {FlexibleData} from './public/FlexibleData';
|
||||
import {LightImageData} from './public/LightImageData';
|
||||
import {LightData} from './public/LightData';
|
||||
import {MeshData} from './public/MeshData';
|
||||
import {SculptData} from './public/SculptData';
|
||||
import {Quaternion} from './Quaternion';
|
||||
import {Subscription} from 'rxjs/internal/Subscription';
|
||||
import {NewObjectEvent} from '../events/NewObjectEvent';
|
||||
import {ObjectUpdatedEvent} from '../events/ObjectUpdatedEvent';
|
||||
|
||||
export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
@@ -29,6 +40,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
protected objectsByParent: { [key: number]: number[] } = {};
|
||||
protected clientEvents: ClientEvents;
|
||||
protected options: BotOptionFlags;
|
||||
protected requestedObjects: {[key: number]: boolean} = {};
|
||||
protected deadObjects: number[] = [];
|
||||
protected persist = false;
|
||||
private physicsSubscription: Subscription;
|
||||
|
||||
rtree?: RBush3D;
|
||||
|
||||
@@ -45,7 +60,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
Message.ObjectUpdateCompressed,
|
||||
Message.ImprovedTerseObjectUpdate,
|
||||
Message.KillObject
|
||||
], (packet: Packet) =>
|
||||
], async (packet: Packet) =>
|
||||
{
|
||||
switch (packet.message.id)
|
||||
{
|
||||
@@ -60,7 +75,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
case Message.ObjectUpdateCompressed:
|
||||
{
|
||||
const objectUpdateCompressed = packet.message as ObjectUpdateCompressedMessage;
|
||||
this.objectUpdateCompressed(objectUpdateCompressed);
|
||||
await this.objectUpdateCompressed(objectUpdateCompressed);
|
||||
break;
|
||||
}
|
||||
case Message.ImprovedTerseObjectUpdate:
|
||||
@@ -73,6 +88,93 @@ export class ObjectStoreLite implements IObjectStore
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.physicsSubscription = this.clientEvents.onPhysicsDataEvent.subscribe((evt: ObjectPhysicsDataEvent) =>
|
||||
{
|
||||
if (this.objects[evt.localID])
|
||||
{
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected async requestMissingObject(localID: number, attempt = 0)
|
||||
{
|
||||
if (this.requestedObjects[localID])
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.requestedObjects[localID] = true;
|
||||
const rmo = new RequestMultipleObjectsMessage();
|
||||
rmo.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
rmo.ObjectData = [];
|
||||
rmo.ObjectData.push({
|
||||
CacheMissType: 0,
|
||||
ID: localID
|
||||
});
|
||||
this.circuit.sendMessage(rmo, PacketFlags.Reliable);
|
||||
|
||||
const selectObject = new ObjectSelectMessage();
|
||||
selectObject.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
selectObject.ObjectData = [
|
||||
{
|
||||
'ObjectLocalID': localID
|
||||
}
|
||||
];
|
||||
this.circuit.sendMessage(selectObject, PacketFlags.Reliable);
|
||||
|
||||
try
|
||||
{
|
||||
await this.circuit.waitForMessage<ObjectUpdateMessage>(Message.ObjectUpdate, 10000, (message: ObjectUpdateMessage): FilterResponse =>
|
||||
{
|
||||
for (const obj of message.ObjectData)
|
||||
{
|
||||
if (obj.ID === localID)
|
||||
{
|
||||
return FilterResponse.Finish;
|
||||
}
|
||||
}
|
||||
return FilterResponse.NoMatch;
|
||||
});
|
||||
delete this.requestedObjects[localID];
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
delete this.requestedObjects[localID];
|
||||
if (attempt < 5)
|
||||
{
|
||||
await this.requestMissingObject(localID, ++attempt);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error('Error retrieving missing object after 5 attempts: ' + localID);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
const deselectObject = new ObjectDeselectMessage();
|
||||
deselectObject.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
deselectObject.ObjectData = [
|
||||
{
|
||||
'ObjectLocalID': localID
|
||||
}
|
||||
];
|
||||
this.circuit.sendMessage(selectObject, PacketFlags.Reliable);
|
||||
}
|
||||
}
|
||||
|
||||
protected objectUpdate(objectUpdate: ObjectUpdateMessage)
|
||||
@@ -82,6 +184,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const localID = objData.ID;
|
||||
const parentID = objData.ParentID;
|
||||
let addToParentList = true;
|
||||
let newObject = false;
|
||||
|
||||
if (this.objects[localID])
|
||||
{
|
||||
@@ -93,17 +196,20 @@ export class ObjectStoreLite implements IObjectStore
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (this.objectsByParent[parentID])
|
||||
{
|
||||
addToParentList = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newObject = true;
|
||||
this.objects[localID] = new GameObject();
|
||||
this.objects[localID].region = this.agent.currentRegion;
|
||||
}
|
||||
|
||||
const obj = this.objects[localID];
|
||||
obj.deleted = false;
|
||||
obj.ID = objData.ID;
|
||||
obj.FullID = objData.FullID;
|
||||
obj.ParentID = objData.ParentID;
|
||||
@@ -112,14 +218,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
this.objects[localID].NameValue = this.parseNameValues(Utils.BufferToStringSimple(objData.NameValue));
|
||||
|
||||
if (this.objects[localID].NameValue['AttachItemID'])
|
||||
{
|
||||
this.objects[localID].IsAttachment = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.objects[localID].IsAttachment = false;
|
||||
}
|
||||
this.objects[localID].IsAttachment = this.objects[localID].NameValue['AttachItemID'] !== undefined;
|
||||
|
||||
if (objData.PCode === PCode.Avatar && this.objects[localID].FullID.toString() === this.agent.agentID.toString())
|
||||
{
|
||||
@@ -180,9 +279,39 @@ export class ObjectStoreLite implements IObjectStore
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.ParentID === 0)
|
||||
{
|
||||
this.notifyObjectUpdate(newObject, obj);
|
||||
}
|
||||
|
||||
if (objData.ParentID !== undefined && objData.ParentID !== 0 && !this.objects[objData.ParentID])
|
||||
{
|
||||
this.requestMissingObject(objData.ParentID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected notifyObjectUpdate(newObject: boolean, obj: GameObject)
|
||||
{
|
||||
if (newObject)
|
||||
{
|
||||
const newObj = new NewObjectEvent();
|
||||
newObj.localID = obj.ID;
|
||||
newObj.objectID = obj.FullID;
|
||||
newObj.object = obj;
|
||||
this.clientEvents.onNewObjectEvent.next(newObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
const updObj = new ObjectUpdatedEvent();
|
||||
updObj.localID = obj.ID;
|
||||
updObj.objectID = obj.FullID;
|
||||
updObj.object = obj;
|
||||
this.clientEvents.onObjectUpdatedEvent.next(updObj);
|
||||
}
|
||||
}
|
||||
|
||||
protected objectUpdateCached(objectUpdateCached: ObjectUpdateCachedMessage)
|
||||
{
|
||||
const rmo = new RequestMultipleObjectsMessage();
|
||||
@@ -201,7 +330,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
this.circuit.sendMessage(rmo, 0);
|
||||
}
|
||||
|
||||
protected objectUpdateCompressed(objectUpdateCompressed: ObjectUpdateCompressedMessage)
|
||||
protected async objectUpdateCompressed(objectUpdateCompressed: ObjectUpdateCompressedMessage)
|
||||
{
|
||||
for (const obj of objectUpdateCompressed.ObjectData)
|
||||
{
|
||||
@@ -219,8 +348,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
newObj = true;
|
||||
this.objects[localID] = new GameObject();
|
||||
this.objects[localID].region = this.agent.currentRegion;
|
||||
}
|
||||
const o = this.objects[localID];
|
||||
o.deleted = false;
|
||||
o.ID = localID;
|
||||
o.PCode = pcode;
|
||||
this.objectsByUUID[fullID.toString()] = localID;
|
||||
@@ -229,15 +360,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
pos++;
|
||||
|
||||
pos = pos + 4;
|
||||
pos++;
|
||||
pos++;
|
||||
|
||||
pos = pos + 12;
|
||||
|
||||
pos = pos + 12;
|
||||
|
||||
pos = pos + 12;
|
||||
pos = pos + 42;
|
||||
const compressedflags: CompressedFlags = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
o.OwnerID = new UUID(buf, pos);
|
||||
@@ -247,35 +370,37 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
pos = pos + 12;
|
||||
}
|
||||
let newParentID = 0;
|
||||
if (compressedflags & CompressedFlags.HasParent)
|
||||
{
|
||||
const newParentID = buf.readUInt32LE(pos);
|
||||
newParentID = buf.readUInt32LE(pos);
|
||||
pos += 4;
|
||||
let add = true;
|
||||
if (!newObj)
|
||||
}
|
||||
|
||||
o.ParentID = newParentID;
|
||||
let add = true;
|
||||
if (!newObj && o.ParentID !== undefined)
|
||||
{
|
||||
if (newParentID !== o.ParentID)
|
||||
{
|
||||
if (newParentID !== o.ParentID)
|
||||
const index = this.objectsByParent[o.ParentID].indexOf(localID);
|
||||
if (index !== -1)
|
||||
{
|
||||
const index = this.objectsByParent[o.ParentID].indexOf(localID);
|
||||
if (index !== -1)
|
||||
{
|
||||
this.objectsByParent[o.ParentID].splice(index, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
add = false;
|
||||
this.objectsByParent[o.ParentID].splice(index, 1);
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
else if (this.objectsByParent[o.ParentID])
|
||||
{
|
||||
if (!this.objectsByParent[newParentID])
|
||||
{
|
||||
this.objectsByParent[newParentID] = [];
|
||||
}
|
||||
this.objectsByParent[newParentID].push(localID);
|
||||
add = false;
|
||||
}
|
||||
o.ParentID = newParentID;
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
if (!this.objectsByParent[newParentID])
|
||||
{
|
||||
this.objectsByParent[newParentID] = [];
|
||||
}
|
||||
this.objectsByParent[newParentID].push(localID);
|
||||
}
|
||||
if (pcode !== PCode.Avatar && newObj && this.options & BotOptionFlags.StoreMyAttachmentsOnly)
|
||||
{
|
||||
@@ -286,6 +411,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (o.ParentID !== undefined && o.ParentID !== 0 && !this.objects[o.ParentID])
|
||||
{
|
||||
this.requestMissingObject(o.ParentID);
|
||||
}
|
||||
if (compressedflags & CompressedFlags.Tree)
|
||||
{
|
||||
pos++;
|
||||
@@ -312,7 +441,6 @@ export class ObjectStoreLite implements IObjectStore
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasParticles)
|
||||
{
|
||||
// TODO: Particle system block
|
||||
pos += 86;
|
||||
}
|
||||
|
||||
@@ -321,10 +449,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
if (compressedflags & CompressedFlags.HasSound)
|
||||
{
|
||||
pos = pos + 16;
|
||||
pos += 4;
|
||||
pos++;
|
||||
pos = pos + 4;
|
||||
pos = pos + 25
|
||||
}
|
||||
if (compressedflags & CompressedFlags.HasNameValues)
|
||||
{
|
||||
@@ -333,24 +458,21 @@ export class ObjectStoreLite implements IObjectStore
|
||||
pos += result.readLength;
|
||||
}
|
||||
pos++;
|
||||
pos = pos + 2;
|
||||
pos = pos + 2;
|
||||
pos = pos + 12;
|
||||
pos = pos + 2;
|
||||
pos = pos + 2;
|
||||
pos = pos + 2;
|
||||
pos = pos + 22;
|
||||
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;
|
||||
|
||||
if (o.ParentID === 0)
|
||||
{
|
||||
this.notifyObjectUpdate(newObj, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,17 +491,38 @@ export class ObjectStoreLite implements IObjectStore
|
||||
});
|
||||
}
|
||||
|
||||
setPersist(persist: boolean): void
|
||||
{
|
||||
this.persist = persist;
|
||||
if (!this.persist)
|
||||
{
|
||||
for (const d of this.deadObjects)
|
||||
{
|
||||
this.deleteObject(d);
|
||||
}
|
||||
this.deadObjects = [];
|
||||
}
|
||||
}
|
||||
|
||||
deleteObject(objectID: number)
|
||||
{
|
||||
if (this.objects[objectID])
|
||||
{
|
||||
this.objects[objectID].deleted = true;
|
||||
|
||||
if (this.persist)
|
||||
{
|
||||
this.deadObjects.push(objectID);
|
||||
return;
|
||||
}
|
||||
|
||||
// First, kill all children
|
||||
if (this.objectsByParent[objectID])
|
||||
{
|
||||
this.objectsByParent[objectID].forEach((childObjID) =>
|
||||
for (const childObjID of this.objectsByParent[objectID])
|
||||
{
|
||||
this.deleteObject(childObjID);
|
||||
});
|
||||
}
|
||||
}
|
||||
delete this.objectsByParent[objectID];
|
||||
|
||||
@@ -391,13 +534,16 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
delete this.objectsByUUID[uuid];
|
||||
}
|
||||
const parentID = objct.ParentID;
|
||||
if (this.objectsByParent[parentID])
|
||||
if (objct.ParentID !== undefined)
|
||||
{
|
||||
const ind = this.objectsByParent[parentID].indexOf(objectID);
|
||||
if (ind !== -1)
|
||||
const parentID = objct.ParentID;
|
||||
if (this.objectsByParent[parentID])
|
||||
{
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
const ind = this.objectsByParent[parentID].indexOf(objectID);
|
||||
if (ind !== -1)
|
||||
{
|
||||
this.objectsByParent[parentID].splice(ind, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.rtree && this.objects[objectID].rtreeEntry !== undefined)
|
||||
@@ -410,6 +556,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
readExtraParams(buf: Buffer, pos: number, o: GameObject): number
|
||||
{
|
||||
const startPos = pos;
|
||||
if (pos >= buf.length)
|
||||
{
|
||||
return 0;
|
||||
@@ -422,9 +569,28 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const paramLength = buf.readUInt32LE(pos);
|
||||
pos = pos + 4;
|
||||
|
||||
// TODO: Read extra param data
|
||||
switch (type)
|
||||
{
|
||||
case ExtraParamType.Flexible:
|
||||
o.FlexibleData = new FlexibleData(buf, pos, paramLength);
|
||||
break;
|
||||
case ExtraParamType.Light:
|
||||
o.LightData = new LightData(buf, pos, paramLength);
|
||||
break;
|
||||
case ExtraParamType.LightImage:
|
||||
o.LightImageData = new LightImageData(buf, pos, paramLength);
|
||||
break;
|
||||
case ExtraParamType.Mesh:
|
||||
o.MeshData = new MeshData(buf, pos, paramLength);
|
||||
break;
|
||||
case ExtraParamType.Sculpt:
|
||||
o.SculptData = new SculptData(buf, pos, paramLength);
|
||||
break;
|
||||
}
|
||||
|
||||
pos += paramLength;
|
||||
}
|
||||
o.ExtraParams = buf.slice(startPos, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -438,7 +604,10 @@ export class ObjectStoreLite implements IObjectStore
|
||||
const result: GameObject[] = [];
|
||||
list.forEach((localID) =>
|
||||
{
|
||||
result.push(this.objects[localID]);
|
||||
if (this.objects[localID])
|
||||
{
|
||||
result.push(this.objects[localID]);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -481,6 +650,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
shutdown()
|
||||
{
|
||||
this.physicsSubscription.unsubscribe();
|
||||
this.objects = {};
|
||||
if (this.rtree)
|
||||
{
|
||||
@@ -492,33 +662,43 @@ export class ObjectStoreLite implements IObjectStore
|
||||
|
||||
protected findParent(go: GameObject): GameObject
|
||||
{
|
||||
if (go.ParentID !== 0 && this.objects[go.ParentID])
|
||||
if (go.ParentID !== undefined && go.ParentID !== 0 && this.objects[go.ParentID])
|
||||
{
|
||||
return this.findParent(this.objects[go.ParentID]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (go.ParentID !== undefined && go.ParentID !== 0 && !this.objects[go.ParentID])
|
||||
{
|
||||
this.requestMissingObject(go.ParentID);
|
||||
}
|
||||
return go;
|
||||
}
|
||||
}
|
||||
|
||||
private populateChildren(obj: GameObject)
|
||||
{
|
||||
obj.children = [];
|
||||
obj.totalChildren = 0;
|
||||
for (const child of this.getObjectsByParent(obj.ID))
|
||||
if (obj !== undefined)
|
||||
{
|
||||
obj.totalChildren++;
|
||||
this.populateChildren(child);
|
||||
if (child.totalChildren !== undefined)
|
||||
obj.children = [];
|
||||
obj.totalChildren = 0;
|
||||
for (const child of this.getObjectsByParent(obj.ID))
|
||||
{
|
||||
obj.totalChildren += child.totalChildren;
|
||||
if (child.PCode !== PCode.Avatar)
|
||||
{
|
||||
obj.totalChildren++;
|
||||
this.populateChildren(child);
|
||||
if (child.totalChildren !== undefined)
|
||||
{
|
||||
obj.totalChildren += child.totalChildren;
|
||||
}
|
||||
obj.children.push(child);
|
||||
}
|
||||
}
|
||||
obj.children.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
getAllObjects(): GameObject[]
|
||||
async getAllObjects(): Promise<GameObject[]>
|
||||
{
|
||||
const results = [];
|
||||
const found: {[key: string]: GameObject} = {};
|
||||
@@ -530,7 +710,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
try
|
||||
{
|
||||
const parent = this.findParent(go);
|
||||
if (parent.PCode !== PCode.Avatar && (parent.IsAttachment === undefined || parent.IsAttachment === false))
|
||||
if (parent.PCode !== PCode.Avatar && (parent.IsAttachment === undefined || parent.IsAttachment === false) && parent.ParentID === 0)
|
||||
{
|
||||
const uuid = parent.FullID.toString();
|
||||
|
||||
@@ -565,7 +745,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
return Object.keys(this.objects).length;
|
||||
}
|
||||
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObject[]
|
||||
async getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): Promise<GameObject[]>
|
||||
{
|
||||
if (!this.rtree)
|
||||
{
|
||||
@@ -590,7 +770,7 @@ export class ObjectStoreLite implements IObjectStore
|
||||
try
|
||||
{
|
||||
const parent = this.findParent(go);
|
||||
if (parent.PCode !== PCode.Avatar && (parent.IsAttachment === undefined || parent.IsAttachment === false))
|
||||
if (parent.PCode !== PCode.Avatar && (parent.IsAttachment === undefined || parent.IsAttachment === false) && parent.ParentID === 0)
|
||||
{
|
||||
const uuid = parent.FullID.toString();
|
||||
|
||||
@@ -656,7 +836,8 @@ export class ObjectStoreLite implements IObjectStore
|
||||
{
|
||||
return;
|
||||
}
|
||||
const normalizedScale = obj.Scale.multiplyByQuat(obj.Rotation);
|
||||
const normalizedScale = new Vector3(obj.Scale).multiplyByQuat(new Quaternion(obj.Rotation));
|
||||
|
||||
const bounds: ITreeBoundingBox = {
|
||||
minX: obj.Position.x - (normalizedScale.x / 2),
|
||||
maxX: obj.Position.x + (normalizedScale.x / 2),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {quat} from '../tsm/quat';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
|
||||
export class Quaternion extends quat
|
||||
{
|
||||
@@ -9,24 +10,47 @@ export class Quaternion extends quat
|
||||
return q;
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | number[], pos?: number)
|
||||
static getXML(doc: XMLElementOrXMLNode, v?: Quaternion)
|
||||
{
|
||||
if (buf !== undefined && pos !== undefined && buf instanceof Buffer)
|
||||
if (v === undefined)
|
||||
{
|
||||
const x = buf.readFloatLE(pos);
|
||||
const y = buf.readFloatLE(pos + 4);
|
||||
const z = buf.readFloatLE(pos + 8);
|
||||
const xyzsum = 1.0 - x * x - y * y - z * z;
|
||||
const w = (xyzsum > 0.0) ? Math.sqrt(xyzsum) : 0;
|
||||
super([x, y, z, w]);
|
||||
v = Quaternion.getIdentity();
|
||||
}
|
||||
else if (buf !== undefined && Array.isArray(buf))
|
||||
doc.ele('X', v.x);
|
||||
doc.ele('Y', v.y);
|
||||
doc.ele('Z', v.z);
|
||||
doc.ele('W', v.w);
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | number[] | Quaternion, pos?: number)
|
||||
{
|
||||
if (buf instanceof Quaternion)
|
||||
{
|
||||
super(buf);
|
||||
super();
|
||||
this.x = buf.x;
|
||||
this.y = buf.y;
|
||||
this.z = buf.z;
|
||||
this.w = buf.w;
|
||||
}
|
||||
else
|
||||
{
|
||||
super();
|
||||
if (buf !== undefined && pos !== undefined && buf instanceof Buffer)
|
||||
{
|
||||
const x = buf.readFloatLE(pos);
|
||||
const y = buf.readFloatLE(pos + 4);
|
||||
const z = buf.readFloatLE(pos + 8);
|
||||
const xyzsum = 1.0 - x * x - y * y - z * z;
|
||||
const w = (xyzsum > 0.0) ? Math.sqrt(xyzsum) : 0;
|
||||
super([x, y, z, w]);
|
||||
}
|
||||
else if (buf !== undefined && Array.isArray(buf))
|
||||
{
|
||||
super(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
writeToBuffer(buf: Buffer, pos: number)
|
||||
|
||||
@@ -6,7 +6,7 @@ import {ClientEvents} from './ClientEvents';
|
||||
import {IObjectStore} from './interfaces/IObjectStore';
|
||||
import {ObjectStoreFull} from './ObjectStoreFull';
|
||||
import {ObjectStoreLite} from './ObjectStoreLite';
|
||||
import {BotOptionFlags, PacketFlags, RegionFlags, UUID} from '..';
|
||||
import {BotOptionFlags, PacketFlags, ParcelPropertiesEvent, RegionFlags, UUID, Vector2, Vector3} from '..';
|
||||
import {RequestRegionInfoMessage} from './messages/RequestRegionInfo';
|
||||
import {RegionInfoMessage} from './messages/RegionInfo';
|
||||
import {Message} from '../enums/Message';
|
||||
@@ -17,9 +17,32 @@ import {GridLayerType} from '../enums/GridLayerType';
|
||||
import {MapBlockReplyMessage} from './messages/MapBlockReply';
|
||||
import {FilterResponse} from '../enums/FilterResponse';
|
||||
import * as Long from 'long';
|
||||
import {Packet} from './Packet';
|
||||
import {LayerDataMessage} from './messages/LayerData';
|
||||
import {LayerType} from '../enums/LayerType';
|
||||
import {Subscription} from 'rxjs/internal/Subscription';
|
||||
import {BitPack} from './BitPack';
|
||||
import * as builder from 'xmlbuilder';
|
||||
import {SimAccessFlags} from '../enums/SimAccessFlags';
|
||||
import {Subject} from 'rxjs/internal/Subject';
|
||||
import {ParcelDwellRequestMessage} from './messages/ParcelDwellRequest';
|
||||
import {ParcelDwellReplyMessage} from './messages/ParcelDwellReply';
|
||||
import Timer = NodeJS.Timer;
|
||||
import {Parcel} from './public/Parcel';
|
||||
import {RegionEnvironment} from './public/RegionEnvironment';
|
||||
import {Color4} from './Color4';
|
||||
import {SkyPreset} from './public/interfaces/SkyPreset';
|
||||
import {Vector4} from './Vector4';
|
||||
import {WaterPreset} from './public/interfaces/WaterPreset';
|
||||
|
||||
export class Region
|
||||
{
|
||||
static CopyMatrix16: number[] = [];
|
||||
static CosineTable16: number[] = [];
|
||||
static DequantizeTable16: number[] = [];
|
||||
static setup = false;
|
||||
static OO_SQRT_2 = 0.7071067811865475244008443621049;
|
||||
|
||||
regionName: string;
|
||||
regionOwner: UUID;
|
||||
regionID: UUID;
|
||||
@@ -77,9 +100,180 @@ export class Region
|
||||
clientEvents: ClientEvents;
|
||||
options: BotOptionFlags;
|
||||
agent: Agent;
|
||||
messageSubscription: Subscription;
|
||||
parcelPropertiesSubscription: Subscription;
|
||||
|
||||
terrain: number[][] = [];
|
||||
tilesReceived = 0;
|
||||
terrainComplete = false;
|
||||
terrainCompleteEvent: Subject<void> = new Subject<void>();
|
||||
|
||||
parcelsComplete = false;
|
||||
parcelsCompleteEvent: Subject<void> = new Subject<void>();
|
||||
|
||||
parcels: {[key: number]: Parcel} = {};
|
||||
parcelsByUUID: {[key: string]: Parcel} = {};
|
||||
parcelMap: number[][] = [];
|
||||
|
||||
environment: RegionEnvironment;
|
||||
|
||||
static IDCTColumn16(linein: number[], lineout: number[], column: number)
|
||||
{
|
||||
let total: number;
|
||||
let usize: number;
|
||||
|
||||
for (let n = 0; n < 16; n++)
|
||||
{
|
||||
total = this.OO_SQRT_2 * linein[column];
|
||||
|
||||
for (let u = 1; u < 16; u++)
|
||||
{
|
||||
usize = u * 16;
|
||||
total += linein[usize + column] * this.CosineTable16[usize + n];
|
||||
}
|
||||
|
||||
lineout[16 * n + column] = total;
|
||||
}
|
||||
}
|
||||
|
||||
static IDCTLine16(linein: number[], lineout: number[], line: number)
|
||||
{
|
||||
const oosob: number = 2.0 / 16.0;
|
||||
const lineSize: number = line * 16;
|
||||
let total = 0;
|
||||
|
||||
for (let n = 0; n < 16; n++)
|
||||
{
|
||||
total = this.OO_SQRT_2 * linein[lineSize];
|
||||
|
||||
for (let u = 1; u < 16; u++)
|
||||
{
|
||||
total += linein[lineSize + u] * this.CosineTable16[u * 16 + n];
|
||||
}
|
||||
|
||||
lineout[lineSize + n] = total * oosob;
|
||||
}
|
||||
}
|
||||
|
||||
static InitialSetup()
|
||||
{
|
||||
// Build copy matrix 16
|
||||
{
|
||||
let diag = false;
|
||||
let right = true;
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
let count = 0;
|
||||
|
||||
for (let x = 0; x < 16 * 16; x++)
|
||||
{
|
||||
this.CopyMatrix16.push(0);
|
||||
this.DequantizeTable16.push(0);
|
||||
this.CosineTable16.push(0);
|
||||
}
|
||||
while (i < 16 && j < 16)
|
||||
{
|
||||
this.CopyMatrix16[j * 16 + i] = count++;
|
||||
|
||||
if (!diag)
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
if (i < 16 - 1)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
j++;
|
||||
}
|
||||
|
||||
right = false;
|
||||
diag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j < 16 - 1)
|
||||
{
|
||||
j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
right = true;
|
||||
diag = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
i++;
|
||||
j--;
|
||||
if (i === 16 - 1 || j === 0)
|
||||
{
|
||||
diag = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
j++;
|
||||
if (j === 16 - 1 || i === 0)
|
||||
{
|
||||
diag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
for (let j = 0; j < 16; j++)
|
||||
{
|
||||
for (let i = 0; i < 16; i++)
|
||||
{
|
||||
this.DequantizeTable16[j * 16 + i] = 1.0 + 2.0 * (i + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
const hposz: number = Math.PI * 0.5 / 16.0;
|
||||
|
||||
for (let u = 0; u < 16; u++)
|
||||
{
|
||||
for (let n = 0; n < 16; n++)
|
||||
{
|
||||
this.CosineTable16[u * 16 + n] = Math.cos((2.0 * n + 1.0) * u * hposz);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setup = true;
|
||||
}
|
||||
|
||||
constructor(agent: Agent, clientEvents: ClientEvents, options: BotOptionFlags)
|
||||
{
|
||||
if (!Region.setup)
|
||||
{
|
||||
Region.InitialSetup();
|
||||
}
|
||||
for (let x = 0; x < 256; x++)
|
||||
{
|
||||
this.terrain.push([]);
|
||||
for (let y = 0; y < 256; y++)
|
||||
{
|
||||
this.terrain[x].push(-1);
|
||||
}
|
||||
}
|
||||
for (let x = 0; x < 64; x++)
|
||||
{
|
||||
this.parcelMap.push([]);
|
||||
for (let y = 0; y < 64; y++)
|
||||
{
|
||||
this.parcelMap[x].push(0);
|
||||
}
|
||||
}
|
||||
this.agent = agent;
|
||||
this.options = options;
|
||||
this.clientEvents = clientEvents;
|
||||
@@ -93,7 +287,454 @@ export class Region
|
||||
this.objects = new ObjectStoreFull(this.circuit, agent, clientEvents, options);
|
||||
}
|
||||
this.comms = new Comms(this.circuit, agent, clientEvents);
|
||||
|
||||
this.parcelPropertiesSubscription = this.clientEvents.onParcelPropertiesEvent.subscribe(async (parcelProperties: ParcelPropertiesEvent) =>
|
||||
{
|
||||
// Get the parcel UUID
|
||||
const msg = new ParcelDwellRequestMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
msg.Data = {
|
||||
LocalID: parcelProperties.LocalID,
|
||||
ParcelID: UUID.zero()
|
||||
};
|
||||
|
||||
this.circuit.sendMessage(msg, PacketFlags.Reliable);
|
||||
const dwellReply = await this.circuit.waitForMessage<ParcelDwellReplyMessage>(Message.ParcelDwellReply, 1000, (message: ParcelDwellReplyMessage): FilterResponse =>
|
||||
{
|
||||
if (message.Data.LocalID === parcelProperties.LocalID)
|
||||
{
|
||||
return FilterResponse.Finish;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FilterResponse.NoMatch;
|
||||
}
|
||||
});
|
||||
const parcelID: string = dwellReply.Data.ParcelID.toString();
|
||||
let parcel = new Parcel();
|
||||
if (this.parcelsByUUID[parcelID])
|
||||
{
|
||||
parcel = this.parcelsByUUID[parcelID];
|
||||
}
|
||||
parcel.LocalID = parcelProperties.LocalID;
|
||||
parcel.ParcelID = dwellReply.Data.ParcelID;
|
||||
parcel.RegionDenyAgeUnverified = parcelProperties.RegionDenyTransacted;
|
||||
parcel.MediaDesc = parcelProperties.MediaDesc;
|
||||
parcel.MediaHeight = parcelProperties.MediaHeight;
|
||||
parcel.MediaLoop = parcelProperties.MediaLoop;
|
||||
parcel.MediaType = parcelProperties.MediaType;
|
||||
parcel.MediaWidth = parcelProperties.MediaWidth;
|
||||
parcel.ObscureMedia = parcelProperties.ObscureMedia;
|
||||
parcel.ObscureMusic = parcelProperties.ObscureMusic;
|
||||
parcel.AABBMax = parcelProperties.AABBMax;
|
||||
parcel.AABBMin = parcelProperties.AABBMin;
|
||||
parcel.AnyAVSounds = parcelProperties.AnyAVSounds;
|
||||
parcel.Area = parcelProperties.Area;
|
||||
parcel.AuctionID = parcelProperties.AuctionID;
|
||||
parcel.AuthBuyerID = parcelProperties.AuthBuyerID;
|
||||
parcel.Bitmap = parcelProperties.Bitmap;
|
||||
parcel.Category = parcelProperties.Category;
|
||||
parcel.ClaimDate = parcelProperties.ClaimDate;
|
||||
parcel.ClaimPrice = parcelProperties.ClaimPrice;
|
||||
parcel.Desc = parcelProperties.Desc;
|
||||
parcel.GroupAVSounds = parcelProperties.GroupAVSounds;
|
||||
parcel.GroupID = parcelProperties.GroupID;
|
||||
parcel.GroupPrims = parcelProperties.GroupPrims;
|
||||
parcel.IsGroupOwned = parcelProperties.IsGroupOwned;
|
||||
parcel.LandingType = parcelProperties.LandingType;
|
||||
parcel.MaxPrims = parcelProperties.MaxPrims;
|
||||
parcel.MediaAutoScale = parcelProperties.MediaAutoScale;
|
||||
parcel.MediaID = parcelProperties.MediaID;
|
||||
parcel.MediaURL = parcelProperties.MediaURL;
|
||||
parcel.MusicURL = parcelProperties.MusicURL;
|
||||
parcel.Name = parcelProperties.Name;
|
||||
parcel.OtherCleanTime = parcelProperties.OtherCleanTime;
|
||||
parcel.OtherCount = parcelProperties.OtherCount;
|
||||
parcel.OtherPrims = parcelProperties.OtherPrims;
|
||||
parcel.OwnerID = parcelProperties.OwnerID;
|
||||
parcel.OwnerPrims = parcelProperties.OwnerPrims;
|
||||
parcel.ParcelFlags = parcelProperties.ParcelFlags;
|
||||
parcel.ParcelPrimBonus = parcelProperties.ParcelPrimBonus;
|
||||
parcel.PassHours = parcelProperties.PassHours;
|
||||
parcel.PassPrice = parcelProperties.PassPrice;
|
||||
parcel.PublicCount = parcelProperties.PublicCount;
|
||||
parcel.RegionDenyAnonymous = parcelProperties.RegionDenyAnonymous;
|
||||
parcel.RegionDenyIdentified = parcelProperties.RegionDenyIdentified;
|
||||
parcel.RegionPushOverride = parcelProperties.RegionPushOverride;
|
||||
parcel.RegionDenyTransacted = parcelProperties.RegionDenyTransacted;
|
||||
parcel.RentPrice = parcelProperties.RentPrice;
|
||||
parcel.RequestResult = parcelProperties.RequestResult;
|
||||
parcel.SalePrice = parcelProperties.SalePrice;
|
||||
parcel.SeeAvs = parcelProperties.SeeAvs;
|
||||
parcel.SelectedPrims = parcelProperties.SelectedPrims;
|
||||
parcel.SelfCount = parcelProperties.SelfCount;
|
||||
parcel.SequenceID = parcelProperties.SequenceID;
|
||||
parcel.SimWideMaxPrims = parcelProperties.SimWideMaxPrims;
|
||||
parcel.SimWideTotalPrims = parcelProperties.SimWideTotalPrims;
|
||||
parcel.SnapSelection = parcelProperties.SnapSelection;
|
||||
parcel.SnapshotID = parcelProperties.SnapshotID;
|
||||
parcel.Status = parcelProperties.Status;
|
||||
parcel.TotalPrims = parcelProperties.TotalPrims;
|
||||
parcel.UserLocation = parcelProperties.UserLocation;
|
||||
parcel.UserLookAt = parcelProperties.UserLookAt;
|
||||
parcel.RegionAllowAccessOverride = parcelProperties.RegionAllowAccessOverride;
|
||||
this.parcels[parcelProperties.LocalID] = parcel;
|
||||
|
||||
let foundEmpty = false;
|
||||
for (let y = 0; y < 64; y++)
|
||||
{
|
||||
for (let x = 0; x < 64; x++)
|
||||
{
|
||||
let index = (y * 64) + x;
|
||||
const bit = index % 8;
|
||||
index >>= 3;
|
||||
if ((parcel.Bitmap[index] & (1 << bit)) !== 0)
|
||||
{
|
||||
this.parcelMap[y][x] = parcel.LocalID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.parcelMap[y][x] === 0)
|
||||
{
|
||||
foundEmpty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundEmpty === false)
|
||||
{
|
||||
if (this.parcelsComplete === false)
|
||||
{
|
||||
this.parcelsComplete = true;
|
||||
this.parcelsCompleteEvent.next();
|
||||
}
|
||||
}
|
||||
else if (this.parcelsComplete === true)
|
||||
{
|
||||
this.parcelsComplete = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.messageSubscription = this.circuit.subscribeToMessages([
|
||||
Message.LayerData
|
||||
], (packet: Packet) =>
|
||||
{
|
||||
switch (packet.message.id)
|
||||
{
|
||||
case Message.LayerData:
|
||||
const layerData: LayerDataMessage = packet.message as LayerDataMessage;
|
||||
const type: LayerType = layerData.LayerID.Type;
|
||||
|
||||
const nibbler = new BitPack(layerData.LayerData.Data, 0);
|
||||
|
||||
const stride = nibbler.UnpackBits(16);
|
||||
const patchSize = nibbler.UnpackBits(8);
|
||||
const headerLayerType = nibbler.UnpackBits(8);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LayerType.Land:
|
||||
if (headerLayerType === type) // Quick sanity check
|
||||
{
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
const patches: number[] = [];
|
||||
for (let xi = 0; xi < 32 * 32; xi++)
|
||||
{
|
||||
patches.push(0);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// DecodePatchHeader
|
||||
const quantWBits = nibbler.UnpackBits(8);
|
||||
if (quantWBits === 97)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const dcOffset = nibbler.UnpackFloat();
|
||||
const range = nibbler.UnpackBits(16);
|
||||
const patchIDs = nibbler.UnpackBits(10);
|
||||
const wordBits = (quantWBits & 0x0f) + 2;
|
||||
|
||||
x = patchIDs >> 5;
|
||||
y = patchIDs & 0x1F;
|
||||
if (x >= 16 || y >= 16)
|
||||
{
|
||||
console.error('Invalid land packet. x: ' + x + ', y: ' + y + ', patchSize: ' + patchSize);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decode patch
|
||||
let temp = 0;
|
||||
for (let n = 0; n < patchSize * patchSize; n++)
|
||||
{
|
||||
temp = nibbler.UnpackBits(1);
|
||||
if (temp !== 0)
|
||||
{
|
||||
temp = nibbler.UnpackBits(1);
|
||||
if (temp !== 0)
|
||||
{
|
||||
temp = nibbler.UnpackBits(1);
|
||||
if (temp !== 0)
|
||||
{
|
||||
// negative
|
||||
temp = nibbler.UnpackBits(wordBits);
|
||||
patches[n] = temp * -1;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// positive
|
||||
temp = nibbler.UnpackBits(wordBits);
|
||||
patches[n] = temp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (let o = n; o < patchSize * patchSize; o++)
|
||||
{
|
||||
patches[o] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
patches[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Decompress this patch
|
||||
const block: number[] = [];
|
||||
const output: number[] = [];
|
||||
|
||||
const prequant = (quantWBits >> 4) + 2;
|
||||
const quantize = 1 << prequant;
|
||||
const ooq = 1.0 / quantize;
|
||||
const mult = ooq * range;
|
||||
const addVal = mult * (1 << (prequant - 1)) + dcOffset;
|
||||
|
||||
if (patchSize === 16)
|
||||
{
|
||||
for (let n = 0; n < 16 * 16; n++)
|
||||
{
|
||||
block.push(patches[Region.CopyMatrix16[n]] * Region.DequantizeTable16[n])
|
||||
}
|
||||
|
||||
const ftemp: number[] = [];
|
||||
for (let o = 0; o < 16 * 16; o++)
|
||||
{
|
||||
ftemp.push(o);
|
||||
}
|
||||
for (let o = 0; o < 16; o++)
|
||||
{
|
||||
Region.IDCTColumn16(block, ftemp, o);
|
||||
}
|
||||
for (let o = 0; o < 16; o++)
|
||||
{
|
||||
Region.IDCTLine16(ftemp, block, o);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error('IDCTPatchLarge not implemented');
|
||||
}
|
||||
|
||||
for (let j = 0; j < block.length; j++)
|
||||
{
|
||||
output.push(block[j] * mult + addVal);
|
||||
}
|
||||
|
||||
let outputIndex = 0;
|
||||
for (let yPoint = y * 16; yPoint < (y + 1) * 16; yPoint++)
|
||||
{
|
||||
for (let xPoint = x * 16; xPoint < (x + 1) * 16; xPoint++)
|
||||
{
|
||||
if (this.terrain[yPoint][xPoint] === -1)
|
||||
{
|
||||
this.tilesReceived++;
|
||||
}
|
||||
this.terrain[yPoint][xPoint] = output[outputIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tilesReceived === 65536)
|
||||
{
|
||||
this.terrainComplete = true;
|
||||
this.terrainCompleteEvent.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
})
|
||||
}
|
||||
getParcels(): Parcel[]
|
||||
{
|
||||
const found: {[key: number]: Parcel} = {};
|
||||
for (let y = 0; y < 64; y++)
|
||||
{
|
||||
for (let x = 0; x < 64; x++)
|
||||
{
|
||||
if (this.parcelMap[y][x] !== 0)
|
||||
{
|
||||
const localID = this.parcelMap[y][x];
|
||||
if (!found[localID])
|
||||
{
|
||||
found[localID] = this.parcels[localID];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const result: Parcel[] = [];
|
||||
for (const key of Object.keys(found))
|
||||
{
|
||||
result.push(found[parseInt(key, 10)]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
resetParcels(): void
|
||||
{
|
||||
this.parcelMap = [];
|
||||
for (let x = 0; x < 64; x++)
|
||||
{
|
||||
this.parcelMap.push([]);
|
||||
for (let y = 0; y < 64; y++)
|
||||
{
|
||||
this.parcelMap[x].push(0);
|
||||
}
|
||||
}
|
||||
this.parcels = {};
|
||||
this.parcelsByUUID = {};
|
||||
this.parcelsComplete = false;
|
||||
}
|
||||
|
||||
waitForParcels(): Promise<void>
|
||||
{
|
||||
return new Promise<void>((resolve, reject) =>
|
||||
{
|
||||
if (this.parcelsComplete)
|
||||
{
|
||||
resolve();
|
||||
}
|
||||
else
|
||||
{
|
||||
let timeout: Timer | null = null;
|
||||
const subscription = this.parcelsCompleteEvent.subscribe(() =>
|
||||
{
|
||||
if (timeout !== null)
|
||||
{
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
timeout = setTimeout(() =>
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
reject(new Error('Timeout waiting for parcels'));
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
waitForTerrain(): Promise<void>
|
||||
{
|
||||
return new Promise<void>((resolve, reject) =>
|
||||
{
|
||||
if (this.terrainComplete)
|
||||
{
|
||||
resolve();
|
||||
}
|
||||
else
|
||||
{
|
||||
let timeout: Timer | null = null;
|
||||
const subscription = this.terrainCompleteEvent.subscribe(() =>
|
||||
{
|
||||
if (timeout !== null)
|
||||
{
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
timeout = setTimeout(() =>
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
reject(new Error('Timeout waiting for terrain'));
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getTerrainHeightAtPoint(x: number, y: number): number
|
||||
{
|
||||
const patchX = Math.floor(x / 16);
|
||||
const patchY = Math.floor(y / 16);
|
||||
x = x % 16;
|
||||
y = y % 16;
|
||||
|
||||
const p = this.terrain[patchY * 16 + patchX];
|
||||
if (p === null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return p[y * 16 + x];
|
||||
}
|
||||
|
||||
exportXML(): string
|
||||
{
|
||||
const document = builder.create('RegionSettings');
|
||||
const general = document.ele('General');
|
||||
general.ele('AllowDamage', (this.regionFlags & RegionFlags.AllowDamage) ? 'True' : 'False');
|
||||
general.ele('AllowLandResell', !(this.regionFlags & RegionFlags.BlockLandResell) ? 'True' : 'False');
|
||||
general.ele('AllowLandJoinDivide', (this.regionFlags & RegionFlags.AllowParcelChanges) ? 'True' : 'False');
|
||||
general.ele('BlockFly', (this.regionFlags & RegionFlags.NoFly) ? 'True' : 'False');
|
||||
general.ele('BlockLandShowInSearch', (this.regionFlags & RegionFlags.BlockParcelSearch) ? 'True' : 'False');
|
||||
general.ele('BlockTerraform', (this.regionFlags & RegionFlags.BlockTerraform) ? 'True' : 'False');
|
||||
general.ele('DisableCollisions', (this.regionFlags & RegionFlags.SkipCollisions) ? 'True' : 'False');
|
||||
general.ele('DisablePhysics', (this.regionFlags & RegionFlags.SkipPhysics) ? 'True' : 'False');
|
||||
general.ele('DisableScripts', (this.regionFlags & RegionFlags.EstateSkipScripts) ? 'True' : 'False');
|
||||
general.ele('MaturityRating', (this.simAccess & SimAccessFlags.Mature & SimAccessFlags.Adult & SimAccessFlags.PG));
|
||||
general.ele('RestrictPushing', (this.regionFlags & RegionFlags.RestrictPushObject) ? 'True' : 'False');
|
||||
general.ele('AgentLimit', this.maxAgents);
|
||||
general.ele('ObjectBonus', this.objectBonusFactor);
|
||||
const groundTextures = document.ele('GroundTextures');
|
||||
groundTextures.ele('Texture1', this.terrainDetail0.toString());
|
||||
groundTextures.ele('Texture2', this.terrainDetail1.toString());
|
||||
groundTextures.ele('Texture3', this.terrainDetail2.toString());
|
||||
groundTextures.ele('Texture4', this.terrainDetail3.toString());
|
||||
|
||||
groundTextures.ele('ElevationLowSW', this.terrainStartHeight00);
|
||||
groundTextures.ele('ElevationLowNW', this.terrainStartHeight01);
|
||||
groundTextures.ele('ElevationLowSE', this.terrainStartHeight10);
|
||||
groundTextures.ele('ElevationLowNE', this.terrainStartHeight11);
|
||||
|
||||
groundTextures.ele('ElevationHighSW', this.terrainHeightRange00);
|
||||
groundTextures.ele('ElevationHighNW', this.terrainHeightRange01);
|
||||
groundTextures.ele('ElevationHighSE', this.terrainHeightRange10);
|
||||
groundTextures.ele('ElevationHighNE', this.terrainHeightRange11);
|
||||
|
||||
const terrain = document.ele('Terrain');
|
||||
terrain.ele('WaterHeight', this.waterHeight);
|
||||
terrain.ele('TerrainRaiseLimit', this.terrainRaiseLimit);
|
||||
terrain.ele('TerrainLowerLimit', this.terrainLowerLimit);
|
||||
terrain.ele('UseEstateSun', (this.useEstateSun) ? 'True' : 'False');
|
||||
terrain.ele('FixedSun', (this.regionFlags & RegionFlags.SunFixed) ? 'True' : 'False');
|
||||
terrain.ele('SunPosition', this.sunHour);
|
||||
this.environment.getXML(document);
|
||||
return document.end({pretty: true, allowEmpty: true});
|
||||
}
|
||||
|
||||
activateCaps(seedURL: string)
|
||||
{
|
||||
this.caps = new Caps(this.agent, this, seedURL, this.clientEvents);
|
||||
@@ -185,9 +826,93 @@ export class Region
|
||||
}
|
||||
return FilterResponse.NoMatch;
|
||||
});
|
||||
|
||||
this.environment = new RegionEnvironment();
|
||||
this.environment.dayCycleKeyframes = [];
|
||||
this.environment.skyPresets = {};
|
||||
this.environment.water = {
|
||||
blurMultiplier: 0,
|
||||
fresnelOffset: 0,
|
||||
fresnelScale: 0,
|
||||
normalScale: Vector3.getZero(),
|
||||
normalMap: UUID.zero(),
|
||||
scaleAbove: 0,
|
||||
scaleBelow: 0,
|
||||
underWaterFogMod: 0,
|
||||
waterFogColor: Color4.white,
|
||||
waterFogDensity: 0,
|
||||
wave1Dir: Vector2.getZero(),
|
||||
wave2Dir: Vector2.getZero()
|
||||
};
|
||||
|
||||
await this.caps.waitForSeedCapability();
|
||||
const response = await this.caps.capsGetXML('EnvironmentSettings');
|
||||
if (response.length >= 4)
|
||||
{
|
||||
if (Array.isArray(response[1]) && typeof response[2] === 'object' && typeof response[3] === 'object')
|
||||
{
|
||||
for (const kf of response[1])
|
||||
{
|
||||
this.environment.dayCycleKeyframes.push({
|
||||
time: kf[0],
|
||||
preset: kf[1]
|
||||
});
|
||||
}
|
||||
for (const presetKey of Object.keys(response[2]))
|
||||
{
|
||||
const preset = response[2][presetKey];
|
||||
this.environment.skyPresets[presetKey] = new class implements SkyPreset
|
||||
{
|
||||
ambient = new Vector4(preset['ambient']);
|
||||
blueDensity = new Vector4(preset['blue_density']);
|
||||
blueHorizon = new Vector4(preset['blue_horizon']);
|
||||
cloudColor = new Color4(preset['cloud_color']);
|
||||
cloudPosDensity1 = new Vector4(preset['cloud_pos_density1']);
|
||||
cloudPosDensity2 = new Vector4(preset['cloud_pos_density2']);
|
||||
cloudScale = new Vector4(preset['cloud_scale']);
|
||||
cloudScrollRate = new Vector2(preset['cloud_scroll_rate']);
|
||||
cloudShadow = new Vector4(preset['cloud_shadow']);
|
||||
densityMultiplier = new Vector4(preset['density_multiplier']);
|
||||
distanceMultiplier = new Vector4(preset['distance_multiplier']);
|
||||
eastAngle = preset['east_angle'];
|
||||
enableCloudScroll = {
|
||||
x: preset['enable_cloud_scroll'][0],
|
||||
y: preset['enable_cloud_scroll'][1]
|
||||
};
|
||||
gamma = new Vector4(preset['gamma']);
|
||||
glow = new Vector4(preset['glow']);
|
||||
hazeDensity = new Vector4(preset['haze_density']);
|
||||
hazeHorizon = new Vector4(preset['haze_horizon']);
|
||||
lightNormal = new Vector4(preset['lightnorm']);
|
||||
maxY = new Vector4(preset['max_y']);
|
||||
starBrightness = preset['start_brightness'];
|
||||
sunAngle = preset['sun_angle'];
|
||||
sunlightColor = new Color4(preset['sunlight_color']);
|
||||
};
|
||||
}
|
||||
const wat = response[3];
|
||||
this.environment.water = new class implements WaterPreset
|
||||
{
|
||||
blurMultiplier = wat['blurMultiplier'];
|
||||
fresnelOffset = wat['fresnelOffset'];
|
||||
fresnelScale = wat['fresnelScale'];
|
||||
normalScale = new Vector3(wat['normScale']);
|
||||
normalMap = new UUID(wat['normalMap'].toString());
|
||||
scaleAbove = wat['scaleAbove'];
|
||||
scaleBelow = wat['scaleBelow'];
|
||||
underWaterFogMod = wat['underWaterFogMod'];
|
||||
waterFogColor = new Color4(wat['waterFogColor']);
|
||||
waterFogDensity = wat['waterFogDensity'];
|
||||
wave1Dir = new Vector2(wat['wave1Dir']);
|
||||
wave2Dir = new Vector2(wat['wave2Dir']);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
shutdown()
|
||||
{
|
||||
this.parcelPropertiesSubscription.unsubscribe();
|
||||
this.messageSubscription.unsubscribe();
|
||||
this.comms.shutdown();
|
||||
this.caps.shutdown();
|
||||
this.objects.shutdown();
|
||||
|
||||
@@ -7,6 +7,7 @@ export class TextureEntry
|
||||
{
|
||||
defaultTexture: TextureEntryFace | null;
|
||||
faces: TextureEntryFace[] = [];
|
||||
binary: Buffer;
|
||||
|
||||
static readFaceBitfield(buf: Buffer, pos: number): {
|
||||
result: boolean,
|
||||
@@ -40,6 +41,7 @@ export class TextureEntry
|
||||
|
||||
constructor(buf: Buffer)
|
||||
{
|
||||
this.binary = buf;
|
||||
if (buf.length < 16)
|
||||
{
|
||||
this.defaultTexture = null;
|
||||
@@ -47,7 +49,7 @@ export class TextureEntry
|
||||
else
|
||||
{
|
||||
this.defaultTexture = new TextureEntryFace(null);
|
||||
let pos = 0;
|
||||
const pos = 0;
|
||||
let i = pos;
|
||||
|
||||
// Texture
|
||||
@@ -241,7 +243,7 @@ export class TextureEntry
|
||||
|
||||
// Material
|
||||
{
|
||||
this.defaultTexture.materialb = buf[i++];
|
||||
this.defaultTexture.material = buf[i++];
|
||||
|
||||
let done = false;
|
||||
while (!done)
|
||||
@@ -257,7 +259,7 @@ export class TextureEntry
|
||||
if ((result.faceBits & bit) !== 0)
|
||||
{
|
||||
this.createFace(face);
|
||||
this.faces[face].materialb = tmpByte;
|
||||
this.faces[face].material = tmpByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +268,7 @@ export class TextureEntry
|
||||
|
||||
// Media
|
||||
{
|
||||
this.defaultTexture.mediab = buf[i++];
|
||||
this.defaultTexture.media = buf[i++];
|
||||
|
||||
let done = false;
|
||||
while (i - pos < buf.length && !done)
|
||||
@@ -282,7 +284,7 @@ export class TextureEntry
|
||||
if ((result.faceBits & bit) !== 0)
|
||||
{
|
||||
this.createFace(face);
|
||||
this.faces[face].mediab = tmpByte;
|
||||
this.faces[face].media = tmpByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import {UUID} from './UUID';
|
||||
import {Color4} from './Color4';
|
||||
import {TextureFlags} from '../enums/TextureFlags';
|
||||
import {Bumpiness} from '../enums/Bumpiness';
|
||||
import {Shininess} from '../enums/Shininess';
|
||||
import {MappingType} from '../enums/MappingType';
|
||||
|
||||
export class TextureEntryFace
|
||||
{
|
||||
private BUMP_MASK = 0x1F;
|
||||
private FULLBRIGHT_MASK = 0x20;
|
||||
private SHINY_MASK = 0xC0;
|
||||
private MEDIA_MASK = 0x01;
|
||||
private TEX_MAP_MASK = 0x06;
|
||||
static BUMP_MASK = 0x1F;
|
||||
static FULLBRIGHT_MASK = 0x20;
|
||||
static SHINY_MASK = 0xC0;
|
||||
static MEDIA_MASK = 0x01;
|
||||
static TEX_MAP_MASK = 0x06;
|
||||
|
||||
textureID: UUID;
|
||||
rgba: Color4;
|
||||
@@ -17,11 +20,16 @@ export class TextureEntryFace
|
||||
offsetU: number;
|
||||
offsetV: number;
|
||||
rotation: number;
|
||||
materialb: number;
|
||||
mediab: number;
|
||||
glow: number;
|
||||
materialID: UUID;
|
||||
bumpiness: Bumpiness = Bumpiness.None;
|
||||
shininess: Shininess = Shininess.None;
|
||||
mappingType: MappingType = MappingType.Default;
|
||||
fullBright = false;
|
||||
mediaFlags = false;
|
||||
|
||||
private materialb: number;
|
||||
private mediab: number;
|
||||
private hasAttribute: TextureFlags;
|
||||
private defaultTexture: TextureEntryFace | null;
|
||||
|
||||
@@ -40,4 +48,50 @@ export class TextureEntryFace
|
||||
this.hasAttribute = TextureFlags.None;
|
||||
}
|
||||
}
|
||||
|
||||
get material(): number
|
||||
{
|
||||
return this.materialb;
|
||||
}
|
||||
|
||||
set material(material: number)
|
||||
{
|
||||
this.materialb = material;
|
||||
if ((this.hasAttribute & TextureFlags.Material) !== 0)
|
||||
{
|
||||
this.bumpiness = this.materialb & TextureEntryFace.BUMP_MASK;
|
||||
this.shininess = this.materialb & TextureEntryFace.SHINY_MASK;
|
||||
this.fullBright = ((this.materialb & TextureEntryFace.FULLBRIGHT_MASK) !== 0);
|
||||
}
|
||||
else if (this.defaultTexture !== null)
|
||||
{
|
||||
this.bumpiness = this.defaultTexture.bumpiness;
|
||||
this.shininess = this.defaultTexture.shininess;
|
||||
this.fullBright = this.defaultTexture.fullBright;
|
||||
}
|
||||
}
|
||||
|
||||
get media(): number
|
||||
{
|
||||
return this.mediab;
|
||||
}
|
||||
|
||||
set media(media: number)
|
||||
{
|
||||
this.mediab = media;
|
||||
if ((this.hasAttribute & TextureFlags.Media) !== 0)
|
||||
{
|
||||
this.mappingType = media & TextureEntryFace.TEX_MAP_MASK;
|
||||
this.mediaFlags = ((media & TextureEntryFace.MEDIA_MASK) !== 0);
|
||||
}
|
||||
else if (this.defaultTexture !== null)
|
||||
{
|
||||
this.mappingType = this.defaultTexture.mappingType;
|
||||
this.mediaFlags = this.defaultTexture.mediaFlags;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error('No media attribute and default texture is null');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import * as validator from 'validator';
|
||||
import * as builder from 'xmlbuilder';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
import * as Long from 'long';
|
||||
const uuid = require('uuid');
|
||||
|
||||
export class UUID
|
||||
@@ -15,6 +18,24 @@ export class UUID
|
||||
return new UUID(newUUID);
|
||||
}
|
||||
|
||||
static getString(u?: UUID): string
|
||||
{
|
||||
if (u === undefined)
|
||||
{
|
||||
return UUID.zero().toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return u.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static getXML(doc: XMLElementOrXMLNode, u?: UUID)
|
||||
{
|
||||
const str = UUID.getString(u);
|
||||
doc.ele('UUID', str);
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | string, pos?: number)
|
||||
{
|
||||
if (buf !== undefined)
|
||||
@@ -78,4 +99,29 @@ export class UUID
|
||||
return cmp.equals(this.mUUID);
|
||||
}
|
||||
}
|
||||
|
||||
public getBuffer()
|
||||
{
|
||||
const buf = Buffer.allocUnsafe(16);
|
||||
this.writeToBuffer(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public getLong()
|
||||
{
|
||||
const buf = this.getBuffer();
|
||||
return new Long(buf.readUInt32LE(7), buf.readUInt32LE(12));
|
||||
}
|
||||
|
||||
public bitwiseOr(w: UUID): UUID
|
||||
{
|
||||
const buf1 = this.getBuffer();
|
||||
const buf2 = w.getBuffer();
|
||||
const buf3 = Buffer.allocUnsafe(16);
|
||||
for (let x = 0; x < 16; x++)
|
||||
{
|
||||
buf3[x] = buf1[x] ^ buf2[x];
|
||||
}
|
||||
return new UUID(buf3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,29 @@ export class Utils
|
||||
return buf.toString('utf8');
|
||||
}
|
||||
}
|
||||
static JSONStringify(obj: object, space: number)
|
||||
{
|
||||
const cache: any[] = [];
|
||||
return JSON.stringify(obj, function (key, value)
|
||||
{
|
||||
if (typeof value === 'object' && value !== null)
|
||||
{
|
||||
if (cache.indexOf(value) !== -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
return 'Circular Reference';
|
||||
}
|
||||
}
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
}, space);
|
||||
}
|
||||
static BufferToString(buf: Buffer, startPos?: number):
|
||||
{
|
||||
readLength: number,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {vec2} from '../tsm/vec2';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
|
||||
export class Vector2 extends vec2
|
||||
{
|
||||
@@ -7,6 +8,16 @@ export class Vector2 extends vec2
|
||||
return new Vector2();
|
||||
}
|
||||
|
||||
static getXML(doc: XMLElementOrXMLNode, v?: Vector2)
|
||||
{
|
||||
if (v === undefined)
|
||||
{
|
||||
v = Vector2.getZero();
|
||||
}
|
||||
doc.ele('X', v.x);
|
||||
doc.ele('Y', v.y);
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | number[], pos?: number, double?: boolean)
|
||||
{
|
||||
if (double === undefined)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {vec3} from '../tsm/vec3';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
|
||||
export class Vector3 extends vec3
|
||||
{
|
||||
@@ -7,36 +8,57 @@ export class Vector3 extends vec3
|
||||
return new Vector3();
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | number[], pos?: number, double?: boolean)
|
||||
static getXML(doc: XMLElementOrXMLNode, v?: Vector3)
|
||||
{
|
||||
if (double === undefined)
|
||||
if (v === undefined)
|
||||
{
|
||||
double = false;
|
||||
v = Vector3.getZero();
|
||||
}
|
||||
if (buf !== undefined && pos !== undefined && buf instanceof Buffer)
|
||||
doc.ele('X', v.x);
|
||||
doc.ele('Y', v.y);
|
||||
doc.ele('Z', v.z);
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | number[] | Vector3, pos?: number, double?: boolean)
|
||||
{
|
||||
if (buf instanceof Vector3)
|
||||
{
|
||||
if (!double)
|
||||
{
|
||||
const x = buf.readFloatLE(pos);
|
||||
const y = buf.readFloatLE(pos + 4);
|
||||
const z = buf.readFloatLE(pos + 8);
|
||||
super([x, y, z]);
|
||||
}
|
||||
else
|
||||
{
|
||||
const x = buf.readDoubleLE(pos);
|
||||
const y = buf.readDoubleLE(pos + 8);
|
||||
const z = buf.readDoubleLE(pos + 16);
|
||||
super([x, y, z]);
|
||||
}
|
||||
}
|
||||
else if (buf !== undefined && Array.isArray(buf))
|
||||
{
|
||||
super(buf);
|
||||
super();
|
||||
this.x = buf.x;
|
||||
this.y = buf.y;
|
||||
this.z = buf.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
super();
|
||||
if (double === undefined)
|
||||
{
|
||||
double = false;
|
||||
}
|
||||
if (buf !== undefined && pos !== undefined && buf instanceof Buffer)
|
||||
{
|
||||
if (!double)
|
||||
{
|
||||
const x = buf.readFloatLE(pos);
|
||||
const y = buf.readFloatLE(pos + 4);
|
||||
const z = buf.readFloatLE(pos + 8);
|
||||
super([x, y, z]);
|
||||
}
|
||||
else
|
||||
{
|
||||
const x = buf.readDoubleLE(pos);
|
||||
const y = buf.readDoubleLE(pos + 8);
|
||||
const z = buf.readDoubleLE(pos + 16);
|
||||
super([x, y, z]);
|
||||
}
|
||||
}
|
||||
else if (buf !== undefined && Array.isArray(buf))
|
||||
{
|
||||
super(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
writeToBuffer(buf: Buffer, pos: number, double: boolean)
|
||||
@@ -58,4 +80,6 @@ export class Vector3 extends vec3
|
||||
{
|
||||
return '<' + this.x + ', ' + this.y + ', ' + this.z + '>';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {vec4} from '../tsm/vec4';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
|
||||
export class Vector4 extends vec4
|
||||
{
|
||||
@@ -7,6 +8,18 @@ export class Vector4 extends vec4
|
||||
return new Vector4();
|
||||
}
|
||||
|
||||
static getXML(doc: XMLElementOrXMLNode, v?: Vector4)
|
||||
{
|
||||
if (v === undefined)
|
||||
{
|
||||
v = Vector4.getZero();
|
||||
}
|
||||
doc.ele('X', v.x);
|
||||
doc.ele('Y', v.y);
|
||||
doc.ele('Z', v.z);
|
||||
doc.ele('W', v.w);
|
||||
}
|
||||
|
||||
constructor(buf?: Buffer | number[], pos?: number)
|
||||
{
|
||||
if (buf !== undefined && pos !== undefined && buf instanceof Buffer)
|
||||
|
||||
@@ -2,13 +2,313 @@ import {CommandsBase} from './CommandsBase';
|
||||
import {UUID} from '../UUID';
|
||||
import * as LLSD from '@caspertech/llsd';
|
||||
import {Utils} from '../Utils';
|
||||
import {HTTPAssets} from '../..';
|
||||
import {AssetType, HTTPAssets, PacketFlags} from '../..';
|
||||
import {PermissionMask} from '../../enums/PermissionMask';
|
||||
import * as zlib from 'zlib';
|
||||
import {ZlibOptions} from 'zlib';
|
||||
import {Material} from '../public/Material';
|
||||
import {Color4} from '../Color4';
|
||||
import {TransferRequestMessage} from '../messages/TransferRequest';
|
||||
import {TransferChannelType} from '../../enums/TransferChannelType';
|
||||
import {TransferSourceType} from '../../enums/TransferSourceTypes';
|
||||
import {TransferInfoMessage} from '../messages/TransferInfo';
|
||||
import {Message} from '../../enums/Message';
|
||||
import {Packet} from '../Packet';
|
||||
import {TransferPacketMessage} from '../messages/TransferPacket';
|
||||
import {TransferAbortMessage} from '../messages/TransferAbort';
|
||||
import {TransferStatus} from '../../enums/TransferStatus';
|
||||
|
||||
export class AssetCommands extends CommandsBase
|
||||
{
|
||||
async downloadAsset(type: HTTPAssets, uuid: UUID): Promise<Buffer>
|
||||
{
|
||||
return await this.currentRegion.caps.downloadAsset(uuid, type);
|
||||
const result = await this.currentRegion.caps.downloadAsset(uuid, type);
|
||||
if (result.toString('UTF-8').trim() === 'Not found!')
|
||||
{
|
||||
throw new Error('Asset not found');
|
||||
}
|
||||
else if (result.toString('UTF-8').trim() === 'Incorrect Syntax')
|
||||
{
|
||||
throw new Error('Invalid Syntax');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
downloadInventoryAsset(itemID: UUID, ownerID: UUID, type: AssetType, priority: boolean, objectID: UUID = UUID.zero(), assetID: UUID = UUID.zero(), outAssetID?: { assetID: UUID }): Promise<Buffer>
|
||||
{
|
||||
return new Promise<Buffer>((resolve, reject) =>
|
||||
{
|
||||
const transferParams = Buffer.allocUnsafe(100);
|
||||
let pos = 0;
|
||||
this.agent.agentID.writeToBuffer(transferParams, pos);
|
||||
pos = pos + 16;
|
||||
this.circuit.sessionID.writeToBuffer(transferParams, pos);
|
||||
pos = pos + 16;
|
||||
ownerID.writeToBuffer(transferParams, pos);
|
||||
pos = pos + 16;
|
||||
objectID.writeToBuffer(transferParams, pos);
|
||||
pos = pos + 16;
|
||||
itemID.writeToBuffer(transferParams, pos);
|
||||
pos = pos + 16;
|
||||
assetID.writeToBuffer(transferParams, pos);
|
||||
pos = pos + 16;
|
||||
transferParams.writeInt32LE(type, pos);
|
||||
|
||||
const transferID = UUID.random();
|
||||
|
||||
const msg = new TransferRequestMessage();
|
||||
msg.TransferInfo = {
|
||||
TransferID: transferID,
|
||||
ChannelType: TransferChannelType.Asset,
|
||||
SourceType: TransferSourceType.SimInventoryItem,
|
||||
Priority: 100.0 + (priority ? 1.0 : 0.0),
|
||||
Params: transferParams
|
||||
};
|
||||
|
||||
this.circuit.sendMessage(msg, PacketFlags.Reliable);
|
||||
let gotInfo = true;
|
||||
let expectedSize = 0;
|
||||
const packets: {[key: number]: Buffer} = {};
|
||||
const subscription = this.circuit.subscribeToMessages([
|
||||
Message.TransferInfo,
|
||||
Message.TransferAbort,
|
||||
Message.TransferPacket
|
||||
], (packet: Packet) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (packet.message.id)
|
||||
{
|
||||
case Message.TransferPacket:
|
||||
{
|
||||
const messg = packet.message as TransferPacketMessage;
|
||||
packets[messg.TransferData.Packet] = messg.TransferData.Data;
|
||||
switch (messg.TransferData.Status)
|
||||
{
|
||||
case TransferStatus.Abort:
|
||||
throw new Error('Transfer Aborted');
|
||||
case TransferStatus.Error:
|
||||
throw new Error('Error');
|
||||
case TransferStatus.Skip:
|
||||
console.error('TransferPacket: Skip! not sure what this means');
|
||||
break;
|
||||
case TransferStatus.InsufficientPermissions:
|
||||
throw new Error('Insufficient Permissions');
|
||||
case TransferStatus.NotFound:
|
||||
throw new Error('Not Found');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message.TransferInfo:
|
||||
{
|
||||
const messg = packet.message as TransferInfoMessage;
|
||||
if (!messg.TransferInfo.TransferID.equals(transferID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
const status = messg.TransferInfo.Status as TransferStatus;
|
||||
switch (status)
|
||||
{
|
||||
case TransferStatus.OK:
|
||||
expectedSize = messg.TransferInfo.Size;
|
||||
gotInfo = true;
|
||||
if (outAssetID !== undefined)
|
||||
{
|
||||
outAssetID.assetID = new UUID(messg.TransferInfo.Params, 80);
|
||||
}
|
||||
break;
|
||||
case TransferStatus.Abort:
|
||||
throw new Error('Transfer Aborted');
|
||||
case TransferStatus.Error:
|
||||
throw new Error('Error');
|
||||
// See if we get anything else
|
||||
break;
|
||||
case TransferStatus.Skip:
|
||||
console.error('TransferInfo: Skip! not sure what this means');
|
||||
break;
|
||||
case TransferStatus.InsufficientPermissions:
|
||||
throw new Error('Insufficient Permissions');
|
||||
case TransferStatus.NotFound:
|
||||
throw new Error('Not Found');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Message.TransferAbort:
|
||||
{
|
||||
console.log('GOT TRANSFERABORT');
|
||||
const messg = packet.message as TransferAbortMessage;
|
||||
if (!messg.TransferInfo.TransferID.equals(transferID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
throw new Error('Transfer Aborted');
|
||||
}
|
||||
}
|
||||
if (gotInfo)
|
||||
{
|
||||
let gotSize = 0;
|
||||
for (const packetNum of Object.keys(packets))
|
||||
{
|
||||
const pn: number = parseInt(packetNum, 10);
|
||||
gotSize += packets[pn].length;
|
||||
}
|
||||
if (gotSize >= expectedSize)
|
||||
{
|
||||
const packetNumbers = Object.keys(packets).sort();
|
||||
const buffers = [];
|
||||
for (const pn of packetNumbers)
|
||||
{
|
||||
buffers.push(packets[parseInt(pn, 10)]);
|
||||
}
|
||||
subscription.unsubscribe();
|
||||
resolve(Buffer.concat(buffers));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private getMaterialsLimited(uuidArray: any[], uuids: {[key: string]: Material | null}): Promise<void>
|
||||
{
|
||||
return new Promise<void>((resolve, reject) =>
|
||||
{
|
||||
const binary = LLSD.LLSD.formatBinary(uuidArray);
|
||||
const options: ZlibOptions = {
|
||||
level: 9
|
||||
};
|
||||
zlib.deflate(Buffer.from(binary.toArray()), options, async (error: Error | null, res: Buffer) =>
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
const result = await this.currentRegion.caps.capsRequestXML('RenderMaterials', {
|
||||
'Zipped': new LLSD.LLSD.asBinary(res.toString('base64'))
|
||||
}, false);
|
||||
|
||||
const resultZipped = Buffer.from(result['Zipped'].octets);
|
||||
zlib.inflate(resultZipped, async (err: Error | null, reslt: Buffer) =>
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
const binData = new LLSD.Binary(Array.from(reslt), 'BASE64');
|
||||
const obj = LLSD.LLSD.parseBinary(binData);
|
||||
if (obj.length > 0)
|
||||
{
|
||||
for (const mat of obj)
|
||||
{
|
||||
if (mat['ID'])
|
||||
{
|
||||
const nbuf = Buffer.from(mat['ID'].toArray());
|
||||
const nuuid = new UUID(nbuf, 0).toString();
|
||||
if (uuids[nuuid] !== undefined)
|
||||
{
|
||||
if (mat['Material'])
|
||||
{
|
||||
const material = new Material();
|
||||
material.alphaMaskCutoff = mat['Material']['AlphaMaskCutoff'];
|
||||
material.diffuseAlphaMode = mat['Material']['DiffuseAlphaMode'];
|
||||
material.envIntensity = mat['Material']['EnvIntensity'];
|
||||
material.normMap = new UUID(mat['Material']['NormMap'].toString());
|
||||
material.normOffsetX = mat['Material']['NormOffsetX'];
|
||||
material.normOffsetY = mat['Material']['NormOffsetY'];
|
||||
material.normRepeatX = mat['Material']['NormRepeatX'];
|
||||
material.normRepeatY = mat['Material']['NormRepeatY'];
|
||||
material.normRotation = mat['Material']['NormRotation'];
|
||||
material.specColor = new Color4(mat['Material']['SpecColor'][0], mat['Material']['SpecColor'][1], mat['Material']['SpecColor'][2], mat['Material']['SpecColor'][3]);
|
||||
material.specExp = mat['Material']['SpecExp'];
|
||||
material.specMap = new UUID(mat['Material']['SpecMap'].toString());
|
||||
material.specOffsetX = mat['Material']['SpecOffsetX'];
|
||||
material.specOffsetY = mat['Material']['SpecOffsetY'];
|
||||
material.specRepeatX = mat['Material']['SpecRepeatX'];
|
||||
material.specRepeatY = mat['Material']['SpecRepeatY'];
|
||||
material.specRotation = mat['Material']['SpecRotation'];
|
||||
material.llsd = LLSD.LLSD.formatXML(mat['Material']);
|
||||
uuids[nuuid] = material;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
else
|
||||
{
|
||||
reject(new Error('Material data not found'));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getMaterials(uuids: {[key: string]: Material | null}): Promise<void>
|
||||
{
|
||||
let uuidArray: any[] = [];
|
||||
let submittedUUIDS: {[key: string]: Material | null} = {};
|
||||
for (const uuid of Object.keys(uuids))
|
||||
{
|
||||
if (uuidArray.length > 32)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.getMaterialsLimited(uuidArray, submittedUUIDS);
|
||||
let resolvedCount = 0;
|
||||
let totalCount = 0;
|
||||
for (const uu of Object.keys(submittedUUIDS))
|
||||
{
|
||||
if (submittedUUIDS[uu] !== null)
|
||||
{
|
||||
resolvedCount++;
|
||||
uuids[uu] = submittedUUIDS[uu];
|
||||
}
|
||||
totalCount++;
|
||||
}
|
||||
console.log('Resolved ' + resolvedCount + ' of ' + totalCount + ' materials');
|
||||
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error(error);
|
||||
}
|
||||
uuidArray = [];
|
||||
submittedUUIDS = {};
|
||||
}
|
||||
if (!submittedUUIDS[uuid])
|
||||
{
|
||||
submittedUUIDS[uuid] = uuids[uuid];
|
||||
uuidArray.push(new LLSD.Binary(Array.from(new UUID(uuid).getBuffer())))
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
await this.getMaterialsLimited(uuidArray, submittedUUIDS);
|
||||
let resolvedCount = 0;
|
||||
let totalCount = 0;
|
||||
for (const uu of Object.keys(submittedUUIDS))
|
||||
{
|
||||
if (submittedUUIDS[uu] !== null)
|
||||
{
|
||||
resolvedCount++;
|
||||
uuids[uu] = submittedUUIDS[uu];
|
||||
}
|
||||
totalCount++;
|
||||
}
|
||||
console.log('Resolved ' + resolvedCount + ' of ' + totalCount + ' materials (end)');
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
uploadAsset(type: HTTPAssets, data: Buffer, name: string, description: string): Promise<UUID>
|
||||
|
||||
@@ -5,17 +5,28 @@ import {RegionHandleRequestMessage} from '../messages/RegionHandleRequest';
|
||||
import {Message} from '../../enums/Message';
|
||||
import {FilterResponse} from '../../enums/FilterResponse';
|
||||
import {RegionIDAndHandleReplyMessage} from '../messages/RegionIDAndHandleReply';
|
||||
import {PacketFlags, PCode, Vector3} from '../..';
|
||||
import {AssetType, PacketFlags, PCode, Vector3} from '../..';
|
||||
import {ObjectGrabMessage} from '../messages/ObjectGrab';
|
||||
import {ObjectDeGrabMessage} from '../messages/ObjectDeGrab';
|
||||
import {ObjectGrabUpdateMessage} from '../messages/ObjectGrabUpdate';
|
||||
import {GameObject} from '../GameObject';
|
||||
import {GameObject} from '../public/GameObject';
|
||||
import {ObjectSelectMessage} from '../messages/ObjectSelect';
|
||||
import {ObjectPropertiesMessage} from '../messages/ObjectProperties';
|
||||
import {Utils} from '../Utils';
|
||||
import {ObjectDeselectMessage} from '../messages/ObjectDeselect';
|
||||
import * as micromatch from 'micromatch';
|
||||
import * as LLSD from "@caspertech/llsd";
|
||||
import * as LLSD from '@caspertech/llsd';
|
||||
import {PrimFlags} from '../../enums/PrimFlags';
|
||||
import {Parcel} from '../public/Parcel';
|
||||
import {ParcelPropertiesRequestMessage} from '../messages/ParcelPropertiesRequest';
|
||||
import {RequestTaskInventoryMessage} from '../messages/RequestTaskInventory';
|
||||
import {ReplyTaskInventoryMessage} from '../messages/ReplyTaskInventory';
|
||||
import {InventoryItem} from '../InventoryItem';
|
||||
import {AssetTypeLL} from '../../enums/AssetTypeLL';
|
||||
import {SaleTypeLL} from '../../enums/SaleTypeLL';
|
||||
import {InventoryTypeLL} from '../../enums/InventoryTypeLL';
|
||||
import Timer = NodeJS.Timer;
|
||||
import {NewObjectEvent} from '../../events/NewObjectEvent';
|
||||
|
||||
export class RegionCommands extends CommandsBase
|
||||
{
|
||||
@@ -94,6 +105,37 @@ export class RegionCommands extends CommandsBase
|
||||
return this.currentRegion.objects.getNumberOfObjects();
|
||||
}
|
||||
|
||||
getTerrainTextures(): UUID[]
|
||||
{
|
||||
const textures: UUID[] = [];
|
||||
textures.push(this.currentRegion.terrainDetail0);
|
||||
textures.push(this.currentRegion.terrainDetail1);
|
||||
textures.push(this.currentRegion.terrainDetail2);
|
||||
textures.push(this.currentRegion.terrainDetail3);
|
||||
return textures;
|
||||
}
|
||||
|
||||
exportSettings(): string
|
||||
{
|
||||
return this.currentRegion.exportXML();
|
||||
}
|
||||
|
||||
async getTerrain()
|
||||
{
|
||||
await this.currentRegion.waitForTerrain();
|
||||
const buf = Buffer.allocUnsafe(262144);
|
||||
let pos = 0;
|
||||
for (let x = 0; x < 256; x++)
|
||||
{
|
||||
for (let y = 0; y < 256; y++)
|
||||
{
|
||||
buf.writeFloatLE(this.currentRegion.terrain[x][y], pos);
|
||||
pos = pos + 4;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
async selectObjects(objects: GameObject[])
|
||||
{
|
||||
// Limit to 255 objects at once
|
||||
@@ -139,64 +181,131 @@ export class RegionCommands extends CommandsBase
|
||||
let resolved = 0;
|
||||
|
||||
this.circuit.sendMessage(selectObject, PacketFlags.Reliable);
|
||||
return await this.circuit.waitForMessage<ObjectPropertiesMessage>(Message.ObjectProperties, 10000, (propertiesMessage: ObjectPropertiesMessage): FilterResponse =>
|
||||
const unresolved = [];
|
||||
try
|
||||
{
|
||||
let found = false;
|
||||
for (const objData of propertiesMessage.ObjectData)
|
||||
const results = await this.circuit.waitForMessage<ObjectPropertiesMessage>(Message.ObjectProperties, 10000, (propertiesMessage: ObjectPropertiesMessage): FilterResponse =>
|
||||
{
|
||||
const objDataUUID = objData.ObjectID.toString();
|
||||
if (uuidMap[objDataUUID] !== undefined)
|
||||
let found = false;
|
||||
for (const objData of propertiesMessage.ObjectData)
|
||||
{
|
||||
resolved++;
|
||||
const obj = uuidMap[objDataUUID];
|
||||
obj.creatorID = objData.CreatorID;
|
||||
obj.creationDate = objData.CreationDate;
|
||||
obj.baseMask = objData.BaseMask;
|
||||
obj.ownerMask = objData.OwnerMask;
|
||||
obj.groupMask = objData.GroupMask;
|
||||
obj.everyoneMask = objData.EveryoneMask;
|
||||
obj.nextOwnerMask = objData.NextOwnerMask;
|
||||
obj.ownershipCost = objData.OwnershipCost;
|
||||
obj.saleType = objData.SaleType;
|
||||
obj.salePrice = objData.SalePrice;
|
||||
obj.aggregatePerms = objData.AggregatePerms;
|
||||
obj.aggregatePermTextures = objData.AggregatePermTextures;
|
||||
obj.aggregatePermTexturesOwner = objData.AggregatePermTexturesOwner;
|
||||
obj.category = objData.Category;
|
||||
obj.inventorySerial = objData.InventorySerial;
|
||||
obj.itemID = objData.ItemID;
|
||||
obj.folderID = objData.FolderID;
|
||||
obj.fromTaskID = objData.FromTaskID;
|
||||
obj.lastOwnerID = objData.LastOwnerID;
|
||||
obj.name = Utils.BufferToStringSimple(objData.Name);
|
||||
obj.description = Utils.BufferToStringSimple(objData.Description);
|
||||
obj.touchName = Utils.BufferToStringSimple(objData.TouchName);
|
||||
obj.sitName = Utils.BufferToStringSimple(objData.SitName);
|
||||
obj.textureID = Utils.BufferToStringSimple(objData.TextureID);
|
||||
obj.resolvedAt = new Date().getTime() / 1000;
|
||||
delete uuidMap[objDataUUID];
|
||||
found = true;
|
||||
const objDataUUID = objData.ObjectID.toString();
|
||||
if (uuidMap[objDataUUID] !== undefined)
|
||||
{
|
||||
resolved++;
|
||||
const obj = uuidMap[objDataUUID];
|
||||
obj.creatorID = objData.CreatorID;
|
||||
obj.creationDate = objData.CreationDate;
|
||||
obj.baseMask = objData.BaseMask;
|
||||
obj.ownerMask = objData.OwnerMask;
|
||||
obj.groupMask = objData.GroupMask;
|
||||
obj.everyoneMask = objData.EveryoneMask;
|
||||
obj.nextOwnerMask = objData.NextOwnerMask;
|
||||
obj.ownershipCost = objData.OwnershipCost;
|
||||
obj.saleType = objData.SaleType;
|
||||
obj.salePrice = objData.SalePrice;
|
||||
obj.aggregatePerms = objData.AggregatePerms;
|
||||
obj.aggregatePermTextures = objData.AggregatePermTextures;
|
||||
obj.aggregatePermTexturesOwner = objData.AggregatePermTexturesOwner;
|
||||
obj.category = objData.Category;
|
||||
obj.inventorySerial = objData.InventorySerial;
|
||||
obj.itemID = objData.ItemID;
|
||||
obj.folderID = objData.FolderID;
|
||||
obj.fromTaskID = objData.FromTaskID;
|
||||
obj.groupID = objData.GroupID;
|
||||
obj.lastOwnerID = objData.LastOwnerID;
|
||||
obj.name = Utils.BufferToStringSimple(objData.Name);
|
||||
obj.description = Utils.BufferToStringSimple(objData.Description);
|
||||
obj.touchName = Utils.BufferToStringSimple(objData.TouchName);
|
||||
obj.sitName = Utils.BufferToStringSimple(objData.SitName);
|
||||
obj.textureID = Utils.BufferToStringSimple(objData.TextureID);
|
||||
obj.resolvedAt = new Date().getTime() / 1000;
|
||||
delete uuidMap[objDataUUID];
|
||||
found = true;
|
||||
|
||||
// console.log(obj.name + ' (' + resolved + ' of ' + objects.length + ')');
|
||||
// console.log(obj.name + ' (' + resolved + ' of ' + objects.length + ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(uuidMap).length === 0)
|
||||
if (Object.keys(uuidMap).length === 0)
|
||||
{
|
||||
return FilterResponse.Finish;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
return FilterResponse.NoMatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FilterResponse.Match;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
for (const obj of objects)
|
||||
{
|
||||
return FilterResponse.Finish;
|
||||
if (obj.resolvedAt === undefined || obj.name === undefined)
|
||||
{
|
||||
obj.resolveAttempts++;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
return FilterResponse.NoMatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FilterResponse.Match;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveObjects(objects: GameObject[])
|
||||
private parseLine(line: string): {
|
||||
'key': string | null,
|
||||
'value': string
|
||||
}
|
||||
{
|
||||
line = line.trim().replace(/[\t]/gu, ' ').trim();
|
||||
while (line.indexOf('\u0020\u0020') > 0)
|
||||
{
|
||||
line = line.replace(/\u0020\u0020/gu, '\u0020');
|
||||
}
|
||||
let key: string | null = null;
|
||||
let value = '';
|
||||
if (line.length > 2)
|
||||
{
|
||||
const sep = line.indexOf(' ');
|
||||
if (sep > 0)
|
||||
{
|
||||
key = line.substr(0, sep);
|
||||
value = line.substr(sep + 1);
|
||||
}
|
||||
}
|
||||
else if (line.length === 1)
|
||||
{
|
||||
key = line;
|
||||
}
|
||||
else if (line.length > 0)
|
||||
{
|
||||
return {
|
||||
'key': line,
|
||||
'value': ''
|
||||
}
|
||||
}
|
||||
if (key !== null)
|
||||
{
|
||||
key = key.trim();
|
||||
}
|
||||
return {
|
||||
'key': key,
|
||||
'value': value
|
||||
}
|
||||
}
|
||||
|
||||
getName(): string
|
||||
{
|
||||
return this.currentRegion.regionName;
|
||||
}
|
||||
|
||||
private async resolveObjects(objects: GameObject[], onlyUnresolved: boolean = false)
|
||||
{
|
||||
// First, create a map of all object IDs
|
||||
const objs: {[key: number]: GameObject} = {};
|
||||
@@ -235,11 +344,14 @@ export class RegionCommands extends CommandsBase
|
||||
{
|
||||
o.resolvedAt = 0;
|
||||
}
|
||||
if (o.resolvedAt !== undefined && o.resolvedAt < resolveTime && o.PCode !== PCode.Avatar)
|
||||
if (o.resolvedAt !== undefined && o.resolvedAt < resolveTime && o.PCode !== PCode.Avatar && o.resolveAttempts < 3 && (o.Flags === undefined || !(o.Flags & PrimFlags.TemporaryOnRez)))
|
||||
{
|
||||
objs[ky].name = undefined;
|
||||
totalRemaining++;
|
||||
objectList.push(objs[ky]);
|
||||
if (!onlyUnresolved || objs[ky].name === undefined)
|
||||
{
|
||||
objs[ky].name = undefined;
|
||||
objectList.push(objs[ky]);
|
||||
}
|
||||
if (objectList.length > 254)
|
||||
{
|
||||
try
|
||||
@@ -278,10 +390,304 @@ export class RegionCommands extends CommandsBase
|
||||
}
|
||||
}
|
||||
}
|
||||
const objectSet = Object.keys(objs);
|
||||
let count = 0;
|
||||
for (const k of objectSet)
|
||||
{
|
||||
count++;
|
||||
const ky = parseInt(k, 10);
|
||||
if (objs[ky] !== undefined)
|
||||
{
|
||||
const o = objs[ky];
|
||||
if (o.FullID !== undefined && o.name !== undefined && o.Flags !== undefined && !(o.Flags & PrimFlags.InventoryEmpty) && (!o.inventory || o.inventory.length === 0))
|
||||
{
|
||||
console.log(' ... Downloading task inventory for object ' + o.FullID.toString() + ' (' + o.name + '), done ' + count + ' of ' + objectSet.length);
|
||||
const req = new RequestTaskInventoryMessage();
|
||||
req.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
req.InventoryData = {
|
||||
LocalID: o.ID
|
||||
};
|
||||
this.circuit.sendMessage(req, PacketFlags.Reliable);
|
||||
try
|
||||
{
|
||||
const inventory = await this.circuit.waitForMessage<ReplyTaskInventoryMessage>(Message.ReplyTaskInventory, 10000, (message: ReplyTaskInventoryMessage): FilterResponse =>
|
||||
{
|
||||
if (message.InventoryData.TaskID.equals(o.FullID))
|
||||
{
|
||||
return FilterResponse.Finish;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FilterResponse.Match;
|
||||
}
|
||||
});
|
||||
const fileName = Utils.BufferToStringSimple(inventory.InventoryData.Filename);
|
||||
|
||||
const file = await this.circuit.XferFile(fileName, true, false, UUID.zero(), AssetType.Unknown, true);
|
||||
if (file.length === 0)
|
||||
{
|
||||
o.Flags = o.Flags | PrimFlags.InventoryEmpty;
|
||||
}
|
||||
else
|
||||
{
|
||||
let str = file.toString('utf-8');
|
||||
let nl = str.indexOf('\0');
|
||||
while (nl !== -1)
|
||||
{
|
||||
str = str.substr(nl + 1);
|
||||
nl = str.indexOf('\0')
|
||||
}
|
||||
const lines: string[] = str.replace(/\r\n/g, '\n').split('\n');
|
||||
let lineNum = 0;
|
||||
while (lineNum < lines.length)
|
||||
{
|
||||
let line = lines[lineNum++];
|
||||
let result = this.parseLine(line);
|
||||
if (result.key !== null)
|
||||
{
|
||||
switch (result.key)
|
||||
{
|
||||
case 'inv_object':
|
||||
let itemID = UUID.zero();
|
||||
let parentID = UUID.zero();
|
||||
let name = '';
|
||||
let assetType: AssetType = AssetType.Unknown;
|
||||
|
||||
while (lineNum < lines.length)
|
||||
{
|
||||
result = this.parseLine(lines[lineNum++]);
|
||||
if (result.key !== null)
|
||||
{
|
||||
if (result.key === '{')
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (result.key === '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (result.key === 'obj_id')
|
||||
{
|
||||
itemID = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'parent_id')
|
||||
{
|
||||
parentID = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'type')
|
||||
{
|
||||
const typeString = result.value as any;
|
||||
assetType = parseInt(AssetTypeLL[typeString], 10);
|
||||
}
|
||||
else if (result.key === 'name')
|
||||
{
|
||||
name = result.value.substr(0, result.value.indexOf('|'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name !== 'Contents')
|
||||
{
|
||||
console.log('TODO: Do something useful with inv_objects')
|
||||
}
|
||||
|
||||
break;
|
||||
case 'inv_item':
|
||||
const item: InventoryItem = new InventoryItem();
|
||||
while (lineNum < lines.length)
|
||||
{
|
||||
line = lines[lineNum++];
|
||||
result = this.parseLine(line);
|
||||
if (result.key !== null)
|
||||
{
|
||||
if (result.key === '{')
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (result.key === '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (result.key === 'item_id')
|
||||
{
|
||||
item.itemID = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'parent_id')
|
||||
{
|
||||
item.parentID = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'permissions')
|
||||
{
|
||||
while (lineNum < lines.length)
|
||||
{
|
||||
result = this.parseLine(lines[lineNum++]);
|
||||
if (result.key !== null)
|
||||
{
|
||||
if (result.key === '{')
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (result.key === '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (result.key === 'creator_mask')
|
||||
{
|
||||
item.permissions.baseMask = parseInt(result.value, 16);
|
||||
}
|
||||
else if (result.key === 'base_mask')
|
||||
{
|
||||
item.permissions.baseMask = parseInt(result.value, 16);
|
||||
}
|
||||
else if (result.key === 'owner_mask')
|
||||
{
|
||||
item.permissions.ownerMask = parseInt(result.value, 16);
|
||||
}
|
||||
else if (result.key === 'group_mask')
|
||||
{
|
||||
item.permissions.groupMask = parseInt(result.value, 16);
|
||||
}
|
||||
else if (result.key === 'everyone_mask')
|
||||
{
|
||||
item.permissions.everyoneMask = parseInt(result.value, 16);
|
||||
}
|
||||
else if (result.key === 'next_owner_mask')
|
||||
{
|
||||
item.permissions.nextOwnerMask = parseInt(result.value, 16);
|
||||
}
|
||||
else if (result.key === 'creator_id')
|
||||
{
|
||||
item.permissions.creator = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'owner_id')
|
||||
{
|
||||
item.permissions.owner = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'last_owner_id')
|
||||
{
|
||||
item.permissions.lastOwner = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'group_id')
|
||||
{
|
||||
item.permissions.group = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'group_owned')
|
||||
{
|
||||
const val = parseInt(result.value, 10);
|
||||
item.permissions.groupOwned = (val !== 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Unrecognised key (4): ' + result.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (result.key === 'sale_info')
|
||||
{
|
||||
while (lineNum < lines.length)
|
||||
{
|
||||
result = this.parseLine(lines[lineNum++]);
|
||||
if (result.key !== null)
|
||||
{
|
||||
if (result.key === '{')
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (result.key === '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (result.key === 'sale_type')
|
||||
{
|
||||
const typeString = result.value as any;
|
||||
item.saleType = parseInt(SaleTypeLL[typeString], 10);
|
||||
}
|
||||
else if (result.key === 'sale_price')
|
||||
{
|
||||
item.salePrice = parseInt(result.value, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Unrecognised key (3): ' + result.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (result.key === 'shadow_id')
|
||||
{
|
||||
item.assetID = new UUID(result.value).bitwiseOr(new UUID('3c115e51-04f4-523c-9fa6-98aff1034730'));
|
||||
}
|
||||
else if (result.key === 'asset_id')
|
||||
{
|
||||
item.assetID = new UUID(result.value);
|
||||
}
|
||||
else if (result.key === 'type')
|
||||
{
|
||||
const typeString = result.value as any;
|
||||
item.type = parseInt(AssetTypeLL[typeString], 10);
|
||||
}
|
||||
else if (result.key === 'inv_type')
|
||||
{
|
||||
const typeString = result.value as any;
|
||||
item.inventoryType = parseInt(InventoryTypeLL[typeString], 10);
|
||||
}
|
||||
else if (result.key === 'flags')
|
||||
{
|
||||
item.flags = parseInt(result.value, 10);
|
||||
}
|
||||
else if (result.key === 'name')
|
||||
{
|
||||
item.name = result.value.substr(0, result.value.indexOf('|'));
|
||||
}
|
||||
else if (result.key === 'desc')
|
||||
{
|
||||
item.description = result.value.substr(0, result.value.indexOf('|'));
|
||||
}
|
||||
else if (result.key === 'creation_date')
|
||||
{
|
||||
item.created = new Date(parseInt(result.value, 10) * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Unrecognised key (2): ' + result.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
o.inventory.push(item);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
console.log('Unrecognised task inventory token: [' + result.key + ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
if (o.FullID !== undefined)
|
||||
{
|
||||
console.error('Error downloading task inventory of ' + o.FullID.toString() + ':');
|
||||
console.error(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error('Error downloading task inventory of ' + o.ID + ':');
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ignore)
|
||||
{
|
||||
|
||||
console.error(ignore);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -310,17 +716,29 @@ export class RegionCommands extends CommandsBase
|
||||
for (const key of uuids)
|
||||
{
|
||||
const costs = result[key];
|
||||
const obj: GameObject = that.currentRegion.objects.getObjectByUUID(new UUID(key));
|
||||
obj.linkPhysicsImpact = parseFloat(costs['linked_set_physics_cost']);
|
||||
obj.linkResourceImpact = parseFloat(costs['linked_set_resource_cost']);
|
||||
obj.physicaImpact = parseFloat(costs['physics_cost']);
|
||||
obj.resourceImpact = parseFloat(costs['resource_cost']);
|
||||
|
||||
obj.landImpact = Math.ceil(obj.linkPhysicsImpact);
|
||||
if (obj.linkResourceImpact > obj.linkPhysicsImpact)
|
||||
try
|
||||
{
|
||||
obj.landImpact = Math.ceil(obj.linkResourceImpact);
|
||||
const obj: GameObject = that.currentRegion.objects.getObjectByUUID(new UUID(key));
|
||||
obj.linkPhysicsImpact = parseFloat(costs['linked_set_physics_cost']);
|
||||
obj.linkResourceImpact = parseFloat(costs['linked_set_resource_cost']);
|
||||
obj.physicaImpact = parseFloat(costs['physics_cost']);
|
||||
obj.resourceImpact = parseFloat(costs['resource_cost']);
|
||||
obj.limitingType = costs['resource_limiting_type'];
|
||||
|
||||
|
||||
obj.landImpact = Math.round(obj.linkPhysicsImpact);
|
||||
if (obj.linkResourceImpact > obj.linkPhysicsImpact)
|
||||
{
|
||||
obj.landImpact = Math.round(obj.linkResourceImpact);
|
||||
}
|
||||
obj.calculatedLandImpact = obj.landImpact;
|
||||
if (obj.Flags !== undefined && obj.Flags & PrimFlags.TemporaryOnRez && obj.limitingType === 'legacy')
|
||||
{
|
||||
obj.calculatedLandImpact = 0;
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -328,7 +746,10 @@ export class RegionCommands extends CommandsBase
|
||||
const promises: Promise<void>[] = [];
|
||||
for (const obj of objects)
|
||||
{
|
||||
ids.push(new LLSD.UUID(obj.FullID));
|
||||
if (!onlyUnresolved || obj.landImpact === undefined)
|
||||
{
|
||||
ids.push(new LLSD.UUID(obj.FullID));
|
||||
}
|
||||
if (ids.length > 255)
|
||||
{
|
||||
promises.push(getCosts(ids));
|
||||
@@ -343,6 +764,104 @@ export class RegionCommands extends CommandsBase
|
||||
}
|
||||
}
|
||||
|
||||
private waitForObjectByLocalID(localID: number, timeout: number): Promise<GameObject>
|
||||
{
|
||||
return new Promise<GameObject>((resolve, reject) =>
|
||||
{
|
||||
let tmr: Timer | null = null;
|
||||
const subscription = this.currentRegion.clientEvents.onNewObjectEvent.subscribe(async (event: NewObjectEvent) =>
|
||||
{
|
||||
if (event.localID === localID)
|
||||
{
|
||||
if (tmr !== null)
|
||||
{
|
||||
clearTimeout(tmr);
|
||||
}
|
||||
subscription.unsubscribe();
|
||||
resolve(event.object);
|
||||
}
|
||||
});
|
||||
tmr = setTimeout(() => {
|
||||
subscription.unsubscribe();
|
||||
reject(new Error('Timeout'));
|
||||
}, timeout)
|
||||
});
|
||||
}
|
||||
|
||||
private waitForObjectByUUID(uuid: UUID, timeout: number): Promise<GameObject>
|
||||
{
|
||||
return new Promise<GameObject>((resolve, reject) =>
|
||||
{
|
||||
let tmr: Timer | null = null;
|
||||
const subscription = this.currentRegion.clientEvents.onNewObjectEvent.subscribe(async (event: NewObjectEvent) =>
|
||||
{
|
||||
if (event.objectID.equals(uuid))
|
||||
{
|
||||
if (tmr !== null)
|
||||
{
|
||||
clearTimeout(tmr);
|
||||
}
|
||||
subscription.unsubscribe();
|
||||
resolve(event.object);
|
||||
}
|
||||
});
|
||||
tmr = setTimeout(() => {
|
||||
subscription.unsubscribe();
|
||||
reject(new Error('Timeout'));
|
||||
}, timeout)
|
||||
});
|
||||
}
|
||||
|
||||
async getObjectByLocalID(id: number, resolve: boolean, waitFor: number = 0)
|
||||
{
|
||||
let obj = null;
|
||||
try
|
||||
{
|
||||
obj = this.currentRegion.objects.getObjectByLocalID(id);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
if (waitFor > 0)
|
||||
{
|
||||
obj = await this.waitForObjectByLocalID(id, waitFor);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw(error);
|
||||
}
|
||||
}
|
||||
if (resolve)
|
||||
{
|
||||
await this.resolveObjects([obj]);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
async getObjectByUUID(id: UUID, resolve: boolean, waitFor: number = 0)
|
||||
{
|
||||
let obj = null;
|
||||
try
|
||||
{
|
||||
obj = this.currentRegion.objects.getObjectByUUID(id);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
if (waitFor > 0)
|
||||
{
|
||||
obj = await this.waitForObjectByUUID(id, waitFor);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw(error);
|
||||
}
|
||||
}
|
||||
if (resolve)
|
||||
{
|
||||
await this.resolveObjects([obj]);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
async findObjectsByName(pattern: string | RegExp, minX?: number, maxX?: number, minY?: number, maxY?: number, minZ?: number, maxZ?: number): Promise<GameObject[]>
|
||||
{
|
||||
let objects: GameObject[] = [];
|
||||
@@ -398,9 +917,61 @@ export class RegionCommands extends CommandsBase
|
||||
return matches;
|
||||
}
|
||||
|
||||
async getAllObjects(resolve: boolean = false): Promise<GameObject[]>
|
||||
async getParcels(): Promise<Parcel[]>
|
||||
{
|
||||
const objs = this.currentRegion.objects.getAllObjects();
|
||||
this.currentRegion.resetParcels();
|
||||
for (let y = 0; y < 64; y++)
|
||||
{
|
||||
for (let x = 0; x < 64; x++)
|
||||
{
|
||||
if (this.currentRegion.parcelMap[y][x] === 0)
|
||||
{
|
||||
const request = new ParcelPropertiesRequestMessage();
|
||||
request.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
request.ParcelData = {
|
||||
North: (y + 1) * 4.0,
|
||||
East: (x + 1) * 4.0,
|
||||
South: y * 4.0,
|
||||
West: x * 4.0,
|
||||
SequenceID: 2147483647,
|
||||
SnapSelection: false
|
||||
};
|
||||
const seqNo = this.circuit.sendMessage(request, PacketFlags.Reliable);
|
||||
await this.circuit.waitForAck(seqNo, 10000);
|
||||
// Wait a second until we request the next one
|
||||
await function()
|
||||
{
|
||||
return new Promise<void>((resolve, reject) =>
|
||||
{
|
||||
setTimeout(() =>
|
||||
{
|
||||
resolve();
|
||||
}, 1000);
|
||||
})
|
||||
}();
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.currentRegion.waitForParcels();
|
||||
return this.currentRegion.getParcels();
|
||||
}
|
||||
|
||||
async getAllObjects(resolve: boolean = false, onlyUnresolved: boolean = false): Promise<GameObject[]>
|
||||
{
|
||||
const objs = await this.currentRegion.objects.getAllObjects();
|
||||
if (resolve)
|
||||
{
|
||||
await this.resolveObjects(objs, onlyUnresolved);
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
async getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number, resolve: boolean = false): Promise<GameObject[]>
|
||||
{
|
||||
const objs = await this.currentRegion.objects.getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ);
|
||||
if (resolve)
|
||||
{
|
||||
await this.resolveObjects(objs);
|
||||
@@ -408,14 +979,70 @@ export class RegionCommands extends CommandsBase
|
||||
return objs;
|
||||
}
|
||||
|
||||
async getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number, resolve: boolean = false): Promise<GameObject[]>
|
||||
async pruneObjects(checkList: GameObject[]): Promise<GameObject[]>
|
||||
{
|
||||
const objs = this.currentRegion.objects.getObjectsInArea(minX, maxX, minY, maxY, minZ, maxZ);
|
||||
if (resolve)
|
||||
let uuids = [];
|
||||
let objects = [];
|
||||
const stillAlive: {[key: string]: GameObject} = {};
|
||||
const checkObjects = async (uuidList: any[], objectList: GameObject[]) =>
|
||||
{
|
||||
await this.resolveObjects(objs);
|
||||
|
||||
const objRef: {[key: string]: GameObject} = {};
|
||||
for (const obj of objectList)
|
||||
{
|
||||
objRef[obj.FullID.toString()] = obj;
|
||||
}
|
||||
const result = await this.currentRegion.caps.capsRequestXML('GetObjectCost', {
|
||||
'object_ids': uuidList
|
||||
});
|
||||
for (const u of Object.keys(result))
|
||||
{
|
||||
stillAlive[u] = objRef[u];
|
||||
}
|
||||
};
|
||||
|
||||
for (const o of checkList)
|
||||
{
|
||||
if (o.FullID)
|
||||
{
|
||||
uuids.push(new LLSD.UUID(o.FullID));
|
||||
objects.push(o);
|
||||
if (uuids.length > 256)
|
||||
{
|
||||
await checkObjects(uuids, objects);
|
||||
uuids = [];
|
||||
objects = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
return objs;
|
||||
if (uuids.length > 0)
|
||||
{
|
||||
await checkObjects(uuids, objects);
|
||||
}
|
||||
console.log('Found ' + Object.keys(stillAlive).length + ' objects still present out of ' + checkList.length + ' objects');
|
||||
const deadObjects: GameObject[] = [];
|
||||
for (const o of checkList)
|
||||
{
|
||||
let found = false;
|
||||
if (o.FullID)
|
||||
{
|
||||
const uuid = o.FullID.toString();
|
||||
if (stillAlive[uuid])
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
deadObjects.push(o);
|
||||
}
|
||||
}
|
||||
return deadObjects;
|
||||
}
|
||||
|
||||
setPersist(persist: boolean)
|
||||
{
|
||||
this.currentRegion.objects.setPersist(persist);
|
||||
}
|
||||
|
||||
async grabObject(localID: number | UUID,
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import {Vector3} from './Vector3';
|
||||
import {UUID} from './UUID';
|
||||
import {Quaternion} from './Quaternion';
|
||||
import {Tree} from '../enums/Tree';
|
||||
import {SoundFlags} from '..';
|
||||
import {Vector4} from './Vector4';
|
||||
import {TextureEntry} from './TextureEntry';
|
||||
import {Color4} from './Color4';
|
||||
import {ParticleSystem} from './ParticleSystem';
|
||||
import {ITreeBoundingBox} from './interfaces/ITreeBoundingBox';
|
||||
import {NameValue} from './NameValue';
|
||||
import {PCode} from '../enums/PCode';
|
||||
import {Utils} from './Utils';
|
||||
import {UUID} from '../UUID';
|
||||
import * as Long from 'long';
|
||||
import {NameValue} from '../NameValue';
|
||||
import {Vector3} from '../Vector3';
|
||||
import {TextureEntry} from '../TextureEntry';
|
||||
import {Color4} from '../Color4';
|
||||
import {Quaternion} from '../Quaternion';
|
||||
import {Vector4} from '../Vector4';
|
||||
import {Tree} from '../../enums/Tree';
|
||||
import {PCode, SoundFlags} from '../..';
|
||||
import {ParticleSystem} from '../ParticleSystem';
|
||||
import {GameObject} from '../public/GameObject';
|
||||
import {FlexibleData} from '../public/FlexibleData';
|
||||
import {LightData} from '../public/LightData';
|
||||
import {LightImageData} from '../public/LightImageData';
|
||||
import {SculptData} from '../public/SculptData';
|
||||
import {MeshData} from '../public/MeshData';
|
||||
|
||||
export class GameObject
|
||||
export interface IGameObjectData
|
||||
{
|
||||
deleted: boolean;
|
||||
creatorID?: UUID;
|
||||
creationDate?: Long;
|
||||
baseMask?: number;
|
||||
@@ -43,6 +47,7 @@ export class GameObject
|
||||
totalChildren?: number;
|
||||
|
||||
landImpact?: number;
|
||||
calculatedLandImpact?: number;
|
||||
physicaImpact?: number;
|
||||
resourceImpact?: number;
|
||||
linkResourceImpact?: number;
|
||||
@@ -50,22 +55,18 @@ export class GameObject
|
||||
limitingType?: string;
|
||||
|
||||
children?: GameObject[];
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
ID = 0;
|
||||
FullID = UUID.random();
|
||||
ParentID = 0;
|
||||
OwnerID = UUID.zero();
|
||||
IsAttachment = false;
|
||||
NameValue: {[key: string]: NameValue} = {};
|
||||
PCode: PCode = PCode.None;
|
||||
|
||||
ID: number;
|
||||
FullID: UUID;
|
||||
ParentID?: number;
|
||||
OwnerID: UUID;
|
||||
IsAttachment: boolean;
|
||||
NameValue: {[key: string]: NameValue};
|
||||
PCode: PCode;
|
||||
State?: number;
|
||||
CRC?: number;
|
||||
Material?: number;
|
||||
ClickAction?: number;
|
||||
Scale?: Vector3;
|
||||
ObjectData?: Buffer;
|
||||
UpdateFlags?: number;
|
||||
Flags?: number;
|
||||
PathCurve?: number;
|
||||
ProfileCurve?: number;
|
||||
@@ -86,12 +87,9 @@ export class GameObject
|
||||
ProfileEnd?: number;
|
||||
ProfileHollow?: number;
|
||||
TextureEntry?: TextureEntry;
|
||||
TextureAnim?: Buffer;
|
||||
Data?: Buffer;
|
||||
Text?: string;
|
||||
TextColor?: Color4;
|
||||
MediaURL?: string;
|
||||
PSBlock?: Buffer;
|
||||
JointType?: number;
|
||||
JointPivot?: Vector3;
|
||||
JointAxisOrAnchor?: Vector3;
|
||||
@@ -107,30 +105,14 @@ export class GameObject
|
||||
SoundFlags?: SoundFlags;
|
||||
SoundRadius?: number;
|
||||
Particles?: ParticleSystem;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.Position = Vector3.getZero();
|
||||
this.Rotation = Quaternion.getIdentity();
|
||||
this.AngularVelocity = Vector3.getZero();
|
||||
this.TreeSpecies = 0;
|
||||
this.SoundFlags = 0;
|
||||
this.SoundRadius = 1.0;
|
||||
this.SoundGain = 1.0;
|
||||
this.ParentID = 0;
|
||||
}
|
||||
|
||||
hasNameValueEntry(key: string): boolean
|
||||
{
|
||||
return this.NameValue[key] !== undefined;
|
||||
}
|
||||
|
||||
getNameValueEntry(key: string): string
|
||||
{
|
||||
if (this.NameValue[key])
|
||||
{
|
||||
return this.NameValue[key].value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
FlexibleData?: FlexibleData;
|
||||
LightData?: LightData;
|
||||
LightImageData?: LightImageData;
|
||||
SculptData?: SculptData;
|
||||
MeshData?: MeshData;
|
||||
density?: number;
|
||||
friction?: number;
|
||||
gravityMultiplier?: number;
|
||||
physicsShapeType?: number;
|
||||
restitution?: number;
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import {RBush3D} from 'rbush-3d/dist';
|
||||
import {UUID} from '../UUID';
|
||||
import {GameObject} from '../GameObject';
|
||||
import {GameObject} from '../public/GameObject';
|
||||
|
||||
export interface IObjectStore
|
||||
{
|
||||
rtree?: RBush3D;
|
||||
getObjectsByParent(parentID: number): GameObject[];
|
||||
shutdown(): void;
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): GameObject[];
|
||||
getObjectsInArea(minX: number, maxX: number, minY: number, maxY: number, minZ: number, maxZ: number): Promise<GameObject[]>;
|
||||
getObjectByUUID(fullID: UUID): GameObject;
|
||||
getObjectByLocalID(ID: number): GameObject;
|
||||
getNumberOfObjects(): number;
|
||||
getAllObjects(): GameObject[];
|
||||
getAllObjects(): Promise<GameObject[]>;
|
||||
setPersist(persist: boolean): void;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {BBox} from 'rbush-3d/dist';
|
||||
import {GameObject} from '../GameObject';
|
||||
import {GameObject} from '../public/GameObject';
|
||||
|
||||
export interface ITreeBoundingBox extends BBox
|
||||
{
|
||||
|
||||
24
lib/classes/public/FlexibleData.ts
Normal file
24
lib/classes/public/FlexibleData.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {Vector3} from '../Vector3';
|
||||
|
||||
export class FlexibleData
|
||||
{
|
||||
Softness = 0;
|
||||
Tension = 0.0;
|
||||
Drag = 0.0;
|
||||
Gravity = 0.0;
|
||||
Wind = 0.0;
|
||||
Force = Vector3.getZero();
|
||||
|
||||
constructor(buf: Buffer, pos: number, length: number)
|
||||
{
|
||||
if (length >= 5)
|
||||
{
|
||||
this.Softness = ((buf.readUInt8(pos) & 0x80) >> 6) | ((buf.readUInt8(pos + 1) & 0x80) >> 7);
|
||||
this.Tension = (buf.readUInt8(pos++) & 0x7F) / 10.0;
|
||||
this.Drag = (buf.readUInt8(pos++) & 0x7F) / 10.0;
|
||||
this.Gravity = (buf.readUInt8(pos++) / 10.0) - 10.0;
|
||||
this.Wind = (buf.readUInt8(pos++) / 10.0);
|
||||
this.Force = new Vector3(buf, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
514
lib/classes/public/GameObject.ts
Normal file
514
lib/classes/public/GameObject.ts
Normal file
@@ -0,0 +1,514 @@
|
||||
import {Vector3} from '../Vector3';
|
||||
import {UUID} from '../UUID';
|
||||
import {Quaternion} from '../Quaternion';
|
||||
import {Tree} from '../../enums/Tree';
|
||||
import {Vector4} from '../Vector4';
|
||||
import {TextureEntry} from '../TextureEntry';
|
||||
import {Color4} from '../Color4';
|
||||
import {ParticleSystem} from '../ParticleSystem';
|
||||
import {ITreeBoundingBox} from '../interfaces/ITreeBoundingBox';
|
||||
import {NameValue} from '../NameValue';
|
||||
import * as Long from 'long';
|
||||
import {IGameObjectData} from '../interfaces/IGameObjectData';
|
||||
import {FlexibleData} from './FlexibleData';
|
||||
import {LightData} from './LightData';
|
||||
import {LightImageData} from './LightImageData';
|
||||
import {SculptData} from './SculptData';
|
||||
import {MeshData} from './MeshData';
|
||||
import {PCode, PrimFlags, SoundFlags} from '../..';
|
||||
import * as builder from 'xmlbuilder';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
import {Region} from '../Region';
|
||||
import {TextureAnimFlags} from '../../enums/TextureAnimFlags';
|
||||
import {ProfileShape} from '../../enums/ProfileShape';
|
||||
import {HoleType} from '../../enums/HoleType';
|
||||
import {PhysicsShapeType} from '../../enums/PhysicsShapeType';
|
||||
import {InventoryItem} from '../InventoryItem';
|
||||
|
||||
export class GameObject implements IGameObjectData
|
||||
{
|
||||
rtreeEntry?: ITreeBoundingBox;
|
||||
TextureAnim?: Buffer;
|
||||
Data?: Buffer;
|
||||
ObjectData?: Buffer;
|
||||
PSBlock?: Buffer;
|
||||
|
||||
deleted = false;
|
||||
creatorID?: UUID;
|
||||
creationDate?: Long;
|
||||
baseMask?: number;
|
||||
ownerMask?: number;
|
||||
groupMask?: number;
|
||||
groupID?: UUID;
|
||||
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?: string;
|
||||
description?: string;
|
||||
touchName?: string;
|
||||
sitName?: string;
|
||||
textureID?: string;
|
||||
resolvedAt?: number;
|
||||
totalChildren?: number;
|
||||
|
||||
landImpact?: number;
|
||||
calculatedLandImpact?: number;
|
||||
physicaImpact?: number;
|
||||
resourceImpact?: number;
|
||||
linkResourceImpact?: number;
|
||||
linkPhysicsImpact?: number;
|
||||
limitingType?: string;
|
||||
|
||||
children?: GameObject[];
|
||||
ID = 0;
|
||||
FullID = UUID.random();
|
||||
ParentID?: number;
|
||||
OwnerID = UUID.zero();
|
||||
IsAttachment = false;
|
||||
NameValue: {[key: string]: NameValue} = {};
|
||||
PCode: PCode = PCode.None;
|
||||
|
||||
State?: number;
|
||||
CRC?: number;
|
||||
Material?: number;
|
||||
ClickAction?: number;
|
||||
Scale?: Vector3;
|
||||
Flags?: PrimFlags;
|
||||
PathCurve?: number;
|
||||
ProfileCurve?: number;
|
||||
PathBegin?: number;
|
||||
PathEnd?: number;
|
||||
PathScaleX?: number;
|
||||
PathScaleY?: number;
|
||||
ExtraParams?: Buffer;
|
||||
PathShearX?: number;
|
||||
PathShearY?: number;
|
||||
PathTwist?: number;
|
||||
PathTwistBegin?: number;
|
||||
PathRadiusOffset?: number;
|
||||
PathTaperX?: number;
|
||||
PathTaperY?: number;
|
||||
PathRevolutions?: number;
|
||||
PathSkew?: number;
|
||||
ProfileBegin?: number;
|
||||
ProfileEnd?: number;
|
||||
ProfileHollow?: number;
|
||||
TextureEntry?: TextureEntry;
|
||||
Text?: string;
|
||||
TextColor?: Color4;
|
||||
MediaURL?: string;
|
||||
JointType?: number;
|
||||
JointPivot?: Vector3;
|
||||
JointAxisOrAnchor?: Vector3;
|
||||
Position?: Vector3;
|
||||
Rotation?: Quaternion;
|
||||
CollisionPlane?: Vector4;
|
||||
Velocity?: Vector3;
|
||||
Acceleration?: Vector3;
|
||||
AngularVelocity?: Vector3;
|
||||
TreeSpecies?: Tree;
|
||||
Sound?: UUID;
|
||||
SoundGain?: number;
|
||||
SoundFlags?: SoundFlags;
|
||||
SoundRadius?: number;
|
||||
Particles?: ParticleSystem;
|
||||
FlexibleData?: FlexibleData;
|
||||
LightData?: LightData;
|
||||
LightImageData?: LightImageData;
|
||||
SculptData?: SculptData;
|
||||
MeshData?: MeshData;
|
||||
TextureAnimFlags?: TextureAnimFlags;
|
||||
TextureAnimFace?: number;
|
||||
TextureAnimSizeX?: number;
|
||||
TextureAnimSizeY?: number;
|
||||
TextureAnimStart?: number;
|
||||
TextureAnimLength?: number;
|
||||
TextureAnimRate?: number;
|
||||
|
||||
density?: number;
|
||||
friction?: number;
|
||||
gravityMultiplier?: number;
|
||||
physicsShapeType?: PhysicsShapeType;
|
||||
restitution?: number;
|
||||
|
||||
region: Region;
|
||||
|
||||
inventory: InventoryItem[] = [];
|
||||
|
||||
resolveAttempts = 0;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.Position = Vector3.getZero();
|
||||
this.Rotation = Quaternion.getIdentity();
|
||||
this.AngularVelocity = Vector3.getZero();
|
||||
this.TreeSpecies = 0;
|
||||
this.SoundFlags = 0;
|
||||
this.SoundRadius = 1.0;
|
||||
this.SoundGain = 1.0;
|
||||
}
|
||||
|
||||
hasNameValueEntry(key: string): boolean
|
||||
{
|
||||
return this.NameValue[key] !== undefined;
|
||||
}
|
||||
|
||||
getNameValueEntry(key: string): string
|
||||
{
|
||||
if (this.NameValue[key])
|
||||
{
|
||||
return this.NameValue[key].value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private getInventoryXML(xml: XMLElementOrXMLNode, inv: InventoryItem)
|
||||
{
|
||||
if (!inv.assetID.equals(UUID.zero()))
|
||||
{
|
||||
const item = xml.ele('TaskInventoryItem');
|
||||
UUID.getXML(item.ele('AssetID'), inv.assetID);
|
||||
UUID.getXML(item.ele('ItemID'), inv.itemID);
|
||||
if (inv.permissions)
|
||||
{
|
||||
item.ele('BasePermissions', inv.permissions.baseMask);
|
||||
item.ele('EveryonePermissions', inv.permissions.everyoneMask);
|
||||
item.ele('GroupPermissions', inv.permissions.groupMask);
|
||||
item.ele('NextPermissions', inv.permissions.nextOwnerMask);
|
||||
item.ele('CurrentPermissions', inv.permissions.ownerMask);
|
||||
item.ele('PermsMask', 0);
|
||||
UUID.getXML(item.ele('CreatorID'), inv.permissions.creator);
|
||||
UUID.getXML(item.ele('LastOwnerID'), inv.permissions.lastOwner);
|
||||
UUID.getXML(item.ele('OwnerID'), inv.permissions.owner);
|
||||
UUID.getXML(item.ele('GroupID'), inv.permissions.group);
|
||||
|
||||
}
|
||||
item.ele('CreationDate', inv.created.getTime() / 1000);
|
||||
item.ele('Description', inv.description);
|
||||
item.ele('Flags', inv.flags);
|
||||
item.ele('InvType', inv.inventoryType);
|
||||
UUID.getXML(item.ele('ParentID'), this.FullID);
|
||||
UUID.getXML(item.ele('ParentPartID'), this.FullID);
|
||||
item.ele('Type', inv.type);
|
||||
item.ele('Name', inv.name);
|
||||
}
|
||||
}
|
||||
|
||||
private getXML(xml: XMLElementOrXMLNode, rootPrim: GameObject, linkNum: number)
|
||||
{
|
||||
const sceneObjectPart = xml.ele('SceneObjectPart').att('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance').att('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
|
||||
sceneObjectPart.ele('AllowedDrop', (this.Flags !== undefined && (this.Flags & PrimFlags.AllowInventoryDrop) !== 0) ? 'true' : 'false');
|
||||
UUID.getXML(sceneObjectPart.ele('CreatorID'), this.creatorID);
|
||||
sceneObjectPart.ele('CreatorData', 'node-metaverse');
|
||||
UUID.getXML(sceneObjectPart.ele('CreatorID'), this.folderID);
|
||||
sceneObjectPart.ele('InventorySerial', this.inventorySerial);
|
||||
UUID.getXML(sceneObjectPart.ele('UUID'), this.FullID);
|
||||
sceneObjectPart.ele('LocalId', this.ID);
|
||||
sceneObjectPart.ele('Name', this.name);
|
||||
sceneObjectPart.ele('Material', this.Material);
|
||||
sceneObjectPart.ele('RegionHandle', this.region.regionHandle.toString());
|
||||
Vector3.getXML(sceneObjectPart.ele('GroupPosition'), rootPrim.Position);
|
||||
if (rootPrim === this)
|
||||
{
|
||||
Vector3.getXML(sceneObjectPart.ele('OffsetPosition'), Vector3.getZero());
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3.getXML(sceneObjectPart.ele('OffsetPosition'), this.Position);
|
||||
}
|
||||
Quaternion.getXML(sceneObjectPart.ele('RotationOffset'), this.Rotation);
|
||||
Vector3.getXML(sceneObjectPart.ele('Velocity'), this.Velocity);
|
||||
Vector3.getXML(sceneObjectPart.ele('AngularVelocity'), this.AngularVelocity);
|
||||
Vector3.getXML(sceneObjectPart.ele('Acceleration'), this.Acceleration);
|
||||
sceneObjectPart.ele('Description', this.description);
|
||||
if (this.Text !== undefined && this.Text !== '')
|
||||
{
|
||||
sceneObjectPart.ele('Text', this.Text);
|
||||
}
|
||||
if (this.TextColor !== undefined)
|
||||
{
|
||||
Color4.getXML(sceneObjectPart.ele('Color'), this.TextColor);
|
||||
}
|
||||
sceneObjectPart.ele('SitName', this.sitName);
|
||||
sceneObjectPart.ele('TouchName', this.touchName);
|
||||
sceneObjectPart.ele('LinkNum', linkNum);
|
||||
sceneObjectPart.ele('ClickAction', this.ClickAction);
|
||||
const shape = sceneObjectPart.ele('Shape');
|
||||
{
|
||||
shape.ele('ProfileCurve', this.ProfileCurve);
|
||||
if (this.TextureEntry)
|
||||
{
|
||||
shape.ele('TextureEntry', this.TextureEntry.binary.toString('base64'));
|
||||
}
|
||||
if (this.ExtraParams)
|
||||
{
|
||||
shape.ele('ExtraParams', this.ExtraParams.toString('base64'));
|
||||
}
|
||||
shape.ele('PathBegin', this.PathBegin);
|
||||
shape.ele('PathCurve', this.PathCurve);
|
||||
shape.ele('PathEnd', this.PathEnd);
|
||||
shape.ele('PathRadiusOffset', this.PathRadiusOffset);
|
||||
shape.ele('PathRevolutions', this.PathRevolutions);
|
||||
shape.ele('PathScaleX', this.PathScaleX);
|
||||
shape.ele('PathScaleY', this.PathScaleY);
|
||||
shape.ele('PathShearX', this.PathShearX);
|
||||
shape.ele('PathSkew', this.PathSkew);
|
||||
shape.ele('PathTaperX', this.PathTaperX);
|
||||
shape.ele('PathTaperY', this.PathTaperY);
|
||||
shape.ele('PathTwist', this.PathTwist);
|
||||
shape.ele('PathTwistBegin', this.PathTwistBegin);
|
||||
shape.ele('PCode', this.PCode);
|
||||
shape.ele('ProfileBegin', this.ProfileBegin);
|
||||
shape.ele('ProfileEnd', this.ProfileEnd);
|
||||
shape.ele('ProfileHollow', this.ProfileHollow);
|
||||
shape.ele('State', this.State);
|
||||
|
||||
if (this.ProfileCurve)
|
||||
{
|
||||
|
||||
const profileShape: ProfileShape = this.ProfileCurve & 0x0F;
|
||||
const holeType: HoleType = this.ProfileCurve & 0xF0;
|
||||
|
||||
shape.ele('ProfileShape', ProfileShape[profileShape]);
|
||||
shape.ele('HollowShape', HoleType[holeType]);
|
||||
}
|
||||
if (this.MeshData !== undefined)
|
||||
{
|
||||
shape.ele('SculptType', this.MeshData.type);
|
||||
UUID.getXML(shape.ele('SculptTexture'), this.MeshData.meshData);
|
||||
shape.ele('SculptEntry', true);
|
||||
}
|
||||
else if (this.SculptData !== undefined)
|
||||
{
|
||||
shape.ele('SculptType', this.SculptData.type);
|
||||
UUID.getXML(shape.ele('SculptTexture'), this.SculptData.texture);
|
||||
shape.ele('SculptEntry', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
shape.ele('SculptEntry', false);
|
||||
}
|
||||
if (this.FlexibleData !== undefined)
|
||||
{
|
||||
shape.ele('FlexiSoftness', this.FlexibleData.Softness);
|
||||
shape.ele('FlexiTension', this.FlexibleData.Tension);
|
||||
shape.ele('FlexiDrag', this.FlexibleData.Drag);
|
||||
shape.ele('FlexiGravity', this.FlexibleData.Gravity);
|
||||
shape.ele('FlexiWind', this.FlexibleData.Wind);
|
||||
shape.ele('FlexiForceX', this.FlexibleData.Force.x);
|
||||
shape.ele('FlexiForceY', this.FlexibleData.Force.y);
|
||||
shape.ele('FlexiForceZ', this.FlexibleData.Force.z);
|
||||
shape.ele('FlexiEntry', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
shape.ele('FlexiEntry', false);
|
||||
}
|
||||
if (this.LightData !== undefined)
|
||||
{
|
||||
shape.ele('LightColorR', this.LightData.Color.red);
|
||||
shape.ele('LightColorG', this.LightData.Color.green);
|
||||
shape.ele('LightColorB', this.LightData.Color.blue);
|
||||
shape.ele('LightColorA', this.LightData.Color.alpha);
|
||||
shape.ele('LightRadius', this.LightData.Radius);
|
||||
shape.ele('LightCutoff', this.LightData.Cutoff);
|
||||
shape.ele('LightFalloff', this.LightData.Falloff);
|
||||
shape.ele('LightIntensity', this.LightData.Intensity);
|
||||
shape.ele('LightEntry', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
shape.ele('LightEntry', false);
|
||||
}
|
||||
|
||||
}
|
||||
Vector3.getXML(sceneObjectPart.ele('Scale'), this.Scale);
|
||||
sceneObjectPart.ele('ParentID', this.ParentID);
|
||||
sceneObjectPart.ele('CreationDate', Math.round((new Date()).getTime() / 1000));
|
||||
sceneObjectPart.ele('Category', this.category);
|
||||
sceneObjectPart.ele('SalePrice', this.salePrice);
|
||||
sceneObjectPart.ele('ObjectSaleType', this.saleType);
|
||||
sceneObjectPart.ele('OwnershipCost', this.ownershipCost);
|
||||
UUID.getXML(sceneObjectPart.ele('GroupID'), this.groupID);
|
||||
UUID.getXML(sceneObjectPart.ele('OwnerID'), this.OwnerID);
|
||||
UUID.getXML(sceneObjectPart.ele('LastOwnerID'), this.lastOwnerID);
|
||||
sceneObjectPart.ele('BaseMask', this.baseMask);
|
||||
sceneObjectPart.ele('OwnerMask', this.ownerMask);
|
||||
sceneObjectPart.ele('GroupMask', this.groupMask);
|
||||
sceneObjectPart.ele('EveryoneMask', this.everyoneMask);
|
||||
sceneObjectPart.ele('NextOwnerMask', this.nextOwnerMask);
|
||||
const flags = [];
|
||||
if (this.Flags !== undefined)
|
||||
{
|
||||
for (const flag of Object.keys(PrimFlags))
|
||||
{
|
||||
if (typeof flag === 'string')
|
||||
{
|
||||
const fl: any = PrimFlags;
|
||||
const flagName: string = flag;
|
||||
const flagValue: number = fl[flagName];
|
||||
if (this.Flags & flagValue)
|
||||
{
|
||||
flags.push(flagName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sceneObjectPart.ele('Flags', flags.join(' '));
|
||||
if (this.TextureAnim)
|
||||
{
|
||||
sceneObjectPart.ele('TextureAnimation', this.TextureAnim.toString('base64'));
|
||||
}
|
||||
if (this.Particles && this.PSBlock)
|
||||
{
|
||||
sceneObjectPart.ele('ParticleSystem', this.PSBlock.toString('base64'));
|
||||
}
|
||||
if (this.physicsShapeType)
|
||||
{
|
||||
sceneObjectPart.ele('PhysicsShapeType', this.physicsShapeType);
|
||||
}
|
||||
if (this.Sound && !this.Sound.equals(UUID.zero()))
|
||||
{
|
||||
UUID.getXML(sceneObjectPart.ele('SoundID'), this.Sound);
|
||||
sceneObjectPart.ele('SoundGain', this.SoundGain);
|
||||
sceneObjectPart.ele('SoundFlags', this.SoundFlags);
|
||||
sceneObjectPart.ele('SoundRadius', this.SoundRadius);
|
||||
sceneObjectPart.ele('SoundQueueing', false);
|
||||
}
|
||||
if (this.inventory && this.inventory.length > 0)
|
||||
{
|
||||
const inventory = sceneObjectPart.ele('TaskInventory');
|
||||
for (const inv of this.inventory)
|
||||
{
|
||||
this.getInventoryXML(inventory, inv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exportXML(): string
|
||||
{
|
||||
const document = builder.create('SceneObjectGroup');
|
||||
let linkNum = 1;
|
||||
this.getXML(document, this, linkNum);
|
||||
if (this.children && this.children.length > 0)
|
||||
{
|
||||
const otherParts = document.ele('OtherParts');
|
||||
for (const child of this.children)
|
||||
{
|
||||
child.getXML(otherParts, this, ++linkNum);
|
||||
}
|
||||
}
|
||||
return document.end({pretty: true, allowEmpty: true});
|
||||
}
|
||||
|
||||
public toJSON(): IGameObjectData
|
||||
{
|
||||
return {
|
||||
deleted: this.deleted,
|
||||
creatorID: this.creatorID,
|
||||
creationDate: this.creationDate,
|
||||
baseMask: this.baseMask,
|
||||
ownerMask: this.ownerMask,
|
||||
groupMask: this.groupMask,
|
||||
everyoneMask: this.everyoneMask,
|
||||
nextOwnerMask: this.nextOwnerMask,
|
||||
ownershipCost: this.ownershipCost,
|
||||
saleType: this.saleType,
|
||||
salePrice: this.salePrice,
|
||||
aggregatePerms: this.aggregatePerms,
|
||||
aggregatePermTextures: this.aggregatePermTextures,
|
||||
aggregatePermTexturesOwner: this.aggregatePermTexturesOwner,
|
||||
category: this.category,
|
||||
inventorySerial: this.inventorySerial,
|
||||
itemID: this.itemID,
|
||||
folderID: this.folderID,
|
||||
fromTaskID: this.fromTaskID,
|
||||
lastOwnerID: this.lastOwnerID,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
touchName: this.touchName,
|
||||
sitName: this.sitName,
|
||||
resolvedAt: this.resolvedAt,
|
||||
totalChildren: this.totalChildren,
|
||||
landImpact: this.landImpact,
|
||||
calculatedLandImpact: this.calculatedLandImpact,
|
||||
physicaImpact: this.physicaImpact,
|
||||
resourceImpact: this.resourceImpact,
|
||||
linkResourceImpact: this.linkResourceImpact,
|
||||
linkPhysicsImpact: this.linkPhysicsImpact,
|
||||
limitingType: this.limitingType,
|
||||
children: this.children,
|
||||
ID: this.ID,
|
||||
FullID: this.FullID,
|
||||
ParentID: this.ParentID,
|
||||
OwnerID: this.OwnerID,
|
||||
IsAttachment: this.IsAttachment,
|
||||
NameValue: this.NameValue,
|
||||
PCode: this.PCode,
|
||||
State: this.State,
|
||||
CRC: this.CRC,
|
||||
Material: this.Material,
|
||||
ClickAction: this.ClickAction,
|
||||
Scale: this.Scale,
|
||||
Flags: this.Flags,
|
||||
PathCurve: this.PathCurve,
|
||||
ProfileCurve: this.ProfileCurve,
|
||||
PathBegin: this.PathBegin,
|
||||
PathEnd: this.PathEnd,
|
||||
PathScaleX: this.PathScaleX,
|
||||
PathScaleY: this.PathScaleY,
|
||||
PathShearX: this.PathShearX,
|
||||
PathShearY: this.PathShearY,
|
||||
PathTwist: this.PathTwist,
|
||||
PathTwistBegin: this.PathTwistBegin,
|
||||
PathRadiusOffset: this.PathRadiusOffset,
|
||||
PathTaperX: this.PathTaperX,
|
||||
PathTaperY: this.PathTaperY,
|
||||
PathRevolutions: this.PathRevolutions,
|
||||
PathSkew: this.PathSkew,
|
||||
ProfileBegin: this.ProfileBegin,
|
||||
ProfileEnd: this.ProfileEnd,
|
||||
ProfileHollow: this.ProfileHollow,
|
||||
TextureEntry: this.TextureEntry,
|
||||
Text: this.Text,
|
||||
TextColor: this.TextColor,
|
||||
MediaURL: this.MediaURL,
|
||||
JointType: this.JointType,
|
||||
JointPivot: this.JointPivot,
|
||||
JointAxisOrAnchor: this.JointAxisOrAnchor,
|
||||
Position: this.Position,
|
||||
Rotation: this.Rotation,
|
||||
CollisionPlane: this.CollisionPlane,
|
||||
Velocity: this.Velocity,
|
||||
Acceleration: this.Acceleration,
|
||||
AngularVelocity: this.AngularVelocity,
|
||||
TreeSpecies: this.TreeSpecies,
|
||||
Sound: this.Sound,
|
||||
SoundGain: this.SoundGain,
|
||||
SoundFlags: this.SoundFlags,
|
||||
SoundRadius: this.SoundRadius,
|
||||
Particles: this.Particles,
|
||||
FlexibleData: this.FlexibleData,
|
||||
LightData: this.LightData,
|
||||
LightImageData: this.LightImageData,
|
||||
SculptData: this.SculptData,
|
||||
MeshData: this.MeshData,
|
||||
density: this.density,
|
||||
friction: this.friction,
|
||||
gravityMultiplier: this.gravityMultiplier,
|
||||
physicsShapeType: this.physicsShapeType,
|
||||
restitution: this.restitution
|
||||
}
|
||||
}
|
||||
}
|
||||
30
lib/classes/public/LightData.ts
Normal file
30
lib/classes/public/LightData.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {Color4} from '../Color4';
|
||||
import {Utils} from '../Utils';
|
||||
|
||||
export class LightData
|
||||
{
|
||||
Color: Color4 = Color4.black;
|
||||
Radius = 0.0;
|
||||
Cutoff = 0.0;
|
||||
Falloff = 0.0;
|
||||
Intensity = 0.0;
|
||||
|
||||
constructor(buf: Buffer, pos: number, length: number)
|
||||
{
|
||||
if (length >= 16)
|
||||
{
|
||||
this.Color = new Color4(buf, pos, false);
|
||||
pos += 4;
|
||||
this.Radius = buf.readFloatLE(pos);
|
||||
pos += 4;
|
||||
this.Cutoff = buf.readFloatLE(pos);
|
||||
pos += 4;
|
||||
this.Falloff = buf.readFloatLE(pos);
|
||||
if (typeof this.Color.alpha === 'number')
|
||||
{
|
||||
this.Intensity = this.Color.alpha;
|
||||
}
|
||||
this.Color.alpha = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
lib/classes/public/LightImageData.ts
Normal file
18
lib/classes/public/LightImageData.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {UUID} from '../UUID';
|
||||
import {Vector3} from '../Vector3';
|
||||
|
||||
export class LightImageData
|
||||
{
|
||||
texture: UUID = UUID.zero();
|
||||
params: Vector3 = Vector3.getZero();
|
||||
|
||||
constructor(buf: Buffer, pos: number, length: number)
|
||||
{
|
||||
if (length >= 28)
|
||||
{
|
||||
this.texture = new UUID(buf, pos);
|
||||
pos += 16;
|
||||
this.params = new Vector3(buf, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
lib/classes/public/Material.ts
Normal file
24
lib/classes/public/Material.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {UUID} from '../UUID';
|
||||
import {Color4} from '../Color4';
|
||||
|
||||
export class Material
|
||||
{
|
||||
alphaMaskCutoff: number;
|
||||
diffuseAlphaMode: number;
|
||||
envIntensity: number;
|
||||
normMap: UUID;
|
||||
normOffsetX: number;
|
||||
normOffsetY: number;
|
||||
normRepeatX: number;
|
||||
normRepeatY: number;
|
||||
normRotation: number;
|
||||
specColor: Color4;
|
||||
specExp: number;
|
||||
specMap: UUID;
|
||||
specOffsetX: number;
|
||||
specOffsetY: number;
|
||||
specRepeatX: number;
|
||||
specRepeatY: number;
|
||||
specRotation: number;
|
||||
llsd: string;
|
||||
}
|
||||
18
lib/classes/public/MeshData.ts
Normal file
18
lib/classes/public/MeshData.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {UUID} from '../UUID';
|
||||
import {SculptType} from '../../enums/SculptType';
|
||||
|
||||
export class MeshData
|
||||
{
|
||||
meshData: UUID = UUID.zero();
|
||||
type: SculptType = SculptType.None;
|
||||
|
||||
constructor(buf: Buffer, pos: number, length: number)
|
||||
{
|
||||
if (length >= 17)
|
||||
{
|
||||
this.meshData = new UUID(buf, pos);
|
||||
pos += 16;
|
||||
this.type = buf.readUInt8(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
lib/classes/public/Parcel.ts
Normal file
111
lib/classes/public/Parcel.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import {Vector3} from '../Vector3';
|
||||
import {UUID} from '../UUID';
|
||||
import * as builder from 'xmlbuilder';
|
||||
import * as LLSD from '@caspertech/llsd';
|
||||
import {ParcelFlags} from '../../enums/ParcelFlags';
|
||||
|
||||
export class Parcel
|
||||
{
|
||||
LocalID: number;
|
||||
ParcelID: UUID;
|
||||
|
||||
RegionDenyAgeUnverified: boolean;
|
||||
|
||||
MediaDesc: string;
|
||||
MediaWidth: number;
|
||||
MediaHeight: number;
|
||||
MediaLoop: number;
|
||||
MediaType: string;
|
||||
ObscureMedia: number;
|
||||
ObscureMusic: number;
|
||||
|
||||
AABBMax: Vector3;
|
||||
AABBMin: Vector3;
|
||||
AnyAVSounds: boolean;
|
||||
Area: number;
|
||||
AuctionID: number;
|
||||
AuthBuyerID: UUID;
|
||||
Bitmap: Buffer;
|
||||
Category: number;
|
||||
ClaimDate: number;
|
||||
ClaimPrice: number;
|
||||
Desc: string;
|
||||
GroupAVSounds: boolean;
|
||||
GroupID: UUID;
|
||||
GroupPrims: number;
|
||||
IsGroupOwned: boolean;
|
||||
LandingType: number;
|
||||
MaxPrims: number;
|
||||
MediaAutoScale: number;
|
||||
MediaID: UUID;
|
||||
MediaURL: string;
|
||||
MusicURL: string;
|
||||
Name: string;
|
||||
OtherCleanTime: number;
|
||||
OtherCount: number;
|
||||
OtherPrims: number;
|
||||
OwnerID: UUID;
|
||||
OwnerPrims: number;
|
||||
ParcelFlags: ParcelFlags;
|
||||
ParcelPrimBonus: number;
|
||||
PassHours: number;
|
||||
PassPrice: number;
|
||||
PublicCount: number;
|
||||
RegionDenyAnonymous: boolean;
|
||||
RegionDenyIdentified: boolean;
|
||||
RegionDenyTransacted: boolean;
|
||||
RegionPushOverride: boolean;
|
||||
RentPrice: number;
|
||||
RequestResult: number;
|
||||
SalePrice: number;
|
||||
SeeAvs: boolean;
|
||||
SelectedPrims: number;
|
||||
SelfCount: number;
|
||||
SequenceID: number;
|
||||
SimWideMaxPrims: number;
|
||||
SimWideTotalPrims: number;
|
||||
SnapSelection: boolean;
|
||||
SnapshotID: UUID;
|
||||
Status: number;
|
||||
TotalPrims: number;
|
||||
UserLocation: Vector3;
|
||||
UserLookAt: Vector3;
|
||||
|
||||
RegionAllowAccessOverride: boolean;
|
||||
|
||||
exportXML(): string
|
||||
{
|
||||
const document = builder.create('LandData');
|
||||
document.ele('Area', this.Area);
|
||||
document.ele('AuctionID', this.AuctionID);
|
||||
document.ele('AuthBuyerID', this.AuthBuyerID.toString());
|
||||
document.ele('Category', this.Category);
|
||||
document.ele('ClaimDate', this.ClaimDate);
|
||||
document.ele('ClaimPrice', this.ClaimPrice);
|
||||
document.ele('GlobalID', this.ParcelID.toString());
|
||||
document.ele('GroupID', this.GroupID.toString());
|
||||
document.ele('IsGroupOwned', this.IsGroupOwned);
|
||||
document.ele('Bitmap', this.Bitmap.toString('base64'));
|
||||
document.ele('Description', this.Desc);
|
||||
document.ele('Flags', this.ParcelFlags);
|
||||
document.ele('LandingType', this.LandingType);
|
||||
document.ele('Name', this.Name);
|
||||
document.ele('Status', this.Status);
|
||||
document.ele('LocalID', this.LocalID);
|
||||
document.ele('MediaAutoScale', this.MediaAutoScale);
|
||||
document.ele('MediaID', this.MediaID.toString());
|
||||
document.ele('MediaURL', this.MediaURL);
|
||||
document.ele('MusicURL', this.MusicURL);
|
||||
document.ele('OwnerID', this.OwnerID.toString());
|
||||
document.ele('ParcelAccessList');
|
||||
document.ele('PassHours', this.PassHours);
|
||||
document.ele('PassPrice', this.PassPrice);
|
||||
document.ele('SalePrice', this.SalePrice);
|
||||
document.ele('SnapshotID', this.SnapshotID.toString());
|
||||
document.ele('UserLocation', this.UserLocation.toString());
|
||||
document.ele('UserLookAt', this.UserLookAt.toString());
|
||||
document.ele('Dwell', 0);
|
||||
document.ele('OtherCleanTime', this.OtherCleanTime);
|
||||
return document.end({pretty: true, allowEmpty: true});
|
||||
}
|
||||
}
|
||||
76
lib/classes/public/RegionEnvironment.ts
Normal file
76
lib/classes/public/RegionEnvironment.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {UUID} from '../UUID';
|
||||
import {Vector4} from '../Vector4';
|
||||
import {Color4} from '../Color4';
|
||||
import {Vector2} from '../Vector2';
|
||||
import {Vector3} from '../Vector3';
|
||||
import {XMLElementOrXMLNode} from 'xmlbuilder';
|
||||
import {SkyPreset} from './interfaces/SkyPreset';
|
||||
import {WaterPreset} from './interfaces/WaterPreset';
|
||||
|
||||
export class RegionEnvironment
|
||||
{
|
||||
regionID: UUID;
|
||||
dayCycleKeyframes: {
|
||||
time: number,
|
||||
preset: string
|
||||
}[];
|
||||
skyPresets: {
|
||||
[key: string]: SkyPreset
|
||||
} = {};
|
||||
water: WaterPreset;
|
||||
|
||||
getXML(xml: XMLElementOrXMLNode)
|
||||
{
|
||||
const env = xml.ele('Environment');
|
||||
const dayCycle = env.ele('DayCycle');
|
||||
for (const keyFrame of this.dayCycleKeyframes)
|
||||
{
|
||||
const kf = dayCycle.ele('KeyFrame');
|
||||
kf.ele('Time', keyFrame.time);
|
||||
kf.ele('Preset', keyFrame.preset);
|
||||
}
|
||||
const skyPresets = env.ele('SkyPresets');
|
||||
for (const presetKey of Object.keys(this.skyPresets))
|
||||
{
|
||||
const preset = this.skyPresets[presetKey];
|
||||
const pre = skyPresets.ele('Preset');
|
||||
pre.att('name', presetKey);
|
||||
Vector4.getXML(pre.ele('Ambient'), preset.ambient);
|
||||
Vector4.getXML(pre.ele('BlueDensity'), preset.blueDensity);
|
||||
Vector4.getXML(pre.ele('BlueHorizon'), preset.blueHorizon);
|
||||
Color4.getXML(pre.ele('CloudColor'), preset.cloudColor);
|
||||
Vector4.getXML(pre.ele('CloudPosDensity1'), preset.cloudPosDensity1);
|
||||
Vector4.getXML(pre.ele('CloudPosDensity2'), preset.cloudPosDensity2);
|
||||
Vector4.getXML(pre.ele('CloudScale'), preset.cloudScale);
|
||||
Vector2.getXML(pre.ele('CloudScrollRate'), preset.cloudScrollRate);
|
||||
Vector4.getXML(pre.ele('CloudShadow'), preset.cloudScale);
|
||||
Vector4.getXML(pre.ele('DensityMultiplier'), preset.cloudScale);
|
||||
Vector4.getXML(pre.ele('DistanceMultiplier'), preset.cloudScale);
|
||||
pre.ele('EastAngle', preset.eastAngle);
|
||||
const cloudScroll = pre.ele('EnableCloudScroll');
|
||||
cloudScroll.ele('X', preset.enableCloudScroll.x);
|
||||
cloudScroll.ele('Y', preset.enableCloudScroll.y);
|
||||
Vector4.getXML(pre.ele('Gamma'), preset.gamma);
|
||||
Vector4.getXML(pre.ele('Glow'), preset.glow);
|
||||
Vector4.getXML(pre.ele('HazeDensity'), preset.hazeDensity);
|
||||
Vector4.getXML(pre.ele('HazeHorizon'), preset.hazeHorizon);
|
||||
Vector4.getXML(pre.ele('LightNormal'), preset.lightNormal);
|
||||
Vector4.getXML(pre.ele('MaxY'), preset.maxY);
|
||||
pre.ele('StarBrightness', preset.starBrightness);
|
||||
pre.ele('SunAngle', preset.sunAngle);
|
||||
Color4.getXML(pre.ele('SunLightColor'), preset.sunlightColor);
|
||||
}
|
||||
const water = env.ele('Water');
|
||||
water.ele('BlurMultiplier', this.water.blurMultiplier);
|
||||
water.ele('FresnelOffset', this.water.fresnelOffset);
|
||||
water.ele('FresnelScale', this.water.fresnelScale);
|
||||
UUID.getXML(water.ele('NormalMap'), this.water.normalMap);
|
||||
water.ele('ScaleAbove', this.water.scaleAbove);
|
||||
water.ele('ScaleBelow', this.water.scaleBelow);
|
||||
water.ele('UnderWaterFogMod', this.water.underWaterFogMod);
|
||||
Color4.getXML(water.ele('WaterFogColor'), this.water.waterFogColor);
|
||||
water.ele('WaterFogDensity', this.water.waterFogDensity);
|
||||
Vector2.getXML(water.ele('Wave1Dir'), this.water.wave1Dir);
|
||||
Vector2.getXML(water.ele('Wave2Dir'), this.water.wave2Dir);
|
||||
}
|
||||
}
|
||||
18
lib/classes/public/SculptData.ts
Normal file
18
lib/classes/public/SculptData.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {UUID} from '../UUID';
|
||||
import {SculptType} from '../../enums/SculptType';
|
||||
|
||||
export class SculptData
|
||||
{
|
||||
texture: UUID = UUID.zero();
|
||||
type: SculptType = SculptType.None;
|
||||
|
||||
constructor(buf: Buffer, pos: number, length: number)
|
||||
{
|
||||
if (length >= 17)
|
||||
{
|
||||
this.texture = new UUID(buf, pos);
|
||||
pos += 16;
|
||||
this.type = buf.readUInt8(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
lib/classes/public/interfaces/SkyPreset.ts
Normal file
32
lib/classes/public/interfaces/SkyPreset.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {Vector4} from '../../Vector4';
|
||||
import {Color4} from '../../Color4';
|
||||
import {Vector2} from '../../Vector2';
|
||||
|
||||
export interface SkyPreset
|
||||
{
|
||||
ambient: Vector4,
|
||||
blueDensity: Vector4,
|
||||
blueHorizon: Vector4,
|
||||
cloudColor: Color4,
|
||||
cloudPosDensity1: Vector4,
|
||||
cloudPosDensity2: Vector4,
|
||||
cloudScale: Vector4,
|
||||
cloudScrollRate: Vector2,
|
||||
cloudShadow: Vector4,
|
||||
densityMultiplier: Vector4,
|
||||
distanceMultiplier: Vector4,
|
||||
eastAngle: number,
|
||||
enableCloudScroll: {
|
||||
x: boolean,
|
||||
y: boolean
|
||||
},
|
||||
gamma: Vector4,
|
||||
glow: Vector4,
|
||||
hazeDensity: Vector4,
|
||||
hazeHorizon: Vector4,
|
||||
lightNormal: Vector4,
|
||||
maxY: Vector4,
|
||||
starBrightness: number,
|
||||
sunAngle: number,
|
||||
sunlightColor: Color4
|
||||
}
|
||||
20
lib/classes/public/interfaces/WaterPreset.ts
Normal file
20
lib/classes/public/interfaces/WaterPreset.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {Vector3} from '../../Vector3';
|
||||
import {UUID} from '../../UUID';
|
||||
import {Color4} from '../../Color4';
|
||||
import {Vector2} from '../../Vector2';
|
||||
|
||||
export interface WaterPreset
|
||||
{
|
||||
blurMultiplier: number,
|
||||
fresnelOffset: number,
|
||||
fresnelScale: number,
|
||||
normalScale: Vector3,
|
||||
normalMap: UUID,
|
||||
scaleAbove: number,
|
||||
scaleBelow: number,
|
||||
underWaterFogMod: number,
|
||||
waterFogColor: Color4,
|
||||
waterFogDensity: number,
|
||||
wave1Dir: Vector2,
|
||||
wave2Dir: Vector2
|
||||
}
|
||||
25
lib/enums/AssetTypeLL.ts
Normal file
25
lib/enums/AssetTypeLL.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export enum AssetTypeLL
|
||||
{
|
||||
texture = 0,
|
||||
sound = 1,
|
||||
callcard = 2,
|
||||
landmark = 3,
|
||||
script = 4,
|
||||
clothing = 5,
|
||||
object = 6,
|
||||
notecard = 7,
|
||||
category = 8,
|
||||
lsltext = 10,
|
||||
lslbyte = 11,
|
||||
txtr_tga = 12,
|
||||
bodypart = 13,
|
||||
snd_wav = 17,
|
||||
img_tga = 18,
|
||||
jpeg = 19,
|
||||
animatn = 20,
|
||||
gesture = 21,
|
||||
simstate = 22,
|
||||
link = 24,
|
||||
link_f = 25,
|
||||
mesh = 49
|
||||
}
|
||||
21
lib/enums/Bumpiness.ts
Normal file
21
lib/enums/Bumpiness.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export enum Bumpiness
|
||||
{
|
||||
None = 0,
|
||||
Brightness = 1,
|
||||
Darkness = 2,
|
||||
Woodgrain = 3,
|
||||
Bark = 4,
|
||||
Bricks = 5,
|
||||
Checker = 6,
|
||||
Concrete = 7,
|
||||
Crustytile = 8,
|
||||
Cutstone = 9,
|
||||
Discs = 10,
|
||||
Gravel = 11,
|
||||
Petridish = 12,
|
||||
Siding = 13,
|
||||
Stonetile = 14,
|
||||
Stucco = 15,
|
||||
Suction = 16,
|
||||
Weave = 17
|
||||
}
|
||||
@@ -19,5 +19,6 @@ export enum HTTPAssets
|
||||
ASSET_LINK_FOLDER = 'link_f',
|
||||
ASSET_MESH = 'mesh',
|
||||
ASSET_WIDGET = 'widget',
|
||||
ASSET_PERSON = 'person'
|
||||
ASSET_PERSON = 'person',
|
||||
ASSET_MATERIAL = 'material'
|
||||
}
|
||||
|
||||
7
lib/enums/HoleType.ts
Normal file
7
lib/enums/HoleType.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum HoleType
|
||||
{
|
||||
Same = 0x00,
|
||||
Circle = 0x10,
|
||||
Square = 0x20,
|
||||
Triangle = 0x30
|
||||
}
|
||||
18
lib/enums/InventoryTypeLL.ts
Normal file
18
lib/enums/InventoryTypeLL.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export enum InventoryTypeLL
|
||||
{
|
||||
texture = 0,
|
||||
sound = 1,
|
||||
callcard = 2,
|
||||
landmark = 3,
|
||||
object = 6,
|
||||
notecard = 7,
|
||||
category = 8,
|
||||
root = 9,
|
||||
script = 10,
|
||||
snapshot = 15,
|
||||
attach = 17,
|
||||
wearable = 18,
|
||||
animation = 19,
|
||||
gesture = 20,
|
||||
mesh = 22
|
||||
}
|
||||
11
lib/enums/LayerType.ts
Normal file
11
lib/enums/LayerType.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export enum LayerType
|
||||
{
|
||||
Land = 0x4C,
|
||||
LandExtended = 0x4D,
|
||||
Water = 0x57,
|
||||
WaterExtended = 0x57,
|
||||
Wind = 0x37,
|
||||
WindExtended = 0x39,
|
||||
Cloud = 0x38,
|
||||
CloudExtended = 0x3A
|
||||
}
|
||||
6
lib/enums/MappingType.ts
Normal file
6
lib/enums/MappingType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum MappingType
|
||||
{
|
||||
Default = 0,
|
||||
Planar = 2,
|
||||
Spherical = 4
|
||||
}
|
||||
36
lib/enums/ParcelFlags.ts
Normal file
36
lib/enums/ParcelFlags.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export enum ParcelFlags
|
||||
{
|
||||
None = 0,
|
||||
AllowFly = 1 << 0,
|
||||
AllowOtherScripts = 1 << 1,
|
||||
ForSale = 1 << 2,
|
||||
AllowLandmark = 1 << 3,
|
||||
AllowTerraform = 1 << 4,
|
||||
AllowDamage = 1 << 5,
|
||||
CreateObjects = 1 << 6,
|
||||
ForSaleObjects = 1 << 7,
|
||||
UseAccessGroup = 1 << 8,
|
||||
UseAccessList = 1 << 9,
|
||||
UseBanList = 1 << 10,
|
||||
UsePassList = 1 << 11,
|
||||
ShowDirectory = 1 << 12,
|
||||
AllowDeedToGroup = 1 << 13,
|
||||
ContributeWithDeed = 1 << 14,
|
||||
SoundLocal = 1 << 15,
|
||||
SellParcelObjects = 1 << 16,
|
||||
AllowPublish = 1 << 17,
|
||||
MaturePublish = 1 << 18,
|
||||
UrlWebPage = 1 << 19,
|
||||
UrlRawHtml = 1 << 20,
|
||||
RestrictPushObject = 1 << 21,
|
||||
DenyAnonymous = 1 << 22,
|
||||
LindenHome = 1 << 23,
|
||||
DenyTransacted = 1 << 24,
|
||||
AllowGroupScripts = 1 << 25,
|
||||
CreateGroupObjects = 1 << 26,
|
||||
AllowAPrimitiveEntry = 1 << 27,
|
||||
AllowGroupObjectEntry = 1 << 28,
|
||||
AllowVoiceChat = 1 << 29,
|
||||
UseEstateVoiceChan = 1 << 30,
|
||||
DenyAgeUnverified = 1 << 31
|
||||
}
|
||||
6
lib/enums/PhysicsShapeType.ts
Normal file
6
lib/enums/PhysicsShapeType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum PhysicsShapeType
|
||||
{
|
||||
Prim = 0,
|
||||
None = 1,
|
||||
ConvexHull = 2
|
||||
}
|
||||
36
lib/enums/PrimFlags.ts
Normal file
36
lib/enums/PrimFlags.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export enum PrimFlags
|
||||
{
|
||||
None = 0,
|
||||
Physics = 0x00000001,
|
||||
CreateSelected = 0x00000002,
|
||||
ObjectModify = 0x00000004,
|
||||
ObjectCopy = 0x00000008,
|
||||
ObjectAnyOwner = 0x00000010,
|
||||
ObjectYouOwner = 0x00000020,
|
||||
Scripted = 0x00000040,
|
||||
Touch = 0x00000080,
|
||||
ObjectMove = 0x00000100,
|
||||
Money = 0x00000200,
|
||||
Phantom = 0x00000400,
|
||||
InventoryEmpty = 0x00000800,
|
||||
JointHinge = 0x00001000,
|
||||
JointP2P = 0x00002000,
|
||||
JointLP2P = 0x00004000,
|
||||
JointWheel = 0x00008000,
|
||||
AllowInventoryDrop = 0x00010000,
|
||||
ObjectTransfer = 0x00020000,
|
||||
ObjectGroupOwned = 0x00040000,
|
||||
ObjectYouOfficer = 0x00080000,
|
||||
CameraDecoupled = 0x00100000,
|
||||
AnimSource = 0x00200000,
|
||||
CameraSource = 0x00400000,
|
||||
CastShadows = 0x00800000,
|
||||
DieAtEdge = 0x01000000,
|
||||
ReturnAtEdge = 0x02000000,
|
||||
Sandbox = 0x04000000,
|
||||
Flying = 0x08000000,
|
||||
ObjectOwnerModify = 0x10000000,
|
||||
TemporaryOnRez = 0x20000000,
|
||||
Temporary = 0x40000000,
|
||||
ZlibCompressed = 0x80000000
|
||||
}
|
||||
9
lib/enums/ProfileShape.ts
Normal file
9
lib/enums/ProfileShape.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export enum ProfileShape
|
||||
{
|
||||
Circle = 0,
|
||||
Square = 1,
|
||||
IsometricTriangle = 2,
|
||||
EquilateralTriangle = 3,
|
||||
RightTriangle = 4,
|
||||
HalfCircle = 5
|
||||
}
|
||||
7
lib/enums/SaleTypeLL.ts
Normal file
7
lib/enums/SaleTypeLL.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum SaleTypeLL
|
||||
{
|
||||
not = 0,
|
||||
orig = 1,
|
||||
copy = 2,
|
||||
cntn = 3
|
||||
}
|
||||
11
lib/enums/SculptType.ts
Normal file
11
lib/enums/SculptType.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export enum SculptType
|
||||
{
|
||||
None = 0,
|
||||
Sphere = 1,
|
||||
Torus = 2,
|
||||
Plane = 3,
|
||||
Cylinder = 4,
|
||||
Mesh = 5,
|
||||
Invert = 64,
|
||||
Mirror = 128
|
||||
}
|
||||
7
lib/enums/Shininess.ts
Normal file
7
lib/enums/Shininess.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum Shininess
|
||||
{
|
||||
None = 0,
|
||||
Low = 0x40,
|
||||
Medium = 0x80,
|
||||
High = 0xC0
|
||||
}
|
||||
10
lib/enums/SimAccessFlags.ts
Normal file
10
lib/enums/SimAccessFlags.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum SimAccessFlags
|
||||
{
|
||||
Unknown = 0,
|
||||
Trial = 7,
|
||||
PG = 13,
|
||||
Mature = 21,
|
||||
Adult = 42,
|
||||
Down = 254,
|
||||
NonExistent = 255
|
||||
}
|
||||
11
lib/enums/TextureAnimFlags.ts
Normal file
11
lib/enums/TextureAnimFlags.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export enum TextureAnimFlags
|
||||
{
|
||||
ANIM_OFF = 0x00,
|
||||
ANIM_ON = 0x01,
|
||||
LOOP = 0x02,
|
||||
REVERSE = 0x04,
|
||||
PING_PONG = 0x08,
|
||||
SMOOTH = 0x10,
|
||||
ROTATE = 0x20,
|
||||
SCALE = 0x40
|
||||
}
|
||||
10
lib/enums/TransferStatus.ts
Normal file
10
lib/enums/TransferStatus.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum TransferStatus
|
||||
{
|
||||
InsufficientPermissions = -3,
|
||||
NotFound = -2,
|
||||
Error = -1,
|
||||
OK = 0,
|
||||
Done = 1,
|
||||
Skip = 2,
|
||||
Abort = 3,
|
||||
}
|
||||
9
lib/events/NewObjectEvent.ts
Normal file
9
lib/events/NewObjectEvent.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {UUID} from '..';
|
||||
import {GameObject} from '../classes/public/GameObject';
|
||||
|
||||
export class NewObjectEvent
|
||||
{
|
||||
objectID: UUID;
|
||||
localID: number;
|
||||
object: GameObject;
|
||||
}
|
||||
9
lib/events/ObjectKilledEvent.ts
Normal file
9
lib/events/ObjectKilledEvent.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {UUID} from '..';
|
||||
import {GameObject} from '../classes/public/GameObject';
|
||||
|
||||
export class ObjectKilledEvent
|
||||
{
|
||||
objectID: UUID;
|
||||
localID: number;
|
||||
object: GameObject;
|
||||
}
|
||||
12
lib/events/ObjectPhysicsDataEvent.ts
Normal file
12
lib/events/ObjectPhysicsDataEvent.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {PhysicsShapeType} from '../enums/PhysicsShapeType';
|
||||
|
||||
export class ObjectPhysicsDataEvent
|
||||
{
|
||||
localID: number;
|
||||
|
||||
density: number;
|
||||
friction: number;
|
||||
gravityMultiplier: number;
|
||||
physicsShapeType: PhysicsShapeType;
|
||||
restitution: number;
|
||||
}
|
||||
9
lib/events/ObjectUpdatedEvent.ts
Normal file
9
lib/events/ObjectUpdatedEvent.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {UUID} from '..';
|
||||
import {GameObject} from '../classes/public/GameObject';
|
||||
|
||||
export class ObjectUpdatedEvent
|
||||
{
|
||||
objectID: UUID;
|
||||
localID: number;
|
||||
object: GameObject;
|
||||
}
|
||||
71
lib/events/ParcelPropertiesEvent.ts
Normal file
71
lib/events/ParcelPropertiesEvent.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {UUID, Vector3} from '..';
|
||||
import {ParcelFlags} from '../enums/ParcelFlags';
|
||||
|
||||
export class ParcelPropertiesEvent
|
||||
{
|
||||
LocalID: number;
|
||||
|
||||
RegionDenyAgeUnverified: boolean;
|
||||
|
||||
MediaDesc: string;
|
||||
MediaWidth: number;
|
||||
MediaHeight: number;
|
||||
MediaLoop: number;
|
||||
MediaType: string;
|
||||
ObscureMedia: number;
|
||||
ObscureMusic: number;
|
||||
|
||||
AABBMax: Vector3;
|
||||
AABBMin: Vector3;
|
||||
AnyAVSounds: boolean;
|
||||
Area: number;
|
||||
AuctionID: number;
|
||||
AuthBuyerID: UUID;
|
||||
Bitmap: Buffer;
|
||||
Category: number;
|
||||
ClaimDate: number;
|
||||
ClaimPrice: number;
|
||||
Desc: string;
|
||||
GroupAVSounds: boolean;
|
||||
GroupID: UUID;
|
||||
GroupPrims: number;
|
||||
IsGroupOwned: boolean;
|
||||
LandingType: number;
|
||||
MaxPrims: number;
|
||||
MediaAutoScale: number;
|
||||
MediaID: UUID;
|
||||
MediaURL: string;
|
||||
MusicURL: string;
|
||||
Name: string;
|
||||
OtherCleanTime: number;
|
||||
OtherCount: number;
|
||||
OtherPrims: number;
|
||||
OwnerID: UUID;
|
||||
OwnerPrims: number;
|
||||
ParcelFlags: ParcelFlags;
|
||||
ParcelPrimBonus: number;
|
||||
PassHours: number;
|
||||
PassPrice: number;
|
||||
PublicCount: number;
|
||||
RegionDenyAnonymous: boolean;
|
||||
RegionDenyIdentified: boolean;
|
||||
RegionDenyTransacted: boolean;
|
||||
RegionPushOverride: boolean;
|
||||
RentPrice: number;
|
||||
RequestResult: number;
|
||||
SalePrice: number;
|
||||
SeeAvs: boolean;
|
||||
SelectedPrims: number;
|
||||
SelfCount: number;
|
||||
SequenceID: number;
|
||||
SimWideMaxPrims: number;
|
||||
SimWideTotalPrims: number;
|
||||
SnapSelection: boolean;
|
||||
SnapshotID: UUID;
|
||||
Status: number;
|
||||
TotalPrims: number;
|
||||
UserLocation: Vector3;
|
||||
UserLookAt: Vector3;
|
||||
|
||||
RegionAllowAccessOverride: boolean;
|
||||
}
|
||||
64
lib/index.ts
64
lib/index.ts
@@ -55,6 +55,37 @@ import {TextureFlags} from './enums/TextureFlags';
|
||||
import {SourcePattern} from './enums/SourcePattern';
|
||||
import {BlendFunc} from './enums/BlendFunc';
|
||||
import {PCode} from './enums/PCode';
|
||||
import {Utils} from './classes/Utils';
|
||||
import {ObjectPhysicsDataEvent} from './events/ObjectPhysicsDataEvent';
|
||||
import {ParcelPropertiesEvent} from './events/ParcelPropertiesEvent';
|
||||
import {PrimFlags} from './enums/PrimFlags';
|
||||
import {TextureEntry} from './classes/TextureEntry';
|
||||
import {RegionEnvironment} from './classes/public/RegionEnvironment';
|
||||
import {Parcel} from './classes/public/Parcel';
|
||||
import {Material} from './classes/public/Material';
|
||||
import {GameObject} from './classes/public/GameObject';
|
||||
import {LightImageData} from './classes/public/LightImageData';
|
||||
import {LightData} from './classes/public/LightData';
|
||||
import {FlexibleData} from './classes/public/FlexibleData';
|
||||
import {MeshData} from './classes/public/MeshData';
|
||||
import {SculptData} from './classes/public/SculptData';
|
||||
import {SkyPreset} from './classes/public/interfaces/SkyPreset';
|
||||
import {WaterPreset} from './classes/public/interfaces/WaterPreset';
|
||||
import {NewObjectEvent} from './events/NewObjectEvent';
|
||||
import {ObjectKilledEvent} from './events/ObjectKilledEvent';
|
||||
import {ObjectUpdatedEvent} from './events/ObjectUpdatedEvent';
|
||||
import {Bumpiness} from './enums/Bumpiness';
|
||||
import {HoleType} from './enums/HoleType';
|
||||
import {LayerType} from './enums/LayerType';
|
||||
import {MappingType} from './enums/MappingType';
|
||||
import {PhysicsShapeType} from './enums/PhysicsShapeType';
|
||||
import {ParcelFlags} from './enums/ParcelFlags';
|
||||
import {ProfileShape} from './enums/ProfileShape';
|
||||
import {SculptType} from './enums/SculptType';
|
||||
import {Shininess} from './enums/Shininess';
|
||||
import {SimAccessFlags} from './enums/SimAccessFlags';
|
||||
import {TextureAnimFlags} from './enums/TextureAnimFlags';
|
||||
import {TransferStatus} from './enums/TransferStatus';
|
||||
|
||||
export {
|
||||
Bot,
|
||||
@@ -68,6 +99,8 @@ export {
|
||||
UUID,
|
||||
Vector3,
|
||||
Vector2,
|
||||
Utils,
|
||||
TextureEntry,
|
||||
|
||||
// Flags
|
||||
AgentFlags,
|
||||
@@ -90,6 +123,19 @@ export {
|
||||
SourcePattern,
|
||||
BlendFunc,
|
||||
PCode,
|
||||
PrimFlags,
|
||||
Bumpiness,
|
||||
HoleType,
|
||||
LayerType,
|
||||
MappingType,
|
||||
ParcelFlags,
|
||||
PhysicsShapeType,
|
||||
ProfileShape,
|
||||
SculptType,
|
||||
Shininess,
|
||||
SimAccessFlags,
|
||||
TextureAnimFlags,
|
||||
TransferStatus,
|
||||
|
||||
// Events
|
||||
ChatEvent,
|
||||
@@ -113,12 +159,28 @@ export {
|
||||
FriendOnlineEvent,
|
||||
FriendRightsEvent,
|
||||
FriendRemovedEvent,
|
||||
ObjectPhysicsDataEvent,
|
||||
ParcelPropertiesEvent,
|
||||
NewObjectEvent,
|
||||
ObjectKilledEvent,
|
||||
ObjectUpdatedEvent,
|
||||
|
||||
// Public Classes
|
||||
Avatar,
|
||||
Friend,
|
||||
FlexibleData,
|
||||
LightData,
|
||||
LightImageData,
|
||||
GameObject,
|
||||
Material,
|
||||
Parcel,
|
||||
RegionEnvironment,
|
||||
SculptData,
|
||||
MeshData,
|
||||
|
||||
// Public Interfaces
|
||||
GlobalPosition,
|
||||
MapLocation
|
||||
MapLocation,
|
||||
SkyPreset,
|
||||
WaterPreset
|
||||
};
|
||||
|
||||
91
package-lock.json
generated
91
package-lock.json
generated
@@ -122,6 +122,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/xmlbuilder": {
|
||||
"version": "0.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/xmlbuilder/-/xmlbuilder-0.0.34.tgz",
|
||||
"integrity": "sha512-yVsHfYqJblSEg3DvUhGndpCZBZz2GiGVmqMa04fbGro2xzxRj85Q7MQ4os+MaXmKcpCDD42MXuxUWfoUKTuVdQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/xmlrpc": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/xmlrpc/-/xmlrpc-1.3.5.tgz",
|
||||
@@ -432,6 +438,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||
},
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
@@ -830,6 +841,14 @@
|
||||
"map-cache": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
|
||||
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
|
||||
"requires": {
|
||||
"minipass": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -1201,8 +1220,31 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
|
||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz",
|
||||
"integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==",
|
||||
"requires": {
|
||||
"minipass": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.1",
|
||||
@@ -1227,7 +1269,6 @@
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -1251,6 +1292,11 @@
|
||||
"supports-color": "5.4.0"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
@@ -1728,6 +1774,27 @@
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz",
|
||||
"integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==",
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.3.3",
|
||||
"minizlib": "^1.1.0",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"tiny-async-pool": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.0.1.tgz",
|
||||
@@ -2005,9 +2072,9 @@
|
||||
"integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU="
|
||||
},
|
||||
"xmlbuilder": {
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
||||
"integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M="
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.0.tgz",
|
||||
"integrity": "sha512-In21jFWiaulS7Cmw1fPT1Lm7g7L6ml/uwZNAaKlDZc78szm3pn5oH9gizH7sh1h2GGRb3OkL5kLCeMEENEnZwA=="
|
||||
},
|
||||
"xmldom": {
|
||||
"version": "0.1.27",
|
||||
@@ -2021,8 +2088,20 @@
|
||||
"requires": {
|
||||
"sax": "1.2.x",
|
||||
"xmlbuilder": "8.2.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"xmlbuilder": {
|
||||
"version": "8.2.2",
|
||||
"resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
||||
"integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M="
|
||||
}
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
|
||||
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
|
||||
},
|
||||
"yn": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@types/uuid": "^3.4.4",
|
||||
"@types/validator": "^9.4.2",
|
||||
"@types/xml": "^1.0.2",
|
||||
"@types/xmlbuilder": "0.0.34",
|
||||
"@types/xmlrpc": "^1.3.5",
|
||||
"mocha": "^5.2.0",
|
||||
"source-map-support": "^0.5.9",
|
||||
@@ -45,13 +46,16 @@
|
||||
"ipaddr.js": "^1.8.1",
|
||||
"long": "^4.0.0",
|
||||
"micromatch": "^3.1.10",
|
||||
"moment": "^2.22.2",
|
||||
"rbush-3d": "0.0.4",
|
||||
"request": "^2.88.0",
|
||||
"rxjs": "^6.3.3",
|
||||
"tar": "^4.4.6",
|
||||
"tiny-async-pool": "^1.0.1",
|
||||
"uuid": "^3.3.2",
|
||||
"validator": "^10.8.0",
|
||||
"xml": "^1.0.1",
|
||||
"xmlbuilder": "^10.1.0",
|
||||
"xmlrpc": "^1.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user