2017-11-21 15:09:26 +00:00
|
|
|
import {LoginHandler} from './LoginHandler';
|
2017-11-24 01:00:56 +00:00
|
|
|
import {LoginResponse} from './classes/LoginResponse';
|
|
|
|
|
import {LoginParameters} from './classes/LoginParameters';
|
2017-11-26 19:47:41 +00:00
|
|
|
import {Agent} from './classes/Agent';
|
|
|
|
|
import {PacketFlags} from './enums/PacketFlags';
|
|
|
|
|
import {UseCircuitCodeMessage} from './classes/messages/UseCircuitCode';
|
|
|
|
|
import {CompleteAgentMovementMessage} from './classes/messages/CompleteAgentMovement';
|
|
|
|
|
import {Message} from './enums/Message';
|
|
|
|
|
import {Packet} from './classes/Packet';
|
|
|
|
|
import {Region} from './classes/Region';
|
|
|
|
|
import {LogoutRequestMessage} from './classes/messages/LogoutRequest';
|
|
|
|
|
import {Utils} from './classes/Utils';
|
2017-11-30 04:11:59 +00:00
|
|
|
import {RegionHandshakeReplyMessage} from './classes/messages/RegionHandshakeReply';
|
|
|
|
|
import {RegionProtocolFlags} from './enums/RegionProtocolFlags';
|
|
|
|
|
import {AgentDataUpdateRequestMessage} from './classes/messages/AgentDataUpdateRequest';
|
|
|
|
|
import {TeleportProgressMessage} from './classes/messages/TeleportProgress';
|
|
|
|
|
import {TeleportStartMessage} from './classes/messages/TeleportStart';
|
|
|
|
|
import {TeleportEvent} from './events/TeleportEvent';
|
|
|
|
|
import {ClientEvents} from './classes/ClientEvents';
|
|
|
|
|
import {TeleportEventType} from './enums/TeleportEventType';
|
2017-12-13 19:55:08 +00:00
|
|
|
import {ClientCommands} from './classes/ClientCommands';
|
|
|
|
|
import {DisconnectEvent} from './events/DisconnectEvent';
|
|
|
|
|
import {KickUserMessage} from './classes/messages/KickUser';
|
|
|
|
|
import {StartPingCheckMessage} from './classes/messages/StartPingCheck';
|
|
|
|
|
import {CompletePingCheckMessage} from './classes/messages/CompletePingCheck';
|
|
|
|
|
import Timer = NodeJS.Timer;
|
2017-12-14 01:21:18 +00:00
|
|
|
import {Subscription} from 'rxjs/Subscription';
|
2017-12-14 02:06:28 +00:00
|
|
|
import {BotOptionFlags} from './enums/BotOptionFlags';
|
2017-12-15 21:28:45 +00:00
|
|
|
import {FilterResponse} from './enums/FilterResponse';
|
2017-11-21 15:09:26 +00:00
|
|
|
|
|
|
|
|
export class Bot
|
|
|
|
|
{
|
2017-11-30 04:11:59 +00:00
|
|
|
private loginParams: LoginParameters;
|
|
|
|
|
private currentRegion: Region;
|
|
|
|
|
private agent: Agent;
|
2017-12-13 19:55:08 +00:00
|
|
|
private ping: Timer | null = null;
|
|
|
|
|
private pingNumber = 0;
|
|
|
|
|
private lastSuccessfulPing = 0;
|
2017-12-14 02:37:47 +00:00
|
|
|
private circuitSubscription: Subscription | null = null;
|
2017-12-14 02:06:28 +00:00
|
|
|
private options: BotOptionFlags;
|
2017-12-14 01:21:18 +00:00
|
|
|
public clientEvents: ClientEvents;
|
2017-12-13 19:55:08 +00:00
|
|
|
public clientCommands: ClientCommands;
|
2017-11-21 15:09:26 +00:00
|
|
|
|
2017-12-14 02:06:28 +00:00
|
|
|
|
|
|
|
|
constructor(login: LoginParameters, options: BotOptionFlags)
|
2017-11-21 15:09:26 +00:00
|
|
|
{
|
2017-12-14 01:21:18 +00:00
|
|
|
this.clientEvents = new ClientEvents();
|
2017-11-21 15:09:26 +00:00
|
|
|
this.loginParams = login;
|
2017-12-14 02:06:28 +00:00
|
|
|
this.options = options;
|
2017-11-21 15:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-26 19:47:41 +00:00
|
|
|
login()
|
2017-11-21 15:09:26 +00:00
|
|
|
{
|
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
|
{
|
2017-12-14 02:06:28 +00:00
|
|
|
const loginHandler = new LoginHandler(this.clientEvents, this.options);
|
2017-11-21 15:09:26 +00:00
|
|
|
loginHandler.Login(this.loginParams).then((response: LoginResponse) =>
|
|
|
|
|
{
|
2017-11-26 19:47:41 +00:00
|
|
|
this.currentRegion = response.region;
|
|
|
|
|
this.agent = response.agent;
|
2017-12-13 19:55:08 +00:00
|
|
|
this.clientCommands = new ClientCommands(response.region, response.agent, this);
|
2017-11-21 15:09:26 +00:00
|
|
|
resolve(response);
|
|
|
|
|
}).catch((error: Error) =>
|
|
|
|
|
{
|
|
|
|
|
reject(error);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-11-26 19:47:41 +00:00
|
|
|
|
2017-12-13 19:55:08 +00:00
|
|
|
changeRegion(region: Region)
|
|
|
|
|
{
|
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
|
{
|
|
|
|
|
this.currentRegion = region;
|
|
|
|
|
this.clientCommands = new ClientCommands(this.currentRegion, this.agent, this);
|
2017-12-14 01:21:18 +00:00
|
|
|
if (this.ping !== null)
|
|
|
|
|
{
|
|
|
|
|
clearInterval(this.ping);
|
|
|
|
|
this.ping = null;
|
|
|
|
|
}
|
2017-12-13 19:55:08 +00:00
|
|
|
this.connectToSim().then(() =>
|
|
|
|
|
{
|
|
|
|
|
resolve();
|
|
|
|
|
}).catch((error) =>
|
|
|
|
|
{
|
|
|
|
|
reject(error);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-26 19:47:41 +00:00
|
|
|
close()
|
|
|
|
|
{
|
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
|
{
|
|
|
|
|
const circuit = this.currentRegion.circuit;
|
|
|
|
|
const msg: LogoutRequestMessage = new LogoutRequestMessage();
|
|
|
|
|
msg.AgentData = {
|
|
|
|
|
AgentID: this.agent.agentID,
|
|
|
|
|
SessionID: circuit.sessionID
|
|
|
|
|
};
|
|
|
|
|
circuit.sendMessage(msg, PacketFlags.Reliable);
|
2018-10-06 16:03:10 +01:00
|
|
|
circuit.waitForPacket(Message.LogoutReply, 5000).then((packet: Packet) =>
|
2017-11-26 19:47:41 +00:00
|
|
|
{
|
|
|
|
|
|
2018-10-06 16:03:10 +01:00
|
|
|
}).catch((error: Error) =>
|
2017-11-26 19:47:41 +00:00
|
|
|
{
|
|
|
|
|
console.error('Timeout waiting for logout reply')
|
|
|
|
|
}).then(() =>
|
|
|
|
|
{
|
2017-11-30 04:11:59 +00:00
|
|
|
this.agent.shutdown();
|
|
|
|
|
this.currentRegion.shutdown();
|
2017-12-14 01:21:18 +00:00
|
|
|
if (this.circuitSubscription !== null)
|
|
|
|
|
{
|
|
|
|
|
this.circuitSubscription.unsubscribe();
|
|
|
|
|
this.circuitSubscription = null;
|
|
|
|
|
}
|
2017-11-30 04:11:59 +00:00
|
|
|
delete this.currentRegion;
|
|
|
|
|
delete this.agent;
|
2017-12-13 19:55:08 +00:00
|
|
|
delete this.clientCommands;
|
|
|
|
|
if (this.ping !== null)
|
2017-11-30 04:11:59 +00:00
|
|
|
{
|
2017-12-13 19:55:08 +00:00
|
|
|
clearInterval(this.ping);
|
|
|
|
|
this.ping = null;
|
2017-11-30 04:11:59 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-13 19:55:08 +00:00
|
|
|
const disconnectEvent = new DisconnectEvent();
|
|
|
|
|
disconnectEvent.requested = true;
|
|
|
|
|
disconnectEvent.message = 'Logout completed';
|
|
|
|
|
if (this.clientEvents)
|
2017-11-30 04:11:59 +00:00
|
|
|
{
|
2017-12-13 19:55:08 +00:00
|
|
|
this.clientEvents.onDisconnected.next(disconnectEvent);
|
|
|
|
|
}
|
|
|
|
|
resolve();
|
2017-11-30 04:11:59 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-26 19:47:41 +00:00
|
|
|
connectToSim()
|
|
|
|
|
{
|
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
|
{
|
|
|
|
|
const circuit = this.currentRegion.circuit;
|
|
|
|
|
circuit.init();
|
|
|
|
|
const msg: UseCircuitCodeMessage = new UseCircuitCodeMessage();
|
|
|
|
|
msg.CircuitCode = {
|
|
|
|
|
SessionID: circuit.sessionID,
|
|
|
|
|
ID: this.agent.agentID,
|
|
|
|
|
Code: circuit.circuitCode
|
|
|
|
|
};
|
|
|
|
|
circuit.waitForAck(circuit.sendMessage(msg, PacketFlags.Reliable), 1000).then(() =>
|
|
|
|
|
{
|
|
|
|
|
const agentMovement: CompleteAgentMovementMessage = new CompleteAgentMovementMessage();
|
|
|
|
|
agentMovement.AgentData = {
|
|
|
|
|
AgentID: this.agent.agentID,
|
|
|
|
|
SessionID: circuit.sessionID,
|
|
|
|
|
CircuitCode: circuit.circuitCode
|
|
|
|
|
};
|
|
|
|
|
circuit.sendMessage(agentMovement, PacketFlags.Reliable);
|
2018-10-06 16:03:10 +01:00
|
|
|
return circuit.waitForPacket(Message.RegionHandshake, 10000);
|
2017-11-26 19:47:41 +00:00
|
|
|
}).then((packet: Packet) =>
|
|
|
|
|
{
|
2017-11-30 04:11:59 +00:00
|
|
|
const handshakeReply: RegionHandshakeReplyMessage = new RegionHandshakeReplyMessage();
|
|
|
|
|
handshakeReply.AgentData = {
|
|
|
|
|
AgentID: this.agent.agentID,
|
|
|
|
|
SessionID: circuit.sessionID
|
|
|
|
|
};
|
|
|
|
|
handshakeReply.RegionInfo = {
|
|
|
|
|
Flags: RegionProtocolFlags.SelfAppearanceSupport | RegionProtocolFlags.AgentAppearanceService
|
|
|
|
|
};
|
|
|
|
|
return circuit.waitForAck(circuit.sendMessage(handshakeReply, PacketFlags.Reliable), 10000)
|
|
|
|
|
}).then(() =>
|
|
|
|
|
{
|
2017-12-13 19:55:08 +00:00
|
|
|
if (this.clientCommands !== null)
|
|
|
|
|
{
|
|
|
|
|
this.clientCommands.network.setBandwidth(1536000);
|
|
|
|
|
}
|
2017-11-30 04:11:59 +00:00
|
|
|
|
|
|
|
|
const agentRequest = new AgentDataUpdateRequestMessage();
|
|
|
|
|
agentRequest.AgentData = {
|
|
|
|
|
AgentID: this.agent.agentID,
|
|
|
|
|
SessionID: circuit.sessionID
|
|
|
|
|
};
|
|
|
|
|
circuit.sendMessage(agentRequest, PacketFlags.Reliable);
|
|
|
|
|
this.agent.setInitialAppearance();
|
|
|
|
|
this.agent.circuitActive();
|
|
|
|
|
|
2017-12-13 19:55:08 +00:00
|
|
|
this.lastSuccessfulPing = new Date().getTime();
|
|
|
|
|
this.ping = setInterval(() =>
|
|
|
|
|
{
|
|
|
|
|
this.pingNumber++;
|
|
|
|
|
if (this.pingNumber > 255)
|
|
|
|
|
{
|
|
|
|
|
this.pingNumber = 0;
|
|
|
|
|
}
|
|
|
|
|
const ping = new StartPingCheckMessage();
|
|
|
|
|
ping.PingID = {
|
|
|
|
|
PingID: this.pingNumber,
|
|
|
|
|
OldestUnacked: this.currentRegion.circuit.getOldestUnacked()
|
|
|
|
|
};
|
|
|
|
|
circuit.sendMessage(ping, PacketFlags.Reliable);
|
2018-10-06 16:03:10 +01:00
|
|
|
circuit.waitForPacket(Message.CompletePingCheck, 10000, ((pingData: {
|
2017-12-13 19:55:08 +00:00
|
|
|
pingID: number,
|
|
|
|
|
timeSent: number
|
2017-12-15 21:28:45 +00:00
|
|
|
}, packet: Packet): FilterResponse =>
|
2017-12-13 19:55:08 +00:00
|
|
|
{
|
|
|
|
|
const cpc = packet.message as CompletePingCheckMessage;
|
|
|
|
|
if (cpc.PingID.PingID === pingData.pingID)
|
|
|
|
|
{
|
|
|
|
|
this.lastSuccessfulPing = new Date().getTime();
|
|
|
|
|
const pingTime = this.lastSuccessfulPing - pingData.timeSent;
|
|
|
|
|
if (this.clientEvents !== null)
|
|
|
|
|
{
|
|
|
|
|
this.clientEvents.onCircuitLatency.next(pingTime);
|
|
|
|
|
}
|
2017-12-15 21:28:45 +00:00
|
|
|
return FilterResponse.Finish;
|
2017-12-13 19:55:08 +00:00
|
|
|
}
|
2017-12-15 21:28:45 +00:00
|
|
|
return FilterResponse.NoMatch;
|
2017-12-13 19:55:08 +00:00
|
|
|
}).bind(this, {
|
|
|
|
|
pingID: this.pingNumber,
|
|
|
|
|
timeSent: new Date().getTime()
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if ((new Date().getTime() - this.lastSuccessfulPing) > 60000)
|
|
|
|
|
{
|
|
|
|
|
// We're dead, jim
|
|
|
|
|
this.agent.shutdown();
|
|
|
|
|
this.currentRegion.shutdown();
|
2017-12-14 01:21:18 +00:00
|
|
|
if (this.circuitSubscription !== null)
|
|
|
|
|
{
|
|
|
|
|
this.circuitSubscription.unsubscribe();
|
|
|
|
|
this.circuitSubscription = null;
|
|
|
|
|
}
|
2017-12-13 19:55:08 +00:00
|
|
|
delete this.currentRegion;
|
|
|
|
|
delete this.agent;
|
|
|
|
|
delete this.clientCommands;
|
|
|
|
|
if (this.ping !== null)
|
|
|
|
|
{
|
|
|
|
|
clearInterval(this.ping);
|
|
|
|
|
this.ping = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const disconnectEvent = new DisconnectEvent();
|
|
|
|
|
disconnectEvent.requested = false;
|
|
|
|
|
disconnectEvent.message = 'Circuit timeout';
|
|
|
|
|
if (this.clientEvents)
|
|
|
|
|
{
|
|
|
|
|
this.clientEvents.onDisconnected.next(disconnectEvent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}, 5000);
|
|
|
|
|
|
2017-12-14 01:21:18 +00:00
|
|
|
this.circuitSubscription = circuit.subscribeToMessages(
|
2017-11-30 04:11:59 +00:00
|
|
|
[
|
|
|
|
|
Message.TeleportFailed,
|
|
|
|
|
Message.TeleportFinish,
|
|
|
|
|
Message.TeleportLocal,
|
|
|
|
|
Message.TeleportStart,
|
|
|
|
|
Message.TeleportProgress,
|
|
|
|
|
Message.TeleportCancel,
|
2017-12-13 19:55:08 +00:00
|
|
|
Message.KickUser
|
2017-11-30 04:11:59 +00:00
|
|
|
], (packet: Packet) =>
|
|
|
|
|
{
|
|
|
|
|
switch (packet.message.id)
|
|
|
|
|
{
|
|
|
|
|
case Message.TeleportLocal:
|
|
|
|
|
{
|
|
|
|
|
const tpEvent = new TeleportEvent();
|
|
|
|
|
tpEvent.message = '';
|
|
|
|
|
tpEvent.eventType = TeleportEventType.TeleportCompleted;
|
|
|
|
|
tpEvent.simIP = 'local';
|
|
|
|
|
tpEvent.simPort = 0;
|
|
|
|
|
tpEvent.seedCapability = '';
|
|
|
|
|
|
|
|
|
|
if (this.clientEvents === null)
|
|
|
|
|
{
|
|
|
|
|
reject(new Error('ClientEvents is null'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.clientEvents.onTeleportEvent.next(tpEvent);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Message.TeleportStart:
|
|
|
|
|
{
|
|
|
|
|
const teleportStart = packet.message as TeleportStartMessage;
|
|
|
|
|
|
|
|
|
|
const tpEvent = new TeleportEvent();
|
|
|
|
|
tpEvent.message = '';
|
|
|
|
|
tpEvent.eventType = TeleportEventType.TeleportStarted;
|
|
|
|
|
tpEvent.simIP = '';
|
|
|
|
|
tpEvent.simPort = 0;
|
|
|
|
|
tpEvent.seedCapability = '';
|
|
|
|
|
|
|
|
|
|
if (this.clientEvents === null)
|
|
|
|
|
{
|
|
|
|
|
reject(new Error('ClientEvents is null'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.clientEvents.onTeleportEvent.next(tpEvent);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Message.TeleportProgress:
|
|
|
|
|
{
|
|
|
|
|
const teleportProgress = packet.message as TeleportProgressMessage;
|
|
|
|
|
const message = Utils.BufferToStringSimple(teleportProgress.Info.Message);
|
|
|
|
|
|
|
|
|
|
const tpEvent = new TeleportEvent();
|
|
|
|
|
tpEvent.message = message;
|
|
|
|
|
tpEvent.eventType = TeleportEventType.TeleportProgress;
|
|
|
|
|
tpEvent.simIP = '';
|
|
|
|
|
tpEvent.simPort = 0;
|
|
|
|
|
tpEvent.seedCapability = '';
|
|
|
|
|
|
|
|
|
|
if (this.clientEvents === null)
|
|
|
|
|
{
|
|
|
|
|
reject(new Error('ClientEvents is null'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.clientEvents.onTeleportEvent.next(tpEvent);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-13 19:55:08 +00:00
|
|
|
case Message.KickUser:
|
2017-11-30 04:11:59 +00:00
|
|
|
{
|
2017-12-13 19:55:08 +00:00
|
|
|
const kickUser = packet.message as KickUserMessage;
|
|
|
|
|
this.agent.shutdown();
|
|
|
|
|
this.currentRegion.shutdown();
|
2017-12-14 01:21:18 +00:00
|
|
|
if (this.circuitSubscription !== null)
|
|
|
|
|
{
|
|
|
|
|
this.circuitSubscription.unsubscribe();
|
|
|
|
|
this.circuitSubscription = null;
|
|
|
|
|
}
|
2017-12-13 19:55:08 +00:00
|
|
|
delete this.currentRegion;
|
|
|
|
|
delete this.agent;
|
|
|
|
|
delete this.clientCommands;
|
|
|
|
|
if (this.ping !== null)
|
|
|
|
|
{
|
|
|
|
|
clearInterval(this.ping);
|
|
|
|
|
this.ping = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const disconnectEvent = new DisconnectEvent();
|
|
|
|
|
disconnectEvent.requested = false;
|
|
|
|
|
disconnectEvent.message = Utils.BufferToStringSimple(kickUser.UserInfo.Reason);
|
|
|
|
|
if (this.clientEvents)
|
|
|
|
|
{
|
|
|
|
|
this.clientEvents.onDisconnected.next(disconnectEvent);
|
|
|
|
|
}
|
2017-11-30 04:11:59 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2017-11-26 19:47:41 +00:00
|
|
|
resolve();
|
|
|
|
|
}).catch((error) =>
|
|
|
|
|
{
|
|
|
|
|
reject(error);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-11-21 15:09:26 +00:00
|
|
|
}
|