Handle disconnects gracefully! Handle KillObject to prevent perpetually inflating object store. Unsubscribe from rxjs subscription in bot.ts on disconnect. Stop ping timer. Don't re-create clientEvents on each login.

This commit is contained in:
Casper Warden
2017-12-14 01:21:18 +00:00
parent 4e8feb181f
commit c9831ab427
20 changed files with 223 additions and 82 deletions

3
dist/Bot.d.ts vendored
View File

@@ -9,7 +9,8 @@ export declare class Bot {
private ping;
private pingNumber;
private lastSuccessfulPing;
clientEvents: ClientEvents | null;
private circuitSubscription;
clientEvents: ClientEvents;
clientCommands: ClientCommands;
constructor(login: LoginParameters);
login(): Promise<{}>;

25
dist/Bot.js vendored
View File

@@ -11,6 +11,7 @@ const RegionHandshakeReply_1 = require("./classes/messages/RegionHandshakeReply"
const RegionProtocolFlags_1 = require("./enums/RegionProtocolFlags");
const AgentDataUpdateRequest_1 = require("./classes/messages/AgentDataUpdateRequest");
const TeleportEvent_1 = require("./events/TeleportEvent");
const ClientEvents_1 = require("./classes/ClientEvents");
const TeleportEventType_1 = require("./enums/TeleportEventType");
const ClientCommands_1 = require("./classes/ClientCommands");
const DisconnectEvent_1 = require("./events/DisconnectEvent");
@@ -20,14 +21,14 @@ class Bot {
this.ping = null;
this.pingNumber = 0;
this.lastSuccessfulPing = 0;
this.clientEvents = null;
this.circuitSubscription = null;
this.clientEvents = new ClientEvents_1.ClientEvents();
this.loginParams = login;
}
login() {
return new Promise((resolve, reject) => {
const loginHandler = new LoginHandler_1.LoginHandler();
const loginHandler = new LoginHandler_1.LoginHandler(this.clientEvents);
loginHandler.Login(this.loginParams).then((response) => {
this.clientEvents = response.clientEvents;
this.currentRegion = response.region;
this.agent = response.agent;
this.clientCommands = new ClientCommands_1.ClientCommands(response.region, response.agent, this);
@@ -41,6 +42,10 @@ class Bot {
return new Promise((resolve, reject) => {
this.currentRegion = region;
this.clientCommands = new ClientCommands_1.ClientCommands(this.currentRegion, this.agent, this);
if (this.ping !== null) {
clearInterval(this.ping);
this.ping = null;
}
this.connectToSim().then(() => {
resolve();
}).catch((error) => {
@@ -63,6 +68,10 @@ class Bot {
}).then(() => {
this.agent.shutdown();
this.currentRegion.shutdown();
if (this.circuitSubscription !== null) {
this.circuitSubscription.unsubscribe();
this.circuitSubscription = null;
}
delete this.currentRegion;
delete this.agent;
delete this.clientCommands;
@@ -151,6 +160,10 @@ class Bot {
if ((new Date().getTime() - this.lastSuccessfulPing) > 60000) {
this.agent.shutdown();
this.currentRegion.shutdown();
if (this.circuitSubscription !== null) {
this.circuitSubscription.unsubscribe();
this.circuitSubscription = null;
}
delete this.currentRegion;
delete this.agent;
delete this.clientCommands;
@@ -166,7 +179,7 @@ class Bot {
}
}
}, 5000);
circuit.subscribeToMessages([
this.circuitSubscription = circuit.subscribeToMessages([
Message_1.Message.TeleportFailed,
Message_1.Message.TeleportFinish,
Message_1.Message.TeleportLocal,
@@ -229,6 +242,10 @@ class Bot {
const kickUser = packet.message;
this.agent.shutdown();
this.currentRegion.shutdown();
if (this.circuitSubscription !== null) {
this.circuitSubscription.unsubscribe();
this.circuitSubscription = null;
}
delete this.currentRegion;
delete this.agent;
delete this.clientCommands;

2
dist/Bot.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,9 @@
import { LoginParameters } from './classes/LoginParameters';
import { LoginResponse } from './classes/LoginResponse';
import { ClientEvents } from './classes/ClientEvents';
export declare class LoginHandler {
private clientEvents;
static GenerateMAC(): string;
constructor(ce: ClientEvents);
Login(params: LoginParameters): Promise<LoginResponse>;
}

View File

@@ -17,6 +17,9 @@ class LoginHandler {
}
return macAddress;
}
constructor(ce) {
this.clientEvents = ce;
}
Login(params) {
return new Promise((resolve, reject) => {
const secureClientOptions = {
@@ -30,7 +33,7 @@ class LoginHandler {
{
'first': params.firstName,
'last': params.lastName,
'passwd': '$1$' + crypto.createHash('md5').update(params.password).digest('hex'),
'passwd': '$1$' + crypto.createHash('md5').update(params.password.substr(0, 16)).digest('hex'),
'start': 'home',
'major': '0',
'minor': '0',
@@ -57,7 +60,7 @@ class LoginHandler {
'global-textures'
]
}
], function (error, value) {
], (error, value) => {
if (error) {
reject(error);
}
@@ -66,7 +69,7 @@ class LoginHandler {
reject(new Error(value['message']));
}
else {
const response = new LoginResponse_1.LoginResponse(value);
const response = new LoginResponse_1.LoginResponse(value, this.clientEvents);
resolve(response);
}
}

View File

@@ -1 +1 @@
{"version":3,"file":"LoginHandler.js","sourceRoot":"","sources":["../lib/LoginHandler.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"LoginHandler.js","sourceRoot":"","sources":["../lib/LoginHandler.ts"],"names":[],"mappings":"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"}

View File

@@ -24,7 +24,6 @@ class Comms {
switch (im.MessageBlock.Dialog) {
case InstantMessageDialog_1.InstantMessageDialog.MessageFromAgent:
{
console.log(im);
const imEvent = new InstantMessageEvent_1.InstantMessageEvent();
imEvent.source = ChatSourceType_1.ChatSourceType.Agent;
imEvent.from = im.AgentData.AgentID;
@@ -53,7 +52,6 @@ class Comms {
break;
case InstantMessageDialog_1.InstantMessageDialog.MessageFromObject:
{
console.log(im);
const imEvent = new InstantMessageEvent_1.InstantMessageEvent();
imEvent.source = ChatSourceType_1.ChatSourceType.Object;
imEvent.owner = im.AgentData.AgentID;

File diff suppressed because one or more lines are too long

View File

@@ -30,5 +30,5 @@ export declare class LoginResponse {
private static toRegionHandle(x_global, y_global);
private static parseVector3(str);
private static parseHome(str);
constructor(json: any);
constructor(json: any, ce: ClientEvents);
}

View File

@@ -6,9 +6,8 @@ const Region_1 = require("./Region");
const LoginFlags_1 = require("../enums/LoginFlags");
const Vector3_1 = require("./Vector3");
const Long = require("long");
const ClientEvents_1 = require("./ClientEvents");
class LoginResponse {
constructor(json) {
constructor(json, ce) {
this.events = {
categories: []
};
@@ -16,7 +15,7 @@ class LoginResponse {
categories: []
};
this.textures = {};
this.clientEvents = new ClientEvents_1.ClientEvents();
this.clientEvents = ce;
this.agent = new Agent_1.Agent(this.clientEvents);
this.region = new Region_1.Region(this.agent, this.clientEvents);
Object.keys(json).forEach((key) => {

File diff suppressed because one or more lines are too long

View File

@@ -2,8 +2,8 @@
import { Circuit } from './Circuit';
import { Agent } from './Agent';
import { GameObject } from './Object';
import { NameValue } from "./NameValue";
import { ClientEvents } from "./ClientEvents";
import { NameValue } from './NameValue';
import { ClientEvents } from './ClientEvents';
export declare class ObjectStore {
private circuit;
private agent;
@@ -12,6 +12,7 @@ export declare class ObjectStore {
private objectsByParent;
private clientEvents;
constructor(circuit: Circuit, agent: Agent, clientEvents: ClientEvents);
deleteObject(objectID: number): void;
readExtraParams(buf: Buffer, pos: number, o: GameObject): number;
getObjectsByParent(parentID: number): GameObject[];
parseNameValues(str: string): {

View File

@@ -23,7 +23,8 @@ class ObjectStore {
Message_1.Message.ObjectUpdateCached,
Message_1.Message.ObjectUpdateCompressed,
Message_1.Message.ImprovedTerseObjectUpdate,
Message_1.Message.MultipleObjectUpdate
Message_1.Message.MultipleObjectUpdate,
Message_1.Message.KillObject
], (packet) => {
switch (packet.message.id) {
case Message_1.Message.ObjectUpdate:
@@ -269,9 +270,39 @@ class ObjectStore {
const multipleObjectUpdate = packet.message;
console.error('TODO: MultipleObjectUpdate');
break;
case Message_1.Message.KillObject:
const killObj = packet.message;
killObj.ObjectData.forEach((obj) => {
const objectID = obj.ID;
this.deleteObject(objectID);
});
break;
}
});
}
deleteObject(objectID) {
if (this.objects[objectID]) {
if (this.objectsByParent[objectID]) {
this.objectsByParent[objectID].forEach((childObjID) => {
this.deleteObject(childObjID);
});
}
delete this.objectsByParent[objectID];
const objct = this.objects[objectID];
const uuid = objct.FullID.toString();
if (this.objectsByUUID[uuid]) {
delete this.objectsByUUID[uuid];
}
const parentID = objct.ParentID;
if (this.objectsByParent[parentID]) {
const ind = this.objectsByParent[parentID].indexOf(objectID);
if (ind !== -1) {
this.objectsByParent[parentID].splice(ind, 1);
}
}
delete this.objects[objectID];
}
}
readExtraParams(buf, pos, o) {
if (pos >= buf.length) {
return 0;

File diff suppressed because one or more lines are too long

View File

@@ -13,42 +13,54 @@ const bot = new nmv.Bot(loginParameters);
let resp = null;
const master = 'd1cd5b71-6209-4595-9bf0-771bf689ce00';
bot.clientEvents.onLure.subscribe((lureEvent) =>
{
bot.clientCommands.grid.getRegionMapInfo(lureEvent.gridX, lureEvent.gridY).then((regionInfo) =>
{
if (lureEvent.from.toString() === master)
{
console.log('Accepting teleport lure to ' + regionInfo.name + ' (' + regionInfo.avatars.length + ' avatar' + ((regionInfo.avatars.length === 1)?'':'s') + ' present) from ' + lureEvent.fromName + ' with message: ' + lureEvent.lureMessage);
bot.clientCommands.teleport.acceptTeleport(lureEvent);
}
else
{
console.log('Ignoring teleport lure to ' + regionInfo.name + ' (' + regionInfo.avatars.length + ' avatar' + ((regionInfo.avatars.length === 1)?'':'s') + ' present) from ' + lureEvent.fromName + ' with message: ' + lureEvent.lureMessage);
}
});
});
bot.clientEvents.onInstantMessage.subscribe((IMEvent) =>
{
if (IMEvent.source === nmv.ChatSourceType.Agent)
{
if (!(IMEvent.flags & nmv.InstantMessageEventFlags.startTyping || IMEvent.flags & nmv.InstantMessageEventFlags.finishTyping))
{
bot.clientCommands.comms.typeInstantMessage(IMEvent.from, 'Thanks for the message! This account is a scripted agent (bot), so cannot reply to your query. Sorry!');
}
}
});
bot.clientEvents.onDisconnected.subscribe((DisconnectEvent) =>
{
console.log("Disconnected from simulator: "+DisconnectEvent.message);
if (!DisconnectEvent.requested)
{
setTimeout(() =>
{
console.log("Reconnecting");
connect();
}, 5000)
}
});
function connect()
{
console.log("Logging in..");
bot.login().then((response) =>
{
bot.clientEvents.onLure.subscribe((lureEvent) =>
{
bot.clientCommands.grid.getRegionMapInfo(lureEvent.gridX, lureEvent.gridY).then((regionInfo) =>
{
console.log('Auto-accepting teleport lure to ' + regionInfo.name + ' (' + regionInfo.avatars.length + ' avatar' + ((regionInfo.avatars.length === 1)?'':'s') + ' present) from ' + lureEvent.fromName + ' with message: ' + lureEvent.lureMessage);
//bot.clientCommands.teleport.acceptTeleport(lureEvent);
});
});
bot.clientEvents.onInstantMessage.subscribe((IMEvent) =>
{
if (IMEvent.source === nmv.ChatSourceType.Agent)
{
if (!(IMEvent.flags & nmv.InstantMessageEventFlags.startTyping || IMEvent.flags & nmv.InstantMessageEventFlags.finishTyping))
{
bot.clientCommands.comms.typeInstantMessage(IMEvent.from, 'Thanks for the message! This account is a scripted agent (bot), so cannot reply to your query. Sorry!');
}
}
});
bot.clientEvents.onDisconnected.subscribe((DisconnectEvent) =>
{
console.log("Disconnected from simulator: "+DisconnectEvent.message);
if (!DisconnectEvent.requested)
{
setTimeout(() =>
{
console.log("Reconnecting");
connect();
}, 5000)
}
});
console.log("Login complete");
//Establish circuit wit region
resp = response;

View File

@@ -24,6 +24,7 @@ import {KickUserMessage} from './classes/messages/KickUser';
import {StartPingCheckMessage} from './classes/messages/StartPingCheck';
import {CompletePingCheckMessage} from './classes/messages/CompletePingCheck';
import Timer = NodeJS.Timer;
import {Subscription} from 'rxjs/Subscription';
export class Bot
{
@@ -33,11 +34,13 @@ export class Bot
private ping: Timer | null = null;
private pingNumber = 0;
private lastSuccessfulPing = 0;
public clientEvents: ClientEvents | null = null;
private circuitSubscription: Subscription | null = null
public clientEvents: ClientEvents;
public clientCommands: ClientCommands;
constructor(login: LoginParameters)
{
this.clientEvents = new ClientEvents();
this.loginParams = login;
}
@@ -45,10 +48,9 @@ export class Bot
{
return new Promise((resolve, reject) =>
{
const loginHandler = new LoginHandler();
const loginHandler = new LoginHandler(this.clientEvents);
loginHandler.Login(this.loginParams).then((response: LoginResponse) =>
{
this.clientEvents = response.clientEvents;
this.currentRegion = response.region;
this.agent = response.agent;
this.clientCommands = new ClientCommands(response.region, response.agent, this);
@@ -66,6 +68,11 @@ export class Bot
{
this.currentRegion = region;
this.clientCommands = new ClientCommands(this.currentRegion, this.agent, this);
if (this.ping !== null)
{
clearInterval(this.ping);
this.ping = null;
}
this.connectToSim().then(() =>
{
resolve();
@@ -97,6 +104,11 @@ export class Bot
{
this.agent.shutdown();
this.currentRegion.shutdown();
if (this.circuitSubscription !== null)
{
this.circuitSubscription.unsubscribe();
this.circuitSubscription = null;
}
delete this.currentRegion;
delete this.agent;
delete this.clientCommands;
@@ -208,6 +220,11 @@ export class Bot
// We're dead, jim
this.agent.shutdown();
this.currentRegion.shutdown();
if (this.circuitSubscription !== null)
{
this.circuitSubscription.unsubscribe();
this.circuitSubscription = null;
}
delete this.currentRegion;
delete this.agent;
delete this.clientCommands;
@@ -228,7 +245,7 @@ export class Bot
}, 5000);
circuit.subscribeToMessages(
this.circuitSubscription = circuit.subscribeToMessages(
[
Message.TeleportFailed,
Message.TeleportFinish,
@@ -305,6 +322,11 @@ export class Bot
const kickUser = packet.message as KickUserMessage;
this.agent.shutdown();
this.currentRegion.shutdown();
if (this.circuitSubscription !== null)
{
this.circuitSubscription.unsubscribe();
this.circuitSubscription = null;
}
delete this.currentRegion;
delete this.agent;
delete this.clientCommands;

View File

@@ -2,10 +2,13 @@ import * as xmlrpc from 'xmlrpc';
import * as crypto from 'crypto';
import {LoginParameters} from './classes/LoginParameters';
import {LoginResponse} from './classes/LoginResponse';
import {ClientEvents} from './classes/ClientEvents';
const uuid = require('uuid');
export class LoginHandler
{
private clientEvents: ClientEvents;
static GenerateMAC(): string
{
const hexDigits = '0123456789ABCDEF';
@@ -22,6 +25,12 @@ export class LoginHandler
return macAddress;
}
constructor(ce: ClientEvents)
{
this.clientEvents = ce;
}
Login(params: LoginParameters): Promise<LoginResponse>
{
return new Promise<LoginResponse>((resolve, reject) =>
@@ -38,7 +47,7 @@ export class LoginHandler
{
'first': params.firstName,
'last': params.lastName,
'passwd': '$1$' + crypto.createHash('md5').update(params.password).digest('hex'),
'passwd': '$1$' + crypto.createHash('md5').update(params.password.substr(0, 16)).digest('hex'),
'start': 'home',
'major': '0',
'minor': '0',
@@ -65,7 +74,7 @@ export class LoginHandler
'global-textures'
]
}
], function(error, value)
], (error, value) =>
{
if (error)
{
@@ -79,9 +88,8 @@ export class LoginHandler
}
else
{
const response = new LoginResponse(value);
const response = new LoginResponse(value, this.clientEvents);
resolve(response);
}
}
}

View File

@@ -40,7 +40,6 @@ export class Comms
{
case InstantMessageDialog.MessageFromAgent:
{
console.log(im);
const imEvent = new InstantMessageEvent();
imEvent.source = ChatSourceType.Agent;
imEvent.from = im.AgentData.AgentID;
@@ -69,7 +68,6 @@ export class Comms
break;
case InstantMessageDialog.MessageFromObject:
{
console.log(im);
const imEvent = new InstantMessageEvent();
imEvent.source = ChatSourceType.Object;
imEvent.owner = im.AgentData.AgentID;

View File

@@ -88,9 +88,9 @@ export class LoginResponse
return result;
}
constructor(json: any)
constructor(json: any, ce: ClientEvents)
{
this.clientEvents = new ClientEvents();
this.clientEvents = ce;
this.agent = new Agent(this.clientEvents);
this.region = new Region(this.agent, this.clientEvents);
Object.keys(json).forEach((key: string) =>

View File

@@ -15,18 +15,18 @@ import {Vector3} from './Vector3';
import {CompressedFlags} from '../enums/CompressedFlags';
import {ExtraParamType} from '../enums/ExtraParamType';
import {Utils} from './Utils';
import {SoundFlags} from '../enums/SoundFlags';
import {PCode} from '../enums/PCode';
import {NameValue} from "./NameValue";
import {ClientEvents} from "./ClientEvents";
import {NameValue} from './NameValue';
import {ClientEvents} from './ClientEvents';
import {KillObjectMessage} from './messages/KillObject';
export class ObjectStore
{
private circuit: Circuit;
private agent: Agent;
private objects: {[key: number]: GameObject} = {};
private objectsByUUID: {[key: string]: number} = {};
private objectsByParent: {[key: number]: number[]} = {};
private objects: { [key: number]: GameObject } = {};
private objectsByUUID: { [key: string]: number } = {};
private objectsByParent: { [key: number]: number[] } = {};
private clientEvents: ClientEvents;
constructor(circuit: Circuit, agent: Agent, clientEvents: ClientEvents)
@@ -35,11 +35,12 @@ export class ObjectStore
this.circuit = circuit;
this.agent = agent;
this.circuit.subscribeToMessages([
Message.ObjectUpdate,
Message.ObjectUpdateCached,
Message.ObjectUpdateCompressed,
Message.ImprovedTerseObjectUpdate,
Message.MultipleObjectUpdate
Message.ObjectUpdate,
Message.ObjectUpdateCached,
Message.ObjectUpdateCompressed,
Message.ImprovedTerseObjectUpdate,
Message.MultipleObjectUpdate,
Message.KillObject
], (packet: Packet) =>
{
switch (packet.message.id)
@@ -148,8 +149,8 @@ export class ObjectStore
objectUpdateCached.ObjectData.forEach((obj) =>
{
rmo.ObjectData.push({
CacheMissType: 0,
ID: obj.ID
CacheMissType: 0,
ID: obj.ID
});
});
circuit.sendMessage(rmo, 0);
@@ -342,9 +343,53 @@ export class ObjectStore
// TODO: multipleObjectUpdate
console.error('TODO: MultipleObjectUpdate');
break;
case Message.KillObject:
const killObj = packet.message as KillObjectMessage;
killObj.ObjectData.forEach((obj) =>
{
const objectID = obj.ID;
this.deleteObject(objectID);
});
break;
}
});
}
deleteObject(objectID: number)
{
if (this.objects[objectID])
{
// First, kill all children
if (this.objectsByParent[objectID])
{
this.objectsByParent[objectID].forEach((childObjID) =>
{
this.deleteObject(childObjID);
});
}
delete this.objectsByParent[objectID];
// Now delete this object
const objct = this.objects[objectID];
const uuid = objct.FullID.toString();
if (this.objectsByUUID[uuid])
{
delete this.objectsByUUID[uuid];
}
const parentID = objct.ParentID;
if (this.objectsByParent[parentID])
{
const ind = this.objectsByParent[parentID].indexOf(objectID);
if (ind !== -1)
{
this.objectsByParent[parentID].splice(ind, 1);
}
}
delete this.objects[objectID];
}
}
readExtraParams(buf: Buffer, pos: number, o: GameObject): number
{
if (pos >= buf.length)
@@ -364,6 +409,7 @@ export class ObjectStore
}
return pos;
}
getObjectsByParent(parentID: number): GameObject[]
{
const list = this.objectsByParent[parentID];
@@ -378,9 +424,10 @@ export class ObjectStore
});
return result;
}
parseNameValues(str: string): {[key: string]: NameValue}
parseNameValues(str: string): { [key: string]: NameValue }
{
const nv: {[key: string]: NameValue} = {};
const nv: { [key: string]: NameValue } = {};
const lines = str.split('\n');
lines.forEach((line) =>
{
@@ -389,7 +436,7 @@ export class ObjectStore
let kv = line.split(/[\t ]/);
if (kv.length > 5)
{
for(let x = 5; x < kv.length; x++)
for (let x = 5; x < kv.length; x++)
{
kv[4] += ' ' + kv[x];
}
@@ -413,6 +460,7 @@ export class ObjectStore
});
return nv;
}
shutdown()
{
this.objects = {};