Ping / circuit latency, break out commands, add typing function for IM, add thinkingTime and charactersPerSecond parameters to typing functions
This commit is contained in:
39
lib/classes/commands/AgentCommands.ts
Normal file
39
lib/classes/commands/AgentCommands.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {UUID} from '../UUID';
|
||||
import {AgentAnimationMessage} from '../messages/AgentAnimation';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
|
||||
export class AgentCommands extends CommandsBase
|
||||
{
|
||||
private animate(anim: UUID[], run: boolean): Promise<void>
|
||||
{
|
||||
|
||||
const circuit = this.currentRegion.circuit;
|
||||
const animPacket = new AgentAnimationMessage();
|
||||
animPacket.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID
|
||||
};
|
||||
animPacket.PhysicalAvatarEventList = [];
|
||||
animPacket.AnimationList = [];
|
||||
anim.forEach((a) =>
|
||||
{
|
||||
animPacket.AnimationList.push({
|
||||
AnimID: a,
|
||||
StartAnim: run
|
||||
});
|
||||
});
|
||||
|
||||
return circuit.waitForAck(circuit.sendMessage(animPacket, PacketFlags.Reliable), 10000);
|
||||
}
|
||||
|
||||
startAnimations(anim: UUID[]): Promise<void>
|
||||
{
|
||||
return this.animate(anim, true);
|
||||
}
|
||||
|
||||
stopAnimations(anim: UUID[]): Promise<void>
|
||||
{
|
||||
return this.animate(anim, false);
|
||||
}
|
||||
}
|
||||
50
lib/classes/commands/AssetCommands.ts
Normal file
50
lib/classes/commands/AssetCommands.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
import {HTTPAssets} from '../../enums/HTTPAssets';
|
||||
import {UUID} from '../UUID';
|
||||
import * as LLSD from 'llsd';
|
||||
import {Utils} from '../Utils';
|
||||
|
||||
export class AssetCommands extends CommandsBase
|
||||
{
|
||||
downloadAsset(type: HTTPAssets, uuid: UUID)
|
||||
{
|
||||
return this.currentRegion.caps.downloadAsset(uuid, type);
|
||||
}
|
||||
|
||||
uploadAsset(type: HTTPAssets, data: Buffer, name: string, description: string): Promise<UUID>
|
||||
{
|
||||
return new Promise<UUID>((resolve, reject) =>
|
||||
{
|
||||
if (this.agent && this.agent.inventory && this.agent.inventory.main && this.agent.inventory.main.root)
|
||||
{
|
||||
this.currentRegion.caps.capsRequestXML('NewFileAgentInventory', {
|
||||
'folder_id': new LLSD.UUID(this.agent.inventory.main.root.toString()),
|
||||
'asset_type': type,
|
||||
'inventory_type': Utils.HTTPAssetTypeToInventoryType(type),
|
||||
'name': name,
|
||||
'description': description,
|
||||
'everyone_mask': (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19),
|
||||
'group_mask': (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19),
|
||||
'next_owner_mask': (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19),
|
||||
'expected_upload_cost': 0
|
||||
}).then((response: any) =>
|
||||
{
|
||||
if (response['state'] === 'upload')
|
||||
{
|
||||
const uploadURL = response['uploader'];
|
||||
this.currentRegion.caps.capsRequestUpload(uploadURL, data).then((responseUpload: any) =>
|
||||
{
|
||||
resolve(new UUID(responseUpload['new_asset'].toString()));
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
}).catch((err) =>
|
||||
{
|
||||
console.log(err);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
20
lib/classes/commands/CommandsBase.ts
Normal file
20
lib/classes/commands/CommandsBase.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {Region} from '../Region';
|
||||
import {Bot} from '../../Bot';
|
||||
import {Agent} from '../Agent';
|
||||
import {Circuit} from '../Circuit';
|
||||
|
||||
export class CommandsBase
|
||||
{
|
||||
protected currentRegion: Region;
|
||||
protected agent: Agent;
|
||||
protected bot: Bot;
|
||||
protected circuit: Circuit;
|
||||
|
||||
constructor(region: Region, agent: Agent, bot: Bot)
|
||||
{
|
||||
this.currentRegion = region;
|
||||
this.agent = agent;
|
||||
this.bot = bot;
|
||||
this.circuit = this.currentRegion.circuit;
|
||||
}
|
||||
}
|
||||
296
lib/classes/commands/CommunicationsCommands.ts
Normal file
296
lib/classes/commands/CommunicationsCommands.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
import {UUID} from '../UUID';
|
||||
import {Utils} from '../Utils';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {ImprovedInstantMessageMessage} from '../messages/ImprovedInstantMessage';
|
||||
import {Vector3} from '../Vector3';
|
||||
import {ChatFromViewerMessage} from '../messages/ChatFromViewer';
|
||||
import {ChatType} from '../../enums/ChatType';
|
||||
import {InstantMessageDialog} from '../../enums/InstantMessageDialog';
|
||||
import Timer = NodeJS.Timer;
|
||||
|
||||
export class CommunicationsCommands extends CommandsBase
|
||||
{
|
||||
sendInstantMessage(to: UUID | string, message: string): Promise<void>
|
||||
{
|
||||
const circuit = this.circuit;
|
||||
if (typeof to === 'string')
|
||||
{
|
||||
to = new UUID(to);
|
||||
}
|
||||
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
|
||||
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
|
||||
im.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID
|
||||
};
|
||||
im.MessageBlock = {
|
||||
FromGroup: false,
|
||||
ToAgentID: to,
|
||||
ParentEstateID: 0,
|
||||
RegionID: UUID.zero(),
|
||||
Position: Vector3.getZero(),
|
||||
Offline: 1,
|
||||
Dialog: 0,
|
||||
ID: UUID.zero(),
|
||||
Timestamp: 0,
|
||||
FromAgentName: Utils.StringToBuffer(agentName),
|
||||
Message: Utils.StringToBuffer(message),
|
||||
BinaryBucket: Buffer.allocUnsafe(0)
|
||||
};
|
||||
im.EstateBlock = {
|
||||
EstateID: 0
|
||||
};
|
||||
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
|
||||
return circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
nearbyChat(message: string, type: ChatType, channel?: number): Promise<void>
|
||||
{
|
||||
if (channel === undefined)
|
||||
{
|
||||
channel = 0;
|
||||
}
|
||||
const cfv = new ChatFromViewerMessage();
|
||||
cfv.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
cfv.ChatData = {
|
||||
Message: Utils.StringToBuffer(message),
|
||||
Type: type,
|
||||
Channel: channel
|
||||
};
|
||||
const sequenceNo = this.circuit.sendMessage(cfv, PacketFlags.Reliable);
|
||||
return this.circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
say(message: string, channel?: number): Promise<void>
|
||||
{
|
||||
return this.nearbyChat(message, ChatType.Normal, channel);
|
||||
}
|
||||
|
||||
whisper(message: string, channel?: number): Promise<void>
|
||||
{
|
||||
return this.nearbyChat(message, ChatType.Whisper, channel);
|
||||
}
|
||||
|
||||
shout(message: string, channel?: number): Promise<void>
|
||||
{
|
||||
return this.nearbyChat(message, ChatType.Shout, channel);
|
||||
}
|
||||
|
||||
startTypingLocal(): Promise<void>
|
||||
{
|
||||
const cfv = new ChatFromViewerMessage();
|
||||
cfv.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
cfv.ChatData = {
|
||||
Message: Buffer.allocUnsafe(0),
|
||||
Type: ChatType.StartTyping,
|
||||
Channel: 0
|
||||
};
|
||||
const sequenceNo = this.circuit.sendMessage(cfv, PacketFlags.Reliable);
|
||||
return this.circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
stopTypingLocal(): Promise<void>
|
||||
{
|
||||
const cfv = new ChatFromViewerMessage();
|
||||
cfv.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID
|
||||
};
|
||||
cfv.ChatData = {
|
||||
Message: Buffer.allocUnsafe(0),
|
||||
Type: ChatType.StopTyping,
|
||||
Channel: 0
|
||||
};
|
||||
const sequenceNo = this.circuit.sendMessage(cfv, PacketFlags.Reliable);
|
||||
return this.circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
startTypingIM(to: UUID | string): Promise<void>
|
||||
{
|
||||
if (typeof to === 'string')
|
||||
{
|
||||
to = new UUID(to);
|
||||
}
|
||||
const circuit = this.circuit;
|
||||
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
|
||||
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
|
||||
im.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID
|
||||
};
|
||||
im.MessageBlock = {
|
||||
FromGroup: false,
|
||||
ToAgentID: to,
|
||||
ParentEstateID: 0,
|
||||
RegionID: UUID.zero(),
|
||||
Position: Vector3.getZero(),
|
||||
Offline: 0,
|
||||
Dialog: InstantMessageDialog.StartTyping,
|
||||
ID: UUID.zero(),
|
||||
Timestamp: 0,
|
||||
FromAgentName: Utils.StringToBuffer(agentName),
|
||||
Message: Utils.StringToBuffer(''),
|
||||
BinaryBucket: Buffer.allocUnsafe(0)
|
||||
};
|
||||
im.EstateBlock = {
|
||||
EstateID: 0
|
||||
};
|
||||
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
|
||||
return circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
stopTypingIM(to: UUID | string): Promise<void>
|
||||
{
|
||||
if (typeof to === 'string')
|
||||
{
|
||||
to = new UUID(to);
|
||||
}
|
||||
const circuit = this.circuit;
|
||||
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
|
||||
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
|
||||
im.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID
|
||||
};
|
||||
im.MessageBlock = {
|
||||
FromGroup: false,
|
||||
ToAgentID: to,
|
||||
ParentEstateID: 0,
|
||||
RegionID: UUID.zero(),
|
||||
Position: Vector3.getZero(),
|
||||
Offline: 0,
|
||||
Dialog: InstantMessageDialog.StopTyping,
|
||||
ID: UUID.zero(),
|
||||
Timestamp: 0,
|
||||
FromAgentName: Utils.StringToBuffer(agentName),
|
||||
Message: Utils.StringToBuffer(''),
|
||||
BinaryBucket: Buffer.allocUnsafe(0)
|
||||
};
|
||||
im.EstateBlock = {
|
||||
EstateID: 0
|
||||
};
|
||||
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
|
||||
return circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
|
||||
typeInstantMessage(to: UUID | string, message: string, thinkingTime?: number, charactersPerSecond?: number): Promise<void>
|
||||
{
|
||||
return new Promise<void>((resolve, reject) =>
|
||||
{
|
||||
if (thinkingTime === undefined)
|
||||
{
|
||||
thinkingTime = 2000;
|
||||
}
|
||||
setTimeout(() =>
|
||||
{
|
||||
if (typeof to === 'string')
|
||||
{
|
||||
to = new UUID(to);
|
||||
}
|
||||
let typeTimer: Timer | null = null;
|
||||
this.startTypingIM(to).then(() =>
|
||||
{
|
||||
typeTimer = setInterval(() =>
|
||||
{
|
||||
this.startTypingIM(to).catch(() =>
|
||||
{
|
||||
// ignore
|
||||
});
|
||||
}, 5000);
|
||||
if (charactersPerSecond === undefined)
|
||||
{
|
||||
charactersPerSecond = 5;
|
||||
}
|
||||
|
||||
const timeToWait = (message.length / charactersPerSecond) * 1000;
|
||||
setTimeout(() =>
|
||||
{
|
||||
if (typeTimer !== null)
|
||||
{
|
||||
clearInterval(typeTimer);
|
||||
typeTimer = null;
|
||||
}
|
||||
this.stopTypingIM(to).then(() =>
|
||||
{
|
||||
this.sendInstantMessage(to, message).then(() =>
|
||||
{
|
||||
resolve();
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
}, timeToWait);
|
||||
}).catch((err) =>
|
||||
{
|
||||
if (typeTimer !== null)
|
||||
{
|
||||
clearInterval(typeTimer);
|
||||
typeTimer = null;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
}, thinkingTime);
|
||||
});
|
||||
}
|
||||
|
||||
typeLocalMessage(message: string, thinkingTime?: number, charactersPerSecond?: number): Promise<void>
|
||||
{
|
||||
return new Promise<void>((resolve, reject) =>
|
||||
{
|
||||
if (thinkingTime === undefined)
|
||||
{
|
||||
thinkingTime = 0;
|
||||
}
|
||||
setTimeout(() =>
|
||||
{
|
||||
this.startTypingLocal().then(() =>
|
||||
{
|
||||
this.bot.clientCommands.agent.startAnimations([new UUID('c541c47f-e0c0-058b-ad1a-d6ae3a4584d9')]).then(() =>
|
||||
{
|
||||
if (charactersPerSecond === undefined)
|
||||
{
|
||||
charactersPerSecond = 5;
|
||||
}
|
||||
|
||||
const timeToWait = (message.length / charactersPerSecond) * 1000;
|
||||
setTimeout(() =>
|
||||
{
|
||||
this.stopTypingLocal().then(() =>
|
||||
{
|
||||
this.bot.clientCommands.agent.stopAnimations([new UUID('c541c47f-e0c0-058b-ad1a-d6ae3a4584d9')]).then(() =>
|
||||
{
|
||||
this.say(message).then(() =>
|
||||
{
|
||||
resolve();
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
}, timeToWait);
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
}, thinkingTime);
|
||||
});
|
||||
}
|
||||
}
|
||||
141
lib/classes/commands/GridCommands.ts
Normal file
141
lib/classes/commands/GridCommands.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import {MapInfoReply} from '../../events/MapInfoReply';
|
||||
import {Packet} from '../Packet';
|
||||
import * as Long from 'long';
|
||||
import {RegionHandleRequestMessage} from '../messages/RegionHandleRequest';
|
||||
import {MapItemReplyMessage} from '../messages/MapItemReply';
|
||||
import {Message} from '../../enums/Message';
|
||||
import {MapBlockReplyMessage} from '../messages/MapBlockReply';
|
||||
import {MapBlockRequestMessage} from '../messages/MapBlockRequest';
|
||||
import {UUID} from '../UUID';
|
||||
import {MapItemRequestMessage} from '../messages/MapItemRequest';
|
||||
import {Utils} from '../Utils';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {GridItemType} from '../../enums/GridItemType';
|
||||
import {RegionIDAndHandleReplyMessage} from '../messages/RegionIDAndHandleReply';
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
export class GridCommands extends CommandsBase
|
||||
{
|
||||
getRegionHandle(regionID: UUID): Promise<Long>
|
||||
{
|
||||
return new Promise<Long>((resolve, reject) =>
|
||||
{
|
||||
const circuit = this.currentRegion.circuit;
|
||||
const msg: RegionHandleRequestMessage = new RegionHandleRequestMessage();
|
||||
msg.RequestBlock = {
|
||||
RegionID: regionID,
|
||||
};
|
||||
circuit.sendMessage(msg, PacketFlags.Reliable);
|
||||
circuit.waitForMessage(Message.RegionIDAndHandleReply, 10000, (packet: Packet) =>
|
||||
{
|
||||
const filterMsg = packet.message as RegionIDAndHandleReplyMessage;
|
||||
return (filterMsg.ReplyBlock.RegionID.toString() === regionID.toString());
|
||||
}).then((packet: Packet) =>
|
||||
{
|
||||
const responseMsg = packet.message as RegionIDAndHandleReplyMessage;
|
||||
resolve(responseMsg.ReplyBlock.RegionHandle);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRegionMapInfo(gridX: number, gridY: number): Promise<MapInfoReply>
|
||||
{
|
||||
return new Promise<MapInfoReply>((resolve, reject) =>
|
||||
{
|
||||
const circuit = this.currentRegion.circuit;
|
||||
const response = new MapInfoReply();
|
||||
const msg: MapBlockRequestMessage = new MapBlockRequestMessage();
|
||||
msg.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID,
|
||||
Flags: 65536,
|
||||
EstateID: 0,
|
||||
Godlike: true
|
||||
};
|
||||
msg.PositionData = {
|
||||
MinX: (gridX / 256),
|
||||
MaxX: (gridX / 256),
|
||||
MinY: (gridY / 256),
|
||||
MaxY: (gridY / 256)
|
||||
};
|
||||
circuit.sendMessage(msg, PacketFlags.Reliable);
|
||||
circuit.waitForMessage(Message.MapBlockReply, 10000, (packet: Packet) =>
|
||||
{
|
||||
const filterMsg = packet.message as MapBlockReplyMessage;
|
||||
let found = false;
|
||||
filterMsg.Data.forEach((data) =>
|
||||
{
|
||||
if (data.X === (gridX / 256) && data.Y === (gridY / 256))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}).then((packet: Packet) =>
|
||||
{
|
||||
const responseMsg = packet.message as MapBlockReplyMessage;
|
||||
responseMsg.Data.forEach((data) =>
|
||||
{
|
||||
if (data.X === (gridX / 256) && data.Y === (gridY / 256))
|
||||
{
|
||||
response.name = Utils.BufferToStringSimple(data.Name);
|
||||
response.accessFlags = data.Access;
|
||||
response.mapImage = data.MapImageID;
|
||||
}
|
||||
});
|
||||
|
||||
// Now get the region handle
|
||||
const regionHandle: Long = Utils.RegionCoordinatesToHandle(gridX, gridY);
|
||||
|
||||
const mi = new MapItemRequestMessage();
|
||||
mi.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID,
|
||||
Flags: 2,
|
||||
EstateID: 0,
|
||||
Godlike: false
|
||||
};
|
||||
mi.RequestData = {
|
||||
ItemType: GridItemType.AgentLocations,
|
||||
RegionHandle: regionHandle
|
||||
};
|
||||
circuit.sendMessage(mi, PacketFlags.Reliable);
|
||||
const minX = Math.floor(gridX / 256) * 256;
|
||||
const maxX = minX + 256;
|
||||
const minY = Math.floor(gridY / 256) * 256;
|
||||
const maxY = minY + 256;
|
||||
response.avatars = [];
|
||||
circuit.waitForMessage(Message.MapItemReply, 10000, (packet: Packet) =>
|
||||
{
|
||||
const filterMsg = packet.message as MapItemReplyMessage;
|
||||
let found = false;
|
||||
filterMsg.Data.forEach((data) =>
|
||||
{
|
||||
// Check if avatar is within our bounds
|
||||
if (data.X >= minX && data.X <= maxX && data.Y >= minY && data.Y <= maxY)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}).then((packet2: Packet) =>
|
||||
{
|
||||
const responseMsg2 = packet2.message as MapItemReplyMessage;
|
||||
responseMsg2.Data.forEach((data) =>
|
||||
{
|
||||
response.avatars.push({
|
||||
X: data.X,
|
||||
Y: data.Y
|
||||
});
|
||||
});
|
||||
resolve(response);
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
}).catch((err) =>
|
||||
{
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
44
lib/classes/commands/GroupCommands.ts
Normal file
44
lib/classes/commands/GroupCommands.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
import {UUID} from '../UUID';
|
||||
import {InstantMessageDialog} from '../../enums/InstantMessageDialog';
|
||||
import {Utils} from '../Utils';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {ImprovedInstantMessageMessage} from '../messages/ImprovedInstantMessage';
|
||||
import {Vector3} from '../Vector3';
|
||||
|
||||
export class GroupCommands extends CommandsBase
|
||||
{
|
||||
sendGroupNotice(group: UUID | string, subject: string, message: string)
|
||||
{
|
||||
if (typeof group === 'string')
|
||||
{
|
||||
group = new UUID(group);
|
||||
}
|
||||
const circuit = this.circuit;
|
||||
const agentName = this.agent.firstName + ' ' + this.agent.lastName;
|
||||
const im: ImprovedInstantMessageMessage = new ImprovedInstantMessageMessage();
|
||||
im.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID
|
||||
};
|
||||
im.MessageBlock = {
|
||||
FromGroup: false,
|
||||
ToAgentID: group,
|
||||
ParentEstateID: 0,
|
||||
RegionID: UUID.zero(),
|
||||
Position: Vector3.getZero(),
|
||||
Offline: 0,
|
||||
Dialog: InstantMessageDialog.GroupNotice,
|
||||
ID: UUID.zero(),
|
||||
Timestamp: 0,
|
||||
FromAgentName: Utils.StringToBuffer(agentName),
|
||||
Message: Utils.StringToBuffer(subject + '|' + message),
|
||||
BinaryBucket: Buffer.allocUnsafe(0)
|
||||
};
|
||||
im.EstateBlock = {
|
||||
EstateID: 0
|
||||
};
|
||||
const sequenceNo = circuit.sendMessage(im, PacketFlags.Reliable);
|
||||
return circuit.waitForAck(sequenceNo, 10000);
|
||||
}
|
||||
}
|
||||
50
lib/classes/commands/NetworkCommands.ts
Normal file
50
lib/classes/commands/NetworkCommands.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {AgentThrottleMessage} from '../messages/AgentThrottle';
|
||||
|
||||
export class NetworkCommands extends CommandsBase
|
||||
{
|
||||
private throttleGenCounter = 0;
|
||||
|
||||
setBandwidth(total: number)
|
||||
{
|
||||
const agentThrottle: AgentThrottleMessage = new AgentThrottleMessage();
|
||||
agentThrottle.AgentData = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: this.circuit.sessionID,
|
||||
CircuitCode: this.circuit.circuitCode
|
||||
};
|
||||
|
||||
const throttleData = Buffer.allocUnsafe(28);
|
||||
let pos = 0;
|
||||
|
||||
const resendThrottle = total * 0.1;
|
||||
const landThrottle = total * 0.172;
|
||||
const windThrottle = total * 0.05;
|
||||
const cloudThrottle = total * 0.05;
|
||||
const taskThrottle = total * 0.234;
|
||||
const textureThrottle = total * 0.234;
|
||||
const assetThrottle = total * 0.160;
|
||||
|
||||
|
||||
throttleData.writeFloatLE(resendThrottle, pos);
|
||||
pos += 4;
|
||||
throttleData.writeFloatLE(landThrottle, pos);
|
||||
pos += 4;
|
||||
throttleData.writeFloatLE(windThrottle, pos);
|
||||
pos += 4;
|
||||
throttleData.writeFloatLE(cloudThrottle, pos);
|
||||
pos += 4;
|
||||
throttleData.writeFloatLE(taskThrottle, pos);
|
||||
pos += 4;
|
||||
throttleData.writeFloatLE(textureThrottle, pos);
|
||||
pos += 4;
|
||||
throttleData.writeFloatLE(assetThrottle, pos);
|
||||
|
||||
agentThrottle.Throttle = {
|
||||
GenCounter: this.throttleGenCounter++,
|
||||
Throttles: throttleData
|
||||
};
|
||||
this.circuit.sendMessage(agentThrottle, PacketFlags.Reliable);
|
||||
}
|
||||
}
|
||||
6
lib/classes/commands/RegionCommands.ts
Normal file
6
lib/classes/commands/RegionCommands.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
|
||||
export class RegionCommands extends CommandsBase
|
||||
{
|
||||
|
||||
}
|
||||
82
lib/classes/commands/TeleportCommands.ts
Normal file
82
lib/classes/commands/TeleportCommands.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import {CommandsBase} from './CommandsBase';
|
||||
import {Region} from '../Region';
|
||||
import {LureEvent} from '../../events/LureEvent';
|
||||
import {TeleportEventType} from '../../enums/TeleportEventType';
|
||||
import {TeleportEvent} from '../../events/TeleportEvent';
|
||||
import {PacketFlags} from '../../enums/PacketFlags';
|
||||
import {TeleportLureRequestMessage} from '../messages/TeleportLureRequest';
|
||||
import {TeleportFlags} from '../../enums/TeleportFlags';
|
||||
|
||||
export class TeleportCommands extends CommandsBase
|
||||
{
|
||||
acceptTeleport(lure: LureEvent): Promise<TeleportEvent>
|
||||
{
|
||||
return new Promise<TeleportEvent>((resolve, reject) =>
|
||||
{
|
||||
const circuit = this.currentRegion.circuit;
|
||||
const tlr = new TeleportLureRequestMessage();
|
||||
tlr.Info = {
|
||||
AgentID: this.agent.agentID,
|
||||
SessionID: circuit.sessionID,
|
||||
LureID: lure.lureID,
|
||||
TeleportFlags: TeleportFlags.ViaLure
|
||||
};
|
||||
circuit.sendMessage(tlr, PacketFlags.Reliable);
|
||||
if (this.currentRegion.caps.eventQueueClient)
|
||||
{
|
||||
if (this.bot.clientEvents === null)
|
||||
{
|
||||
reject(new Error('ClientEvents is null'));
|
||||
return;
|
||||
}
|
||||
const subscription = this.bot.clientEvents.onTeleportEvent.subscribe((e: TeleportEvent) =>
|
||||
{
|
||||
if (e.eventType === TeleportEventType.TeleportFailed || e.eventType === TeleportEventType.TeleportCompleted)
|
||||
{
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
if (e.eventType === TeleportEventType.TeleportFailed)
|
||||
{
|
||||
reject(e);
|
||||
}
|
||||
else if (e.eventType === TeleportEventType.TeleportCompleted)
|
||||
{
|
||||
if (e.simIP === 'local')
|
||||
{
|
||||
// Local TP - no need for any other shindiggery
|
||||
resolve(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bot.clientEvents === null)
|
||||
{
|
||||
reject(new Error('ClientEvents is null'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Successful teleport! First, rip apart circuit
|
||||
this.currentRegion.shutdown();
|
||||
const region: Region = new Region(this.agent, this.bot.clientEvents);
|
||||
region.circuit.circuitCode = this.currentRegion.circuit.circuitCode;
|
||||
region.circuit.secureSessionID = this.currentRegion.circuit.secureSessionID;
|
||||
region.circuit.sessionID = this.currentRegion.circuit.sessionID;
|
||||
region.circuit.udpBlacklist = this.currentRegion.circuit.udpBlacklist;
|
||||
region.circuit.ipAddress = e.simIP;
|
||||
region.circuit.port = e.simPort;
|
||||
this.agent.setCurrentRegion(region);
|
||||
this.currentRegion = region;
|
||||
this.currentRegion.activateCaps(e.seedCapability);
|
||||
|
||||
this.bot.changeRegion(this.currentRegion).then(() =>
|
||||
{
|
||||
resolve(e);
|
||||
}).catch((error) =>
|
||||
{
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user