Files
node-metaverse/lib/classes/EventQueueClient.ts

503 lines
28 KiB
TypeScript
Raw Normal View History

import {Caps} from './Caps';
import * as LLSD from '@caspertech/llsd';
import * as request from 'request';
import * as Long from 'long';
import {IPAddress} from './IPAddress';
import {ClientEvents} from './ClientEvents';
import {TeleportEventType} from '../enums/TeleportEventType';
import {UUID} from './UUID';
import {Agent} from './Agent';
import {
EventQueueStateChangeEvent,
GroupChatEvent,
GroupChatSessionAgentListEvent,
- 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
2018-10-31 11:28:24 +00:00
GroupChatSessionJoinEvent, ObjectPhysicsDataEvent, ParcelPropertiesEvent,
TeleportEvent, Vector3
} from '..';
export class EventQueueClient
{
caps: Caps;
ack?: number;
done = false;
currentRequest: request.Request | null = null;
private clientEvents: ClientEvents;
private agent: Agent;
constructor(agent: Agent, caps: Caps, clientEvents: ClientEvents)
{
this.agent = agent;
this.clientEvents = clientEvents;
this.caps = caps;
this.Get();
const state = new EventQueueStateChangeEvent();
state.active = true;
this.clientEvents.onEventQueueStateChange.next(state);
}
shutdown()
{
// We must ACK any outstanding events
this.done = true;
if (this.currentRequest !== null)
{
this.currentRequest.abort();
}
const req = {
'ack': this.ack,
'done': true
};
this.capsPostXML('EventQueueGet', req).then((data) =>
{
const state = new EventQueueStateChangeEvent();
state.active = false;
this.clientEvents.onEventQueueStateChange.next(state);
});
}
Get()
{
const req = {
'ack': this.ack,
'done': this.done
};
const startTime = new Date().getTime();
this.capsPostXML('EventQueueGet', req).then((data) =>
{
if (data['id'])
{
this.ack = data['id'];
}
else
{
this.ack = undefined;
}
try
{
if (data['events'])
{
data['events'].forEach((event: any) =>
{
try
{
if (event['message'])
{
// noinspection TsLint
switch (event['message'])
{
case 'EnableSimulator':
/*
{
"body": {
"SimulatorInfo": [
{
"Handle": "AALoAAAECwA=",
"IP": "2FIqRA==",
"Port": 13029
}
]
},
"message": "EnableSimulator"
}
*/
break;
case 'ParcelProperties':
- 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
2018-10-31 11:28:24 +00:00
{
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']));
- 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
2018-10-31 11:28:24 +00:00
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;
- 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
2018-10-31 11:28:24 +00:00
}
case 'AgentGroupDataUpdate':
/*
{
"body": {
"AgentData": [
{
"AgentID": "49cc9041-5c53-4c1c-8490-e6bb84cdbacd"
}
],
"GroupData": [
{
"AcceptNotices": true,
"Contribution": 0,
"GroupID": "06459c46-069f-4de1-c297-c966bd55ab91",
"GroupInsigniaID": "8dacb5c9-80bc-aae4-6a12-d792b6eb7dc4",
"GroupName": "Jez Ember Estates",
"GroupPowers": "AAAgAAQAAAA="
},
{
"AcceptNotices": true,
"Contribution": 0,
"GroupID": "539b5be0-bb18-d0ef-6c07-3326e0130aaf",
"GroupInsigniaID": "7d7d0b4a-bf5b-dc51-3869-5e0eaa6ad41d",
"GroupName": "**BOY BEARS MALL**",
"GroupPowers": "AAAIABgBAAA="
}
],
"NewGroupData": [
{
"ListInProfile": true
},
{
"ListInProfile": true
}
]
},
"message": "AgentGroupDataUpdate"
}
*/
break;
case 'AgentStateUpdate':
/*
{
"body": {
"can_modify_navmesh": true,
"has_modified_navmesh": true,
"preferences": {
"access_prefs": {
"max": "PG"
},
"default_object_perm_masks": {
"Everyone": 0,
"Group": 0,
"NextOwner": 532480
},
"god_level": 0,
"hover_height": 0,
"language": "",
"language_is_public": true
}
},
"message": "AgentStateUpdate"
}
*/
break;
case 'TeleportFailed':
{
const tpEvent = new TeleportEvent();
tpEvent.message = event['body']['Info'][0]['Reason'];
tpEvent.eventType = TeleportEventType.TeleportFailed;
tpEvent.simIP = '';
tpEvent.simPort = 0;
tpEvent.seedCapability = '';
this.clientEvents.onTeleportEvent.next(tpEvent);
break;
}
case "ChatterBoxSessionStartReply":
{
if (event['body'])
{
const gcsje = new GroupChatSessionJoinEvent();
gcsje.sessionID = new UUID(event['body']['session_id'].toString());
gcsje.success = event['body']['success'];
if (gcsje.success)
{
this.agent.addChatSession(gcsje.sessionID);
}
this.clientEvents.onGroupChatSessionJoin.next(gcsje);
}
break;
}
case 'ChatterBoxInvitation':
{
if (event['body'] && event['body']['instantmessage'] && event['body']['instantmessage']['message_params'] && event['body']['instantmessage']['message_params']['id'])
{
const messageParams = event['body']['instantmessage']['message_params'];
const imSessionID = messageParams['id'];
const groupChatEvent = new GroupChatEvent();
groupChatEvent.from = new UUID(messageParams['from_id'].toString());
groupChatEvent.fromName = messageParams['from_name'];
groupChatEvent.groupID = new UUID(messageParams['id'].toString());
groupChatEvent.message = messageParams['message'];
const requested = {
'method': 'accept invitation',
'session-id': imSessionID
};
this.caps.capsPostXML('ChatSessionRequest', requested).then((ignore: any) =>
{
this.agent.addChatSession(groupChatEvent.groupID);
const gcsje = new GroupChatSessionJoinEvent();
gcsje.sessionID = groupChatEvent.groupID;
gcsje.success = true;
this.clientEvents.onGroupChatSessionJoin.next(gcsje);
this.clientEvents.onGroupChat.next(groupChatEvent);
}).catch((err) =>
{
console.error(err);
});
}
break;
}
case 'ChatterBoxSessionAgentListUpdates':
2017-12-19 17:58:25 +00:00
{
if (event['body'])
2017-12-19 17:58:25 +00:00
{
if (event['body']['agent_updates'])
2017-12-19 17:58:25 +00:00
{
Object.keys(event['body']['agent_updates']).forEach((agentUpdate) =>
2017-12-19 17:58:25 +00:00
{
const updObj = event['body']['agent_updates'][agentUpdate];
const gcsale = new GroupChatSessionAgentListEvent();
gcsale.agentID = new UUID(agentUpdate);
gcsale.groupID = new UUID(event['body']['session_id'].toString());
gcsale.canVoiceChat = false;
gcsale.isModerator = false;
gcsale.entered = (updObj['transition'] === 'ENTER');
if (gcsale.entered && updObj['info'])
{
if (updObj['info']['can_voice_chat'] === true)
{
gcsale.canVoiceChat = true;
}
if (updObj['info']['is_moderator'] === true)
{
gcsale.isModerator = true;
}
}
this.clientEvents.onGroupChatAgentListUpdate.next(gcsale);
});
}
2017-12-19 17:58:25 +00:00
}
break;
2017-12-19 17:58:25 +00:00
}
case 'ObjectPhysicsProperties':
{
- 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
2018-10-31 11:28:24 +00:00
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':
{
const info = event['body']['Info'][0];
if (info['LocationID'])
{
info['LocationID'] = Buffer.from(info['LocationID'].toArray()).readUInt32LE(0);
const regionHandleBuf = Buffer.from(info['RegionHandle'].toArray());
info['RegionHandle'] = new Long(regionHandleBuf.readUInt32LE(0), regionHandleBuf.readUInt32LE(4), true);
info['SimIP'] = new IPAddress(Buffer.from(info['SimIP'].toArray()), 0).toString();
info['TeleportFlags'] = Buffer.from(info['TeleportFlags'].toArray()).readUInt32LE(0);
const tpEvent = new TeleportEvent();
tpEvent.message = '';
tpEvent.eventType = TeleportEventType.TeleportCompleted;
tpEvent.simIP = info['SimIP'];
tpEvent.simPort = info['SimPort'];
tpEvent.seedCapability = info['SeedCapability'];
this.clientEvents.onTeleportEvent.next(tpEvent);
}
break;
}
default:
console.log('Unhandled event:');
console.log(JSON.stringify(event, null, 4));
}
}
}
catch (erro)
{
console.error('Error handling cap');
console.error(erro);
}
});
}
}
catch (error)
{
console.error(error);
}
if (!this.done)
{
this.Get();
}
}).catch((err) =>
{
const time = (new Date().getTime()) - startTime;
if (time > 30000)
{
// This is the normal request timeout, so reconnect immediately
if (!this.done)
{
this.Get();
}
}
else
{
if (!this.done)
{
console.error('Event queue aborted after ' + time + 'ms. Reconnecting in 5 seconds');
// Wait 5 seconds before retrying
setTimeout(() =>
{
if (!this.done)
{
this.Get();
}
}, 5000);
}
}
});
}
request(url: string, data: string, contentType: string): Promise<string>
{
return new Promise<string>((resolve, reject) =>
{
this.currentRequest = request({
'headers': {
'Content-Length': data.length,
'Content-Type': contentType
},
'uri': url,
'body': data,
'rejectUnauthorized': false,
'method': 'POST',
'timeout': 1800000 // Super long timeout
}, (err, res, body) =>
{
this.currentRequest = null;
if (err)
{
reject(err);
}
else
{
resolve(body);
}
});
});
}
capsPostXML(capability: string, data: any, attempt: number = 0): Promise<any>
{
return new Promise<any>((resolve, reject) =>
{
this.caps.getCapability(capability).then((url) =>
{
const serializedData = LLSD.LLSD.formatXML(data);
this.request(url, serializedData, 'application/llsd+xml').then((body: string) =>
{
try
{
if (body.indexOf('<llsd>') !== -1)
{
const parsed = LLSD.LLSD.parseXML(body);
resolve(parsed);
}
else
{
// Retry caps request three times before giving up
if (attempt < 3 && capability !== 'EventQueueGet')
{
return this.capsPostXML(capability, data, ++attempt);
}
else
{
reject(new Error('Not an LLSD response, capability: ' + capability));
}
}
}
catch (error)
{
reject(error);
}
}).catch((err) =>
{
reject(err);
});
}).catch((err) =>
{
reject(err);
});
});
}
}