Skip to content

Commit

Permalink
Added GAsync tool
Browse files Browse the repository at this point in the history
  • Loading branch information
WiredSpast committed Feb 10, 2022
1 parent 1540807 commit c36d777
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 18 deletions.
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ import { HFriend } from "./lib/extension/parsers/hfriend";
import { HRelationshipStatus } from "./lib/extension/parsers/hrelationshipstatus";

export { HFloorItem, HStuff, HWallItem, HInventoryItem, HFurniType, HEntity, HEntityUpdate, HEntityType, HStance, HGender, HSign, HAction, HGroup, HPoint, HFacing, HUserProfile, HFriend, HRelationshipStatus };

// Tools
import { GAsync } from "./lib/extension/tools/gasync/gasync";
import { AwaitingPacket } from "./lib/extension/tools/gasync/awaitingpacket";

export { GAsync, AwaitingPacket };
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ exports.HFacing = require("./lib/extension/parsers/hfacing").HFacing;
exports.HUserProfile = require("./lib/extension/parsers/huserprofile").HUserProfile;
exports.HFriend = require("./lib/extension/parsers/hfriend").HFriend;
exports.HRelationShipStatus = require("./lib/extension/parsers/hrelationshipstatus").HRelationshipStatus;

// Tools
exports.GAsync = require("./lib/extension/tools/gasync/gasync").GAsync;
exports.AwaitingPacket = require("./lib/extension/tools/gasync/awaitingpacket").AwaitingPacket;
3 changes: 3 additions & 0 deletions lib/extension/extension.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ export class Extension {
*/
on(event: 'socketdisconnect', listener: () => void): this;

/**
* Get the packet info manager
*/
getPacketInfoManager(): PacketInfoManager;

}
28 changes: 28 additions & 0 deletions lib/extension/tools/gasync/awaitingpacket.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { HDirection } from "../../../protocol/hdirection";
import { HPacket } from "../../../protocol/hpacket";

export class AwaitingPacket {
constructor(headerName: string, direction: HDirection, maxWaitingTimeMillis: number);

/**
* Set minimum waiting time (wait this time even if the packet was already intercepted)
* @param millis minimum waiting time
*/
setMinWaitingTime(millis: number): AwaitingPacket;

/**
* Add a condition to the awaiting packet
* @param condition Predicate with HPacket parameter return true or false
*/
addCondition(condition: (hPacket: HPacket) => boolean): AwaitingPacket;

/**
* Get header name of awaiting packet
*/
get headerName(): string;

/**
* Get direction of awaiting packet
*/
get direction(): HDirection;
}
102 changes: 102 additions & 0 deletions lib/extension/tools/gasync/awaitingpacket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const {HDirection} = require("../../../protocol/hdirection");
const {HPacket} = require("../../../protocol/hpacket");
const {HMessage} = require("../../../protocol/hmessage");
exports.AwaitingPacket = class AwaitingPacket {
#headerName;
#direction;
#packet;
#received = false;
#conditions = [];
#start;
#minWait = 0;

constructor(headerName, direction, maxWaitingTimeMillis) {
if (typeof headerName !== 'string') {
throw new Error("AwaitingPacket.constructor: headerName must be a string");
}
if (!HDirection.identify(direction)) {
throw new Error("AwaitingPacket.constructor: direction must be a value of HDirection");
}
if (!Number.isInteger(maxWaitingTimeMillis)) {
throw new Error("AwaitingPacket.constructor: maxWaitingTimeMillis must be an integer");
}

if(maxWaitingTimeMillis < 30) {
maxWaitingTimeMillis = 30;
}

setTimeout(() => {
this.#received = true;
}, maxWaitingTimeMillis);

this.#start = Date.now();

this.#direction = direction;
this.#headerName = headerName;
}

get headerName() {
return this.#headerName;
}

get direction() {
return this.#direction;
}

setMinWaitingTime(millis) {
if (!Number.isInteger(millis)) {
throw new Error("AwaitingPacket.setMinWaitingTime: millis must be an integer");
}

this.minWait = millis;

return this;
}

addCondition(condition) {
if (typeof condition !== 'function') {
throw new Error("AwaitingPacket.addCondition: condition must be a function");
}

this.#conditions.push(condition);

return this;
}

set packet(val) {
if (!(val instanceof HPacket)) {
throw new Error("AwaitingPacket.setPacket: packet must be an instance of HPacket")
}

this.#packet = val;
this.#received = true;
}

get packet() {
if (this.#packet !== undefined) {
this.#packet.resetReadIndex();
}

return this.#packet;
}

test(hMessage) {
if (!(hMessage instanceof HMessage)) {
throw new Error("AwaitingPacket.test: hMessage must be an instance of HMessage");
}

for (let condition of this.#conditions) {
let packet = hMessage.getPacket();
packet.resetReadIndex();
if(!condition(packet)) {
return false;
}
}

return true;
}

get ready() {
return this.#received && (this.#start + this.#minWait) < Date.now();
}
}
25 changes: 25 additions & 0 deletions lib/extension/tools/gasync/gasync.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Extension } from "../../extension";
import { AwaitingPacket } from "./awaitingpacket";
import { HPacket } from "../../../protocol/hpacket";


export class GAsync {
constructor(ext: Extension);

/**
* Asynchronously await a packet
* @param packets
*/
awaitPacket(...packets: AwaitingPacket[]): HPacket | undefined;

/**
* Asynchronously await multiple packets
* @param packets
*/
awaitMultiplePackets(...packets: AwaitingPacket[]): (HPacket | undefined)[];

/**
* Clear all awaiting packets
*/
clear();
}
95 changes: 95 additions & 0 deletions lib/extension/tools/gasync/gasync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const { Extension } = require("../../extension");
const { HDirection } = require("../../../protocol/hdirection");
const { AwaitingPacket } = require("./awaitingpacket");

exports.GAsync = class GAsync {
#packetInfoManager = undefined;
#awaitingPackets = [];

constructor(ext) {
if (!(ext instanceof Extension)) {
throw new Error("GAsync.constructor: ext must be an instance of Extension");
}
this.#packetInfoManager = ext.getPacketInfoManager();
ext.on('start', () => {
this.#packetInfoManager = ext.getPacketInfoManager();
});

ext.interceptAll(HDirection.TOSERVER, this.#onMessageToServer.bind(this));
ext.interceptAll(HDirection.TOCLIENT, this.#onMessageToClient.bind(this));
}

#onMessageToServer(hMessage) {
if (this.#packetInfoManager !== undefined) {
let info = this.#packetInfoManager.getPacketInfoFromHeaderId(HDirection.TOSERVER, hMessage.getPacket().headerId());
if(info === null) {
return;
}

this.#awaitingPackets
.filter(p => p.direction === HDirection.TOSERVER)
.filter(p => p.headerName === info.name)
.filter(p => p.test(hMessage))
.forEach(p => p.packet = hMessage.getPacket());
}
}

#onMessageToClient(hMessage) {
if(this.#packetInfoManager !== undefined) {
let info = this.#packetInfoManager.getPacketInfoFromHeaderId(HDirection.TOCLIENT, hMessage.getPacket().headerId());
if(info === null) {
return;
}

this.#awaitingPackets
.filter(p => p.direction === HDirection.TOCLIENT)
.filter(p => p.headerName === info.name)
.filter(p => p.test(hMessage))
.forEach(p => p.packet = hMessage.getPacket());
}
}

async awaitPacket(...packets) {
for (let packet of packets) {
if (!(packet instanceof AwaitingPacket)) {
throw new Error("GAsync.awaitMultiplePackets: all packets must be an instance of AwaitingPacket");
}
}

this.#awaitingPackets.push(...packets);

return new Promise(resolve => {
let interval = setInterval(() => {
for (let packet of packets.filter(p => p.ready)) {
clearInterval(interval);
this.#awaitingPackets = this.#awaitingPackets.filter(p => !packets.includes(p));
resolve(packet.packet);
}
}, 1);
});
}

async awaitMultiplePackets(...packets) {
for(let packet of packets) {
if(!(packet instanceof AwaitingPacket)) {
throw new Error("GAsync.awaitMultiplePackets: all packets must be an instance of AwaitingPacket");
}
}

this.#awaitingPackets.push(...packets);

return new Promise(resolve => {
let interval = setInterval(() => {
if(!packets.map(p => p.ready).includes(false)) {
clearInterval(interval);
this.#awaitingPackets = this.#awaitingPackets.filter(p => !packets.includes(p));
resolve(packets.map(p => p.packet));
}
}, 1);
});
}

clear() {
this.#awaitingPackets = [];
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gnode-api",
"version": "0.1.3",
"version": "0.1.4",
"description": "Node.js G-Earth extension API",
"main": "index.js",
"types": "index.d.ts",
Expand Down
40 changes: 23 additions & 17 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
const { Extension, HDirection, HEntity, HUserProfile} = require('../index');
const { Extension, HDirection, HEntity, HEntityUpdate} = require('../index');

const extensionInfo = require('./package.json');
const { GAsync } = require("../lib/extension/tools/gasync/gasync");
const {AwaitingPacket} = require("../lib/extension/tools/gasync/awaitingpacket");

const ext = new Extension(extensionInfo);
//ext.run();
const gAsync = new GAsync(ext);
ext.run();

ext.interceptByNameOrHash(HDirection.TOCLIENT, 'Users', onUsers);

ext.interceptByNameOrHash(HDirection.TOCLIENT, 'ExtendedProfile', onExtendedProfile)
ext.interceptByNameOrHash(HDirection.TOSERVER, "MoveAvatar", async hMessage => {
let packet = hMessage.getPacket();
let x = packet.readInteger();
let y = packet.readInteger();

function onUsers(hMessage) {
let users = HEntity.parse(hMessage.getPacket());
hMessage.blocked = true;
for(let user of users) {
user.figureId = "hr-828-49.hd-180-28.ch-3788-92.lg-3136-106.sh-290-92.ea-3803-92.ca-3187-92";
}
let awaitedPacket = await gAsync
.awaitPacket(new AwaitingPacket("UserUpdate", HDirection.TOCLIENT, 1000)
.addCondition(hPacket => {
let entityUpdates = HEntityUpdate.parse(hPacket);
for (let entityUpdate of entityUpdates) {
if(entityUpdate.tile.x === x && entityUpdate.tile.y === y) {
return true;
}
}
return false;
})
);

let packet = HEntity.constructPacket(users, hMessage.getPacket().headerId());
ext.sendToClient(packet);
}

function onExtendedProfile(hMessage) {
console.log(hMessage);
}
console.log(awaitedPacket);
});

0 comments on commit c36d777

Please sign in to comment.