G-Earth 1.5.4 beta 22 - Initial release

Komplettes G-Earth Paket inkl. JRE, Extensions und Tools.

Extensions:
- G-BuildTools, G-Click Ultimate, G-Loader, G-Manipulate
- G-Presets, G-Translator, G-Trigger, G-itemViewer
- Market Utils, Packet Info Explorer, Plants
- RandomRoomVisitor, RoomLogger, Sanbovir Photo Inspector
- SpyFriends, WallAligner, XabboScripter, xabbo
This commit is contained in:
Administrator
2026-03-16 09:45:04 +01:00
commit 368b92d87a
7984 changed files with 1373096 additions and 0 deletions
+166
View File
@@ -0,0 +1,166 @@
# G-Node
Node.js [G-Earth](https://github.com/sirjonasxx/G-Earth) extension API <br>
Requires Node.js V15.0.0+
## How to install
Using npm:
```cmd
$ npm install gnode-api
```
Using yarn:
```cmd
$ yarn add gnode-api
```
## How to run selfmade extension
```cmd
$ node [filename] -p [port]
```
### Example
```cmd
$ node extension.js -p 9092
```
## Example
```js
import { Extension, HPacket, HDirection } from 'gnode-api';
// Use package.json as extensionInfo or create an object including 'name', 'description', 'version' and 'author'
import { readFile } from 'fs/promises';
const extensionInfo = JSON.parse(
await readFile(
new URL('./package.json', import.meta.url)
)
);
// Create new extension with extensionInfo
let ext = new Extension(extensionInfo);
// Start connection to G-Earth
ext.run();
```
### Listeners
#### Do on connection to G-Earth
```js
ext.on('init', () => {
console.log("Connected to G-Earth");
});
```
#### Do on connection to hotel
```js
ext.on('start', () => {
console.log("Connected to G-Earth");
});
```
#### Do on connection to hotel and get client info
```js
ext.on('connect', (host, connectionPort, hotelVersion, clientIdentifier, clientType) => {
// do something with client info
});
```
#### Do on connection to hotel ended
```js
ext.on('end', () => {
console.log("Connection to G-Earth ended");
});
```
#### Do on click on button in G-Earth Extensions tab
```js
ext.on('click', () => {
console.log("G-Earth button clicked");
});
```
### Packet intercepting
#### Intercept all packets in one direction
```js
ext.interceptAll(HDirection.TOCLIENT, hMessage => {
let hPacket = hMessage.getPacket();
...
});
ext.interceptAll(HDirection.TOSERVER, hMessage => {
let hPacket = hMessage.getPacket();
...
});
```
#### Intercept all packets with a certain header id in one direction
```js
ext.interceptByHeaderId(HDirection.TOCLIENT, 969, hMessage => {
let hPacket = hMessage.getPacket();
...
});
ext.interceptByHeaderId(HDirection.TOSERVER, 2443, hMessage => {
let hPacket = hMessage.getPacket();
...
});
```
#### Intercept all packets by name or hash in one direction
```js
ext.interceptByNameOrHash(HDirection.TOCLIENT, 'Ping', hMessage => {
let hPacket = hMessage.getPacket();
...
});
ext.interceptByNameOrHash(HDirection.TOSERVER, 'Pong', hMessage => {
let hPacket = hMessage.getPacket();
...
});
```
### Reading a packet
#### Reading a var by var
```js
let hPacket = hMessage.getPacket(); // Example: {in:Chat}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}{i:0}
let userIndex = hPacket.readInteger();
let message = hPacket.readString();
hPacket.readInteger();
let bubble = hPacket.readInteger();
```
#### Reading a structure into an array
```js
let hPacket = hMessage.getPacket(); // Example: {in:Chat}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}{i:0}
let vars = hPacket.read('iSiiii');
let userIndex = vars[0];
let message = vars[1];
let bubble = vars[3];
```
### Creating a packet
#### Creating packet from identifier (name or hash) and direction
```js
let hPacket = new HPacket('Chat', HDirection.TOCLIENT); // Example: {in:Chat}
hPacket.appendInteger(1); // {in:Chat}{i:1}
hPacket.appendString('Hello'); // {in:Chat}{i:1}{s:"Hello"}
hPacket.appendInteger(0); // {in:Chat}{i:1}{s:"Hello"}{i:0}
hPacket.appendInteger(1); // {in:Chat}{i:1}{s:"Hello"}{i:0}{i:1}
hPacket.appendInteger(0); // {in:Chat}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}
hPacket.appendInteger(0); // {in:Chat}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}{i:0}
```
#### Creating packet from header Id
```js
let hPacket = new HPacket(1918) // Example: {l}{h:1918}
.appendInteger(1) // {l}{h:1918}{i:1}
.appendString('Hello') // {l}{h:1918}{i:1}{s:"Hello"}
.appendInteger(0) // {l}{h:1918}{i:1}{s:"Hello"}{i:0}
.appendInteger(1) // {l}{h:1918}{i:1}{s:"Hello"}{i:0}{i:1}
.appendInteger(0) // {l}{h:1918}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}
.appendInteger(0); // {l}{h:1918}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}{i:0}
```
#### Creating packet from packet expression
```js
let hPacket = new HPacket('{in:Chat}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}{i:0}');
```
OR
```js
let hPacket = new HPacket('{l}{h:1918}{i:1}{s:"Hello"}{i:0}{i:1}{i:0}{i:0}');
```
### Sending a packet
#### Send packet to client
```js
ext.sendToClient(hPacket);
```
#### Send packet to server
```js
ext.sendToServer(hPacket);
```
## More
For more examples and/or help read the [wiki](https://github.com/WiredSpast/G-Node/wiki)
+42
View File
@@ -0,0 +1,42 @@
// Base
export { HPacket } from "./lib/protocol/hpacket";
export { HDirection } from "./lib/protocol/hdirection";
export { HMessage } from "./lib/protocol/hmessage";
export { Extension } from "./lib/extension/extension";
export { HClient } from "./lib/protocol/hclient";
export { HostInfo } from "./lib/misc/hostinfo";
// Parsers
export { HFloorItem } from "./lib/extension/parsers/hflooritem";
export { HStuff } from "./lib/extension/parsers/hstuff";
export { HWallItem } from "./lib/extension/parsers/hwallitem";
export { HInventoryItem } from "./lib/extension/parsers/hinventoryitem";
export { HProductType } from "./lib/extension/parsers/hproducttype";
export { HEntity } from "./lib/extension/parsers/hentity";
export { HEntityUpdate } from "./lib/extension/parsers/hentityupdate";
export { HEntityType } from "./lib/extension/parsers/hentitytype";
export { HStance } from "./lib/extension/parsers/hstance";
export { HGender } from "./lib/extension/parsers/hgender";
export { HSign } from "./lib/extension/parsers/hsign";
export { HAction } from "./lib/extension/parsers/haction";
export { HGroup } from "./lib/extension/parsers/hgroup";
export { HPoint } from "./lib/extension/parsers/hpoint";
export { HFacing } from "./lib/extension/parsers/hfacing";
export { HUserProfile } from "./lib/extension/parsers/huserprofile";
export { HFriend } from "./lib/extension/parsers/hfriend";
export { HRelationshipStatus } from "./lib/extension/parsers/hrelationshipstatus";
export { HNavigatorSearchResult } from "./lib/extension/parsers/navigator/hnavigatorsearchresult";
export { HNavigatorBlock } from "./lib/extension/parsers/navigator/hnavigatorblock";
export { HNavigatorRoom } from "./lib/extension/parsers/navigator/hnavigatorroom";
export { HRoomResult } from "./lib/extension/parsers/room/hroomresult";
export { HRoomModSettings } from "./lib/extension/parsers/room/hroommodsettings";
export { HRoomChatSettings } from "./lib/extension/parsers/room/hroomchatsettings";
// Tools
export { GAsync } from "./lib/extension/tools/gasync/gasync";
export { AwaitingPacket } from "./lib/extension/tools/gasync/awaitingpacket";
export { GHeightMap } from "./lib/extension/tools/groom/gheightmap";
export { GHeightMapTile } from "./lib/extension/tools/groom/gheightmaptile";
export { Hotel } from "./lib/extension/tools/furnidata/hotel";
export { FurniDataUtils, FurniData, FloorItemData, WallItemData } from "./lib/extension/tools/furnidata/furnidata";
+43
View File
@@ -0,0 +1,43 @@
// Base
export { HPacket } from "./lib/protocol/hpacket.js";
export { HDirection } from "./lib/protocol/hdirection.js";
export { HMessage } from "./lib/protocol/hmessage.js";
export { Extension } from "./lib/extension/extension.js";
export { HClient } from "./lib/protocol/hclient.js";
export { HostInfo } from "./lib/misc/hostinfo.js";
// Parsers
export { HFloorItem } from "./lib/extension/parsers/hflooritem.js";
export { HStuff } from "./lib/extension/parsers/hstuff.js";
export { HWallItem } from "./lib/extension/parsers/hwallitem.js";
export { HInventoryItem } from "./lib/extension/parsers/hinventoryitem.js";
export { HProductType } from "./lib/extension/parsers/hproducttype.js";
export { HEntity } from "./lib/extension/parsers/hentity.js";
export { HEntityUpdate } from "./lib/extension/parsers/hentityupdate.js";
export { HEntityType } from "./lib/extension/parsers/hentitytype.js";
export { HStance } from "./lib/extension/parsers/hstance.js";
export { HGender } from "./lib/extension/parsers/hgender.js";
export { HSign } from "./lib/extension/parsers/hsign.js";
export { HAction } from "./lib/extension/parsers/haction.js";
export { HGroup } from "./lib/extension/parsers/hgroup.js";
export { HPoint } from "./lib/extension/parsers/hpoint.js";
export { HFacing } from "./lib/extension/parsers/hfacing.js";
export { HUserProfile } from "./lib/extension/parsers/huserprofile.js";
export { HFriend } from "./lib/extension/parsers/hfriend.js";
export { HRelationshipStatus } from "./lib/extension/parsers/hrelationshipstatus.js";
export { HNavigatorSearchResult } from "./lib/extension/parsers/navigator/hnavigatorsearchresult.js";
export { HNavigatorBlock } from "./lib/extension/parsers/navigator/hnavigatorblock.js";
export { HNavigatorRoom } from "./lib/extension/parsers/navigator/hnavigatorroom.js";
export { HRoomResult } from "./lib/extension/parsers/room/hroomresult.js";
export { HRoomModSettings } from './lib/extension/parsers/room/hroommodsettings.js';
export { HRoomChatSettings } from './lib/extension/parsers/room/hroomchatsettings.js';
// Tools
export { GAsync } from "./lib/extension/tools/gasync/gasync.js";
export { AwaitingPacket } from "./lib/extension/tools/gasync/awaitingpacket.js";
export { GHeightMap } from "./lib/extension/tools/groom/gheightmap.js";
export { GInventory } from "./lib/extension/tools/ginventory.js";
export { Hotel } from "./lib/extension/tools/furnidata/hotel.js";
export { FurniDataUtils } from "./lib/extension/tools/furnidata/furnidata.js";
@@ -0,0 +1,136 @@
import {HPacket} from "../protocol/hpacket";
import {HDirection} from "../protocol/hdirection";
import {HMessage} from "../protocol/hmessage";
import {ExtensionInfo} from "./extensioninfo";
import {HClient} from "../protocol/hclient";
import {PacketInfoManager} from "../services/packetinfo/packetinfomanager";
import { HostInfo } from "../misc/hostinfo";
export class Extension {
constructor(extensionInfo: ExtensionInfo);
constructor(extensionInfo: ExtensionInfo, args: string[]);
/**
* Start connection with G-Earth
*/
run(): void;
/**
* Send a message to the client
* @param packet packet to be sent
* @return success or failure
*/
sendToClient(packet: HPacket): boolean;
/**
* Send a message to the server
* @param packet packet to be sent
* @return success or failure
*/
sendToServer(packet: HPacket): boolean;
/**
* Register a listener on a specific packet type by name or hash
* @param direction ToClient or ToServer
* @param headerNameOrHash The packet name or hash
* @param messageListener The callback
*/
interceptByNameOrHash(direction: HDirection, headerNameOrHash: string | String, messageListener: (hMessage: HMessage) => void): void;
/**
* Register a listener on a specific packet type by header ID
* @param direction ToClient or ToServer
* @param headerId The packet header ID
* @param messageListener The callback
*/
interceptByHeaderId(direction: HDirection, headerId: number, messageListener: (hMessage: HMessage) => void): void;
/**
* Register a listener on a all packet types
* @param direction ToClient or ToServer
* @param messageListener The callback
*/
interceptAll(direction: HDirection, messageListener: (hMessage: HMessage) => void): void;
/**
* Requests the flags which have been given to G-Earth when it got executed
* For example, you might want this extension to do a specific thing if the flag "-e" was given
* @param flagRequestCallback callback
* @return if the request was successful, will return false if another flag request is busy
*/
requestFlags(flagRequestCallback: Function): boolean;
/**
* Write to the console in G-Earth
* @param s The text to be written
* @param colorClass Optional color of the text to be written (default: "black")
*/
writeToConsole(s: string | String, colorClass?: string): void;
/**
* Listen for an event
* @param event Valid events: init, click, start, end, connect
* @param listener Do on event
*/
on(event: string, listener: (...args: any[]) => void): this;
/**
* Listen for extension initialization
* @param event Extension initialized
* @param listener Do on initialization
*/
on(event: 'init', listener: () => void): this;
/**
* Listen for click on button in G-Earth Extensions tab
* @param event G-Earth button clicked
* @param listener Do on click
*/
on(event: 'click', listener: () => void): this;
/**
* Listen for the connection to start
* @param event Connection started
* @param listener Do on connection start
*/
on(event: 'start', listener: () => void): this;
/**
* Listen for the connection to end
* @param event Connection ended
* @param listener Do on connection end
*/
on(event: 'end', listener: () => void): this;
/**
* Listen for a connection
* @param event Connection made
* @param listener Do on connection made (passes parameters host, connectionPort, hotelVersion, clientIdentifier, clientType)
*/
on(event: 'connect', listener: (host: string, connectionPort: number, hotelVersion: string, clientIdentifier: string, clientType: HClient) => void): this;
/**
* Listen for the socket connection to drop
* @param event Socket connection ended
* @param listener Do on socket connection end
*/
on(event: 'socketdisconnect', listener: () => void): this;
/**
* Listen for updates on host info
* @param event Host info updated
* @param listener Do on host info update
*/
on(event: 'hostinfoupdate', listener: (hostInfo: HostInfo) => void): this;
/**
* Get the packet info manager
*/
getPacketInfoManager(): PacketInfoManager | undefined;
/**
* Get the CURRENT host info (use the hostinfoupdate listener to always have the host info up to date)
*/
getHostInfo(): HostInfo | undefined;
}
@@ -0,0 +1,382 @@
import { HPacket } from "../protocol/hpacket.js";
import EventEmitter from "events";
import util from "util";
import { Socket } from "net";
import { PacketInfoManager } from "../services/packetinfo/packetinfomanager.js";
import { HClient } from "../protocol/hclient.js";
import { HostInfo } from "../misc/hostinfo.js";
import { HMessage } from "../protocol/hmessage.js";
import { HDirection } from "../protocol/hdirection.js";
const INCOMING_MESSAGE_IDS = {
ONDOUBLECLICK: 1,
INFOREQUEST: 2,
PACKETINTERCEPT: 3,
FLAGSCHECK: 4,
CONNECTIONSTART: 5,
CONNECTIONEND: 6,
INIT: 7,
UPDATEHOSTINFO: 10,
PACKETTOSTRING_RESPONSE: 20,
STRINGTOPACKET_RESPONSE: 21
}
const OUTGOING_MESSAGE_IDS = {
EXTENSIONINFO: 1,
MANIPULATEDPACKET: 2,
REQUESTFLAGS: 3,
SENDMESSAGE: 4,
PACKETTOSTRING_REQUEST: 20,
STRINGTOPACKET_REQUEST: 21,
EXTENSIONCONSOLELOG: 98
}
const PORT_FLAG = ["--port", "-p"];
const FILE_FLAG = ["--filename", "-f"];
const COOKIE_FLAG = ["--auth-token", "-c"];
export class Extension extends EventEmitter {
#gEarthExtensionServer;
#incomingMessageListeners = new Map();
#outgoingMessageListeners = new Map();
#flagRequestCallback = null;
#args;
#isCorrupted = false;
#extensionInfo;
#packetInfoManager;
#hostInfo;
#delayed_init = false;
#getArgument = (flags) => {
for(let i = 0; i < this.#args.length - 1; i++) {
for(let j in flags) {
if(this.#args[i].toLowerCase() === flags[j].toLowerCase()) {
return this.#args[i+1];
}
}
}
}
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}Extension {\n`
+ `${indent} name: ${util.inspect(this.#extensionInfo.name, {colors: true})}\n`
+ `${indent} description: ${util.inspect(this.#extensionInfo.description, {colors: true})}\n`
+ `${indent} version: ${util.inspect(this.#extensionInfo.version, {colors: true})}\n`
+ `${indent} author: ${util.inspect(this.#extensionInfo.author, {colors: true})}\n`
+ `${indent} extensionport: ${util.inspect(this.#getArgument(PORT_FLAG), {colors: true})}\n`
+ `${indent}}`;
}
constructor(extensionInfo, args = process.argv) {
super();
if('name' in extensionInfo && 'description' in extensionInfo && 'version' in extensionInfo && 'author' in extensionInfo) {
this.#args = args;
this.#extensionInfo = extensionInfo;
if(!this.#getArgument(PORT_FLAG)) {
throw new Error("Run command arguments must include port, example: node extension.js -p 9092 OR node extension.js --port 9092")
}
return;
}
throw new Error("Extension.constructor: extensionInfo object requires name, description, version and author");
}
run() {
if(this.#isCorrupted) {
return;
}
let port = this.#getArgument(PORT_FLAG);
this.#gEarthExtensionServer = new Socket();
this.#gEarthExtensionServer.setNoDelay(true);
this.#gEarthExtensionServer.connect(port);
this.#gEarthExtensionServer.on('connect', () => {
console.log("Connected to G-Earth");
});
this.#gEarthExtensionServer.on('error', err => {
switch(err.code) {
case "ECONNREFUSED":
throw new Error("Connection to G-Earth refused, make sure G-Earth is active");
}
this.emit('socketdisconnect');
});
this.#gEarthExtensionServer.on('close', () => {
console.log("G-Earth connection closed");
this.emit('socketdisconnect');
});
let appendNext = false;
let prev = null;
this.#gEarthExtensionServer.on('data', data => {
data = Buffer.from(data)
do {
if(appendNext) {
appendNext = false;
let newData = Buffer.alloc(prev.length + data.length);
newData.set(prev);
newData.set(data, prev.length);
data = newData;
}
let length = data.readInt32BE();
if(data.length >= length + 4) {
this.#onGPacket(new HPacket(data.slice(0, 4 + length)));
data = data.slice(4 + length);
} else {
appendNext = true;
prev = data;
}
} while(data.length > 0 && !appendNext);
});
}
#onGPacket = (packet) => {
switch(packet.headerId()) {
case INCOMING_MESSAGE_IDS.INFOREQUEST:
let file = this.#getArgument(FILE_FLAG);
let cookie = this.#getArgument(COOKIE_FLAG);
let response = new HPacket(OUTGOING_MESSAGE_IDS.EXTENSIONINFO)
.appendString(this.#extensionInfo.name)
.appendString(this.#extensionInfo.author)
.appendString(this.#extensionInfo.version)
.appendString(this.#extensionInfo.description)
.appendBoolean(this.eventNames().includes('click'))
.appendBoolean(file !== undefined) // IsInstalledExtension
.appendString(file !== undefined ? file : "")
.appendString(cookie !== undefined ? cookie : "")
.appendBoolean(true) // leaveButtonVisible
.appendBoolean(true); // DeleteButtonVisible
this.#gEarthExtensionServer.write(response.toBytes());
break;
case INCOMING_MESSAGE_IDS.CONNECTIONSTART:
let [ host, connectionPort, hotelVersion, clientIdentifier ] = packet.read('SiSS');
let client = packet.readString().toLowerCase() === "flash" ? HClient.FLASH : HClient.UNITY;
this.#packetInfoManager = PacketInfoManager.readFromPacket(packet);
if (this.#delayed_init) {
this.emit('init');
this.#delayed_init = false;
}
this.emit('connect', host, connectionPort, hotelVersion, clientIdentifier, client);
this.emit('start');
break;
case INCOMING_MESSAGE_IDS.CONNECTIONEND:
this.emit('end');
break;
case INCOMING_MESSAGE_IDS.FLAGSCHECK:
if(this.#flagRequestCallback !== null && this.#flagRequestCallback !== undefined) {
let arraySize = packet.readInteger();
let gEarthArgs = [];
for(let i = 0; i < arraySize; i++) {
gEarthArgs = packet.readString();
}
this.#flagRequestCallback(gEarthArgs);
}
this.#flagRequestCallback = null;
break;
case INCOMING_MESSAGE_IDS.INIT:
this.#delayed_init = packet.readBoolean();
this.#hostInfo = HostInfo.fromPacket(packet);
this.emit('hostinfoupdate', this.#hostInfo);
if (!this.#delayed_init) {
this.emit('init');
}
this.#writeToConsole("Extension \"" + this.#extensionInfo.name + "\" successfully initialized", "green", false);
break;
case INCOMING_MESSAGE_IDS.ONDOUBLECLICK:
this.emit('click');
break;
case INCOMING_MESSAGE_IDS.PACKETINTERCEPT:
let stringMessage = packet.readLongString();
let hMessage = new HMessage(stringMessage);
this.#modifyMessage(hMessage);
let responsePacket = new HPacket(OUTGOING_MESSAGE_IDS.MANIPULATEDPACKET);
responsePacket.appendLongString(hMessage.stringify());
this.#gEarthExtensionServer.write(responsePacket.toBytes());
break;
case INCOMING_MESSAGE_IDS.UPDATEHOSTINFO:
this.#hostInfo = HostInfo.fromPacket(packet);
this.emit('hostinfoupdate', this.#hostInfo);
break;
}
}
#modifyMessage = (hMessage) => {
let hPacket = hMessage.getPacket();
let listeners = hMessage.getDestination() === HDirection.TOCLIENT ? this.#incomingMessageListeners : this.#outgoingMessageListeners;
let correctListeners = [];
if(listeners.has(-1)) {
for(let i = listeners.get(-1).length - 1; i >= 0; i--) {
correctListeners.push(listeners.get(-1)[i]);
}
}
if(listeners.has(hPacket.headerId())) {
for(let i = listeners.get(hPacket.headerId()).length - 1; i >= 0; i--) {
correctListeners.push(listeners.get(hPacket.headerId())[i]);
}
}
if(this.#packetInfoManager) {
let packetInfos = this.#packetInfoManager.getAllPacketInfoFromHeaderId(hMessage.getDestination(), hPacket.headerId());
let packetNames = [...new Set(packetInfos.map(p => p.name))];
let packetHashes = [...new Set(packetInfos.map(p => p.hash))];
for (let name of packetNames) {
if (listeners.has(name)) {
for (let i = listeners.get(name).length - 1; i >= 0; i--) {
correctListeners.push(listeners.get(name)[i]);
}
}
}
for (let hash of packetHashes) {
if (listeners.has(hash)) {
for (let i = listeners.get(hash).length - 1; i >= 0; i--) {
correctListeners.push(listeners.get(hash)[i]);
}
}
}
}
for(let i in correctListeners) {
hMessage.getPacket().resetReadIndex();
correctListeners[i](hMessage);
}
hMessage.getPacket().resetReadIndex();
}
sendToClient(packet) {
if(packet instanceof HPacket) {
return this.#send(packet, HDirection.TOCLIENT);
}
throw new Error("Extension.sendToClient: packet must be an instance of HPacket");
}
sendToServer(packet) {
if(packet instanceof HPacket) {
return this.#send(packet, HDirection.TOSERVER);
}
throw new Error("Extension.sendToServer: packet must be an instance of HPacket");
}
#send = (packet, direction) => {
if(packet.isCorrupted()) return false;
if(!packet.isPacketComplete()) packet.completePacket(this.#packetInfoManager);
if(!packet.isPacketComplete()) return false;
let sendingPacket = new HPacket(OUTGOING_MESSAGE_IDS.SENDMESSAGE)
.appendByte(direction)
.appendInt(packet.getBytesLength())
.appendBytes(packet.toBytes());
try {
this.#gEarthExtensionServer.write(sendingPacket.toBytes());
return true;
} catch {
return false;
}
}
requestFlags(flagRequestCallback) {
if(this.#flagRequestCallback !== null) return false;
this.#flagRequestCallback = flagRequestCallback;
try {
this.#gEarthExtensionServer.write(new HPacket(OUTGOING_MESSAGE_IDS.REQUESTFLAGS).toBytes());
return true;
} catch {
return false;
}
}
writeToConsole(s, colorClass) {
if(typeof colorClass === "undefined") {
colorClass = this.#hostInfo
&& this.#hostInfo.attributes.has('theme')
&& this.#hostInfo.attributes.get('theme').toLowerCase().includes('dark')
? "white" : "black";
}
if(typeof(colorClass) !== "string" || typeof(s) !== "string") {
throw new Error("Extensions.writeToConsole: Both s and colorClass have to be strings")
}
this.#writeToConsole(s, colorClass, true);
}
#writeToConsole = (s, colorClass, mentionTitle) => {
let text = "[" + colorClass + "]" + (mentionTitle ? this.#extensionInfo.name + " --> " : "") + s;
let packet = new HPacket(OUTGOING_MESSAGE_IDS.EXTENSIONCONSOLELOG);
packet.appendString(text);
try {
this.#gEarthExtensionServer.write(packet.toBytes());
} catch {}
}
interceptAll(direction, messageListener) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || typeof(messageListener) !== "function") {
throw new Error("Invalid arguments passed");
}
this.interceptByHeaderId(direction, -1, messageListener);
}
interceptByHeaderId(direction, headerId, messageListener) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || !Number.isInteger(headerId) || typeof(messageListener) !== "function") {
throw new Error("Invalid arguments passed");
}
let listeners = direction === HDirection.TOCLIENT ? this.#incomingMessageListeners : this.#outgoingMessageListeners;
if(!listeners.has(headerId)) {
listeners.set(headerId, []);
}
listeners.get(headerId).push(messageListener);
}
interceptByNameOrHash(direction, headerNameOrHash, messageListener) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || typeof(headerNameOrHash) !== "string" || typeof(messageListener) !== "function") {
throw new Error("Invalid arguments passed");
}
let listeners = direction === HDirection.TOCLIENT ? this.#incomingMessageListeners : this.#outgoingMessageListeners;
if(!listeners.has(headerNameOrHash)) {
listeners.set(headerNameOrHash, []);
}
listeners.get(headerNameOrHash).push(messageListener);
}
getPacketInfoManager() {
return this.#packetInfoManager;
}
getHostInfo() {
return this.#hostInfo;
}
}
@@ -0,0 +1,6 @@
export interface ExtensionInfo {
name: string,
description: string,
version: string,
author: string
}
@@ -0,0 +1,13 @@
export enum HActivityPoint {
DUCKET = 0,
NO_OP_1 = 1,
NO_OP_2 = 2,
NO_OP_3 = 3,
NO_OP_4 = 4,
NO_OP_5 = 5,
SEASONAL_1 = 1,
SEASONAL_2 = 2,
SEASONAL_3 = 3,
SEASONAL_4 = 4,
SEASONAL_5 = 5
}
@@ -0,0 +1,26 @@
/**
* Activity point currencies
* @readonly
* @enum {number}
*/
const HActivityPoint = Object.freeze({
DUCKET: 0,
NO_OP_1: 1,
NO_OP_2: 2,
NO_OP_3: 3,
NO_OP_4: 4,
NO_OP_5: 5,
SEASONAL_1: 1,
SEASONAL_2: 2,
SEASONAL_3: 3,
SEASONAL_4: 4,
SEASONAL_5: 5,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HActivityPoint };
@@ -0,0 +1,59 @@
import { HPacket } from "../../../protocol/hpacket.js";
export class HFrontPageItem {
#position;
#itemName;
#itemPromoImage;
#type;
#cataloguePageLocation = "";
#productOfferId = 0;
#productCode = "";
#expirationTime;
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFrontPageItem.constructor: packet must be an instance of HPacket");
}
[ this.#position, this.#itemName, this.#itemPromoImage, this.#type ]
= packet.read('iSSi');
switch (this.#type) {
case 0:
this.#cataloguePageLocation = packet.readString();
break;
case 1:
this.#productOfferId = packet.readInteger();
break;
case 2:
this.#productCode = packet.readString();
break;
}
this.#expirationTime = packet.readInteger();
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFrontPageItem.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iSSi',
this.#position, this.#itemName, this.#itemPromoImage, this.#type);
switch (this.#type) {
case 0:
packet.appendString(this.#cataloguePageLocation);
break;
case 1:
packet.appendInt(this.#productOfferId);
this.#productOfferId = packet.readInteger();
break;
case 2:
packet.appendString(this.#productCode)
break;
}
packet.appendInt(this.#expirationTime);
}
}
@@ -0,0 +1,45 @@
import { HPacket } from "../../../protocol/hpacket";
import { HActivityPoint } from "./hactivitypoint";
import { HProduct } from "./hproduct";
export class HOffer {
constructor(packet: HPacket);
appendToPacket(packet: HPacket): void;
get offerId(): number;
set offerId(val: number);
get localizationId(): string;
set localizationId(val: string);
get isRent(): boolean;
set isRent(val: boolean);
get priceInCredits(): number;
set priceInCredits(val: number);
get priceInActivityPoints(): number;
set priceInActivityPoints(val: number);
get activityPointType(): HActivityPoint;
set activityPointType(val: HActivityPoint);
get isGiftable(): boolean;
set isGiftable(val: boolean);
get products(): HProduct[];
set products(val: HProduct[]);
get clubLevel(): number;
set clubLevel(val: number);
get isBundlePurchaseAllowed(): boolean;
set isBundlePurchaseAllowed(val: boolean);
get isPet(): boolean;
set isPet(val: boolean);
get previewImage(): string;
set previewImage(val: string);
}
@@ -0,0 +1,196 @@
import { HPacket } from "../../../protocol/hpacket.js";
import { HProduct } from "./hproduct.js";
import { HActivityPoint } from "./hactivitypoint.js";
export class HOffer {
#offerId;
#localizationId;
#isRent;
#priceInCredits;
#priceInActivityPoints;
#activityPointType;
#isGiftable;
#products = [];
#clubLevel;
#isBundlePurchaseAllowed;
#isPet;
#previewImage;
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HOffer.constructor: packet must be an instance of HPacket");
}
[ this.#offerId, this.#localizationId, this.#isRent, this.#priceInCredits,
this.#priceInActivityPoints, this.#activityPointType, this.#isGiftable ]
= packet.read('iSBiiiB');
let productCount = packet.readInteger();
for (let i = 0; i < productCount; i++) {
this.#products.push(new HProduct(packet));
}
[ this.#clubLevel, this.#isBundlePurchaseAllowed, this.#isPet, this.#previewImage ]
= packet.read('iBBS');
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HOffer.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iSBiiiB',
this.#offerId, this.#localizationId, this.#isRent, this.#priceInCredits,
this.#priceInActivityPoints, this.#activityPointType, this.#isGiftable);
packet.appendInt(this.#products.length);
for (let product of this.#products) {
product.appendToPacket(packet);
}
packet.append('iBBS',
this.#clubLevel, this.#isBundlePurchaseAllowed, this.#isPet, this.#previewImage);
}
get offerId() {
return this.#offerId;
}
set offerId(val) {
if (!Number.isInteger(val)) {
throw new Error('HOffer.offerId: must be an integer');
}
this.#offerId = val;
}
get localizationId() {
return this.#localizationId;
}
set localizationId(val) {
if (typeof val !== 'string') {
throw new Error('HOffer.localizationId: must be a string');
}
this.#localizationId = val;
}
get isRent() {
return this.#isRent;
}
set isRent(val) {
if (typeof val !== 'boolean') {
throw new Error('HOffer.isRent: must be a boolean');
}
this.#isRent = val;
}
get priceInCredits() {
return this.#priceInCredits;
}
set priceInCredits(val) {
if (!Number.isInteger(val)) {
throw new Error('HOffer.priceInCredits: must be an integer');
}
this.#priceInCredits = val;
}
get priceInActivityPoints() {
return this.#priceInActivityPoints;
}
set priceInActivityPoints(val) {
if (!Number.isInteger(val)) {
throw new Error('HOffer.priceInActivityPoints: must be an integer');
}
this.#priceInActivityPoints = val;
}
get activityPointType() {
return this.#activityPointType;
}
set activityPointType(val) {
if (!HActivityPoint.identify(val)) {
throw new Error('HOffer.activityPointType: must be a value of HActivityPoint');
}
this.#activityPointType = val;
}
get isGiftable() {
return this.#isGiftable;
}
set isGiftable(val) {
if (typeof val !== 'boolean') {
throw new Error('HOffer.isGiftable: must be a boolean');
}
this.#isGiftable = val;
}
get products() {
return this.#products;
}
set products(val) {
if (!Array.isArray(val) || val.any(v => !(v instanceof HProduct)))
this.#products = val;
}
get clubLevel() {
return this.#clubLevel;
}
set clubLevel(val) {
if (!Number.isInteger(val)) {
throw new Error('HOffer.clubLevel: must be an integer');
}
this.#clubLevel = val;
}
get isBundlePurchaseAllowed() {
return this.#isBundlePurchaseAllowed;
}
set isBundlePurchaseAllowed(val) {
if (typeof val !== 'boolean') {
throw new Error('HOffer.isBundlePurchaseAllowed: must be a boolean');
}
this.#isBundlePurchaseAllowed = val;
}
get isPet() {
return this.#isPet;
}
set isPet(val) {
if (typeof val !== 'boolean') {
throw new Error('HOffer.isPet: must be a boolean');
}
this.#isPet = val;
}
get previewImage() {
return this.#previewImage;
}
set previewImage(val) {
if (typeof val !== 'string') {
throw new Error('HOffer.previewImage: must be a string');
}
this.#previewImage = val;
}
}
@@ -0,0 +1,33 @@
import { HPacket } from "../../../protocol/hpacket";
import { HProductType } from "../hproducttype";
export class HProduct {
constructor(packet: HPacket);
/**
* Append product to a packet
* @param packet Packet to be appended to
*/
appendToPacket(packet: HPacket): void;
get productType(): HProductType;
set productType(val: HProductType);
get furniClassId(): number;
set furniClassId(val: number);
get extraParam(): string;
set extraParam(val: string);
get productCount(): number;
set productCount(val: number);
get isUniqueLimitedItem(): boolean;
set isUniqueLimitedItem(val: boolean);
get uniqueLimitedItemSeriesSize(): number;
set uniqueLimitedItemSeriesSize(val: number);
get uniqueLimitedItemsLeft(): number;
set uniqueLimitedItemsLeft(val: number);
}
@@ -0,0 +1,146 @@
import { HProductType } from "../hproducttype.js";
import { HPacket } from "../../../protocol/hpacket.js";
import util from "util";
export class HProduct {
#productType;
#furniClassId = 0;
#extraParam;
#productCount = 0;
#uniqueLimitedItem = false;
#uniqueLimitedItemSeriesSize = 0;
#uniqueLimitedItemsLeft = 0;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HProductType {\n`
+ `${HProductType.identify(this.#productType) ? `${indent} productType: HProductType.\x1b[36m${HProductType.identify(this.#productType)}\x1b[0m\n` : ''}`
+ `${this.#productType !== HProductType.Badge ? `${indent} furniClassId: ${util.inspect(this.#furniClassId, {colors: true})}\n` : ''}`
+ `${indent} extraParam: ${util.inspect(this.#extraParam, {colors: true})}\n`
+ `${this.#productType !== HProductType.Badge ? `${indent} productCount: ${util.inspect(this.#productCount, {colors: true})}\n` : ''}`
+ `${this.#productType !== HProductType.Badge ? `${indent} uniqueLimitedItem: ${util.inspect(this.#uniqueLimitedItem, {colors: true})}\n` : ''}`
+ `${this.#productType !== HProductType.Badge && this.#uniqueLimitedItem ? `${indent} uniqueLimitedItemSeriesSize: ${util.inspect(this.#uniqueLimitedItemSeriesSize, {colors: true})}\n` : ''}`
+ `${this.#productType !== HProductType.Badge && this.#uniqueLimitedItem ? `${indent} uniqueLimitedItemsLeft: ${util.inspect(this.#uniqueLimitedItemsLeft, {colors: true})}\n` : ''}`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HProduct.constructor: packet must be an instance of HPacket");
}
this.#productType = packet.readString().toUpperCase();
if (this.#productType !== HProductType.Badge) {
[ this.#furniClassId, this.#extraParam, this.#productCount, this.#uniqueLimitedItem ]
= packet.read('iSiB');
if (this.#uniqueLimitedItem) {
[ this.#uniqueLimitedItemSeriesSize, this.#uniqueLimitedItemsLeft ]
= packet.read('ii');
}
} else {
this.#extraParam = packet.readString();
}
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HProduct.appendToPacket: packet must be an instance of HPacket");
}
packet.appendString(this.#productType);
if (this.#productType !== HProductType.Badge) {
packet.append('iSiB',
this.#furniClassId, this.#extraParam, this.#productCount, this.#uniqueLimitedItem);
if (this.#uniqueLimitedItem) {
packet.append('ii',
this.#uniqueLimitedItemSeriesSize, this.#uniqueLimitedItemsLeft);
}
} else {
packet.appendString(this.#extraParam);
}
}
get productType() {
return this.#productType;
}
set productType(val) {
if(!HProductType.identify(val)) {
throw new Error('HProduct.productType: must be a value of HProductType')
}
this.#productType = val;
}
get furniClassId() {
return this.#furniClassId;
}
set furniClassId(val) {
if(!Number.isInteger(val)) {
throw new Error('HProduct.furniClassId: must be an integer');
}
this.#furniClassId = val;
}
get extraParam() {
return this.#extraParam;
}
set extraParam(val) {
if(typeof val != 'string') {
throw new Error('HProduct.extraParam: must be a string');
}
this.#extraParam = val;
}
get productCount() {
return this.#productCount;
}
set productCount(val) {
if(!Number.isInteger(val)) {
throw new Error('HProduct.productCount: must be an integer');
}
this.#productCount = val;
}
get isUniqueLimitedItem() {
return this.#uniqueLimitedItem;
}
set isUniqueLimitedItem(val) {
if(typeof val !== 'boolean') {
throw new Error('HProduct.isUniqueLimitedItem: must be a boolean');
}
this.#uniqueLimitedItem = val;
}
get uniqueLimitedItemSeriesSize() {
return this.#uniqueLimitedItemSeriesSize;
}
set uniqueLimitedItemSeriesSize(val) {
if(!Number.isInteger(val)) {
throw new Error('HProduct.uniqueLimitedItemSeriesSize: must be an integer');
}
this.#uniqueLimitedItemSeriesSize = val;
}
get uniqueLimitedItemsLeft() {
return this.#uniqueLimitedItemSeriesSize;
}
set uniqueLimitedItemsLeft(val) {
if(!Number.isInteger(val)) {
throw new Error('HProduct.uniqueLimitedItemSeriesSize: must be an integer');
}
this.#uniqueLimitedItemSeriesSize = val;
}
}
@@ -0,0 +1,7 @@
export enum HAction {
None,
Move,
Sit,
Lay,
Sign
}
@@ -0,0 +1,19 @@
/**
* Performable entity actions
* @readonly
* @enum {number}
*/
const HAction = Object.freeze({
None: 0,
Move: 1,
Sit: 2,
Lay: 3,
Sign: 4,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HAction };
@@ -0,0 +1,146 @@
import { HPacket } from "../../protocol/hpacket";
import { HPoint } from "./hpoint";
import { HEntityType } from "./hentitytype";
import { HGender } from "./hgender";
import { HEntityUpdate } from "./hentityupdate";
export class HEntity {
constructor(packet: HPacket);
/**
* Parse all HEntities from packet
* @param packet
*/
static parse(packet: HPacket): HEntity[];
/**
* Append entity to a packet
* @param packet Packet to be appended to
*/
appendToPacket(packet: HPacket): void;
/**
* Construct packet with header id containing all entities
* @param entities Entities to add to packet
* @param headerId Header id of packet
*/
static constructPacket(entities: HEntity[], headerId: number): HPacket;
/**
* Try performing an entity update on entity
* @param update entity update to try
*/
tryUpdate(update: HEntityUpdate): boolean;
/**
* Get id from entity
*/
get id(): number;
/**
* Set id from entity
*/
set id(val: number);
/**
* Get entity index in room
*/
get index(): number;
/**
* Set entity index in room
*/
set index(val: number);
/**
* Get entity position in room
*/
get tile(): HPoint;
/**
* Set entity position in room
*/
set tile(val: HPoint);
/**
* Get entity name
*/
get name(): string;
/**
* Set entity name
*/
set name(val: string);
/**
* Get entity motto
*/
get motto(): string;
/**
* Set entity motto
*/
set motto(val: string);
/**
* Get entity gender
*/
get gender(): HGender | null;
/**
* Set entity gender
*/
set gender(val: HGender | null);
/**
* Get entity type
*/
get entityType(): HEntityType;
/**
* Set entity type
*/
set entityType(val: HEntityType);
/**
* Get figure
*/
get figureId(): string;
/**
* Set figure
*/
set figureId(val: string);
/**
* Get favorite group
*/
get favoriteGroup(): string | null;
/**
* Set favorite group
*/
set favoriteGroup(val: string | null);
/**
* Get last entity update
*/
get lastUpdate(): HEntityUpdate | null;
/**
* Set last entity update
*/
set lastUpdate(val: HEntityUpdate | null);
/**
* Get stuff
*/
get stuff(): any[];
/**
* Set stuff
*/
set stuff(val: any[]);
}
@@ -0,0 +1,350 @@
import { HPoint } from "./hpoint.js";
import { HPacket } from "../../protocol/hpacket.js";
import { HFacing } from "./hfacing.js";
import { HGender } from "./hgender.js";
import { HEntityType } from "./hentitytype.js";
import { HEntityUpdate } from "./hentityupdate.js";
import util from "util";
export class HEntity {
#id;
#index;
#tile;
#bodyFacing;
#headFacing;
#name;
#motto;
#gender = null;
#entityType;
#figureId;
#favoriteGroup = null;
#lastUpdate = null;
#stuff = [];
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HEntity {\n${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} index: ${util.inspect(this.#index, {colors: true})}\n`
+ `${indent} tile: ${util.inspect(this.#tile, false, depth + 1)}\n`
+ `${HFacing.identify(this.#bodyFacing) ? `${indent} bodyFacing: HFacing.\x1b[36m${HFacing.identify(this.#bodyFacing)}\x1b[0m\n` : ''}`
+ `${HFacing.identify(this.#headFacing) ? `${indent} headFacing: HFacing.\x1b[36m${HFacing.identify(this.#headFacing)}\x1b[0m\n` : ''}`
+ `${indent} name: ${util.inspect(this.#name, {colors: true})}\n`
+ `${indent} motto: ${util.inspect(this.#motto, {colors: true})}\n`
+ `${HGender.identify(this.#gender) ? `${indent} gender: HGender.\x1b[36m${HGender.identify(this.#gender)}\x1b[0m\n` : ''}`
+ `${HEntityType.identify(this.#entityType) ? `${indent} entityType: HEntityType.\x1b[36m${HEntityType.identify(this.#entityType)}\x1b[0m\n` : ''}`
+ `${indent} figureId: ${util.inspect(this.#figureId, {colors: true})}\n`
+ `${this.#favoriteGroup != null ? `${indent} favoriteGroup: ${util.inspect(this.#favoriteGroup, {colors: true})}\n` : ''}`
+ `${this.#lastUpdate != null ? `${indent} lastUpdate: ${util.inspect(this.#lastUpdate, false, depth + 1)}\n` : ''}`
+ `${indent} stuff: ${util.inspect(this.#stuff, {colors: true})}\n`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HEntity.constructor: packet must be an instance of HPacket");
}
[ this.#id, this.#name, this.#motto, this.#figureId, this.#index ] = packet.read('iSSSi');
this.#tile = new HPoint(packet.readInteger(), packet.readInteger(), Number.parseFloat(packet.readString()));
[ this.#bodyFacing, this.#entityType ] = packet.read('ii');
this.#headFacing = this.#bodyFacing;
switch(this.#entityType) {
case HEntityType.HABBO:
this.#gender = packet.readString().toUpperCase();
this.#stuff.push(...packet.read('ii'));
this.#favoriteGroup = packet.readString();
this.#stuff.push(...packet.read('SiB'));
break;
case HEntityType.PET:
this.#stuff.push(...packet.read('iiSiBBBBBBiS'));
break;
case HEntityType.BOT:
this.#stuff.push(...packet.read('SiS'));
let n = packet.readInteger();
let list = [];
for(let i = 0; i < n; i++) {
list.push(packet.readShort());
}
this.#stuff.push(list);
break;
}
}
static parse(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HEntity.parse: packet must be an instance of HPacket");
}
let entities = [];
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
entities.push(new HEntity(packet));
}
return entities;
}
static constructPacket(entities, headerId) {
if(!Array.isArray(entities)) {
throw new Error("HEntity.constructPacket: entities must be an array of HEntity instances");
}
if(!Number.isInteger(headerId)) {
throw new Error("HEntity.constructPacket: headerId must be an integer");
}
let packet = new HPacket(headerId)
.appendInt(entities.length);
for(let entity of entities) {
if(!(entity instanceof HEntity)) {
throw new Error("HEntity.constructPacket: entities must be an array of HEntity instances");
}
entity.appendToPacket(packet);
}
return packet;
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HEntity.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iSSSiiiSii',
this.#id,
this.#name,
this.#motto,
this.#figureId,
this.#index,
this.#tile.x,
this.#tile.y,
`${this.#tile.z}`,
this.#bodyFacing,
this.#entityType);
switch(this.#entityType) {
case HEntityType.HABBO:
packet.append('SiiSSiB',
this.#gender.toLowerCase(),
this.#stuff[0],
this.#stuff[1],
this.#favoriteGroup,
this.#stuff[2],
this.#stuff[3],
this.#stuff[4]);
break;
case HEntityType.PET:
packet.append('iiSiBBBBBBiS', ...this.#stuff);
break;
case HEntityType.BOT:
packet.append('SiSi',
...this.#stuff.slice(0, 3),
this.#stuff[3].length);
for(let i = 0; i < this.#stuff[3].length; i++) {
packet.appendShort(this.#stuff[3][i]);
}
break;
}
}
tryUpdate(update) {
if (!(update instanceof HEntityUpdate)) {
throw new Error("HEntity.update: update must be an instance op HEntityUpdate");
}
if (this.#index !== update.index) return false;
this.#tile = update.tile;
this.#lastUpdate = update;
return true;
}
getId() {
console.error("\x1b[31mHEntity.getId(): Deprecated method used, use the getter HEntity.id instead\x1b[0m");
return this.#id;
}
get id() {
return this.#id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error('HEntity.id: must be an integer')
}
this.#id = val;
}
getIndex() {
console.error("\x1b[31mHEntity.getIndex(): Deprecated method used, use the getter HEntity.index instead\x1b[0m");
return this.#index;
}
get index() {
return this.#index;
}
set index(val) {
if(!Number.isInteger(val)) {
throw new Error('HEntity.index: must be an integer');
}
this.#index = val;
}
getTile() {
console.error("\x1b[31mHEntity.getTile(): Deprecated method used, use the getter HEntity.tile instead\x1b[0m");
return this.#tile;
}
get tile() {
return this.#tile;
}
set tile(val) {
if(!(val instanceof HPoint)) {
throw new Error('HEntity.tile: must be an instance of HPoint');
}
this.#tile = val;
}
getName() {
console.error("\x1b[31mHEntity.getName(): Deprecated method used, use the getter HEntity.name instead\x1b[0m");
return this.#name;
}
get name() {
return this.#name;
}
set name(val) {
if(typeof val != 'string') {
throw new Error('HEntity.name: must be a string');
}
this.#name = val;
}
getMotto() {
console.error("\x1b[31mHEntity.getMotto(): Deprecated method used, use the getter HEntity.motto instead\x1b[0m");
return this.#motto;
}
get motto() {
return this.#motto;
}
set motto(val) {
if(typeof val != 'string') {
throw new Error('HEntity.motto: must be a string');
}
this.#motto = val;
}
getGender() {
console.error("\x1b[31mHEntity.getGender(): Deprecated method used, use the getter HEntity.gender instead\x1b[0m");
return this.#gender;
}
get gender() {
return this.#gender;
}
set gender(val) {
if(!HGender.identify(val) && val != null) {
throw new Error('HEntity.gender: must be a value of HGender or null')
}
this.#gender = val;
}
getEntityType() {
console.error("\x1b[31mHEntity.getEntityType(): Deprecated method used, use the getter HEntity.entityType instead\x1b[0m");
return this.#entityType;
}
get entityType() {
return this.#entityType;
}
set entityType(val) {
if(!HEntityType.identify(val)) {
throw new Error('HEntity.entityType: must be a value of HEntityType')
}
this.#entityType = val;
}
getFigureId() {
console.error("\x1b[31mHEntity.getFigureId(): Deprecated method used, use the getter HEntity.figureId instead\x1b[0m");
return this.#figureId;
}
get figureId() {
return this.#figureId;
}
set figureId(val) {
if(typeof val != 'string') {
throw new Error('HEntity.figureId: must be a string');
}
this.#figureId = val;
}
getFavoriteGroup() {
console.error("\x1b[31mHEntity.getFavoriteGroup(): Deprecated method used, use the getter HEntity.favoriteGroup instead\x1b[0m");
return this.#favoriteGroup;
}
get favoriteGroup() {
return this.#favoriteGroup;
}
set favoriteGroup(val) {
if(typeof val != 'string' && val != null) {
throw new Error('HEntity.favoriteGroup: must be a string or null');
}
this.#favoriteGroup = val;
}
getLastUpdate() {
console.error("\x1b[31mHEntity.getLastUpdate(): Deprecated method used, use the getter HEntity.lastUpdate instead\x1b[0m");
return this.#lastUpdate;
}
get lastUpdate() {
return this.#lastUpdate;
}
set lastUpdate(val) {
if(!(val instanceof HEntityUpdate) && val != null) {
throw new Error('HEntity.lastUpdate: must be an instance of HEntityUpdate or null');
}
this.#lastUpdate = val;
}
getStuff() {
console.error("\x1b[31mHEntity.getStuff(): Deprecated method used, use the getter HEntity.stuff instead\x1b[0m");
return this.#stuff;
}
get stuff() {
return this.#stuff;
}
set stuff(val) {
if(!Array.isArray(val)) {
throw new Error('HEntity.stuff: must be an array');
}
this.#stuff = val;
}
}
@@ -0,0 +1,6 @@
export enum HEntityType {
HABBO = 1,
PET,
OLD_BOT,
BOT
}
@@ -0,0 +1,18 @@
/**
* Entity types
* @readonly
* @enum {number}
*/
const HEntityType = Object.freeze({
HABBO: 1,
PET: 2,
OLD_BOT: 3,
BOT: 4,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HEntityType };
@@ -0,0 +1,106 @@
import { HPacket } from "../../protocol/hpacket";
import { HPoint } from "./hpoint";
import { HSign } from "./hsign";
import { HStance } from "./hstance";
import { HAction } from "./haction";
import { HFacing } from "./hfacing";
export class HEntityUpdate {
constructor(packet: HPacket);
/**
* Parse all HEntityUpdates from packet
* @param packet Packet to parse
*/
static parse(packet: HPacket): HEntityUpdate[];
/**
* Get user index of update
*/
get index(): number;
/**
* Set user index of update
*/
set index(val: number);
/**
* Check is user has room rights
*/
get isController(): boolean;
/**
* Set if user has room rights
*/
set isController(val: boolean);
/**
* Get current tile position of entity
*/
get tile(): HPoint;
/**
* Set current tile position of entity
*/
set tile(val: HPoint);
/**
* Get tile position where entity is moving towards
*/
get movingTo(): HPoint | null;
/**
* Set tile position where entity is moving towards
*/
set movingTo(val: HPoint | null);
/**
* Get sign entity is holding
*/
get sign(): HSign | null;
/**
* Set sign entity is holding
*/
set sign(val: HSign | null);
/**
* Get stance of entity
*/
get stance(): HStance | null;
/**
* Set stance of entity
*/
set stance(val: HStance | null);
/**
* Get action that entity is doing
*/
get action(): HAction | null;
/**
* Set action that entity is doing
*/
set action(val: HAction | null);
/**
* Get direction in which the entity's head is facing
*/
get headFacing(): HFacing;
/**
* Set direction in which the entity's head is facing
*/
set headFacing(val: HFacing);
/**
* Get direction in which the entity's body is facing
*/
get bodyFacing(): HFacing;
/**
* Set direction in which the entity's body is facing
*/
set bodyFacing(val: HFacing);
}
@@ -0,0 +1,245 @@
import { HPoint } from "./hpoint.js";
import { HPacket } from "../../protocol/hpacket.js";
import { HFacing } from "./hfacing.js";
import { HSign } from "./hsign.js";
import { HStance } from "./hstance.js";
import { HAction } from "./haction.js";
import util from "util";
export class HEntityUpdate {
#index;
#isController = false;
#tile;
#movingTo = null;
#sign = null;
#stance = null;
#action = null;
#headFacing;
#bodyFacing;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HEntityUpdate {\n`
+ `${indent} index: ${util.inspect(this.#index, {colors: true})}\n`
+ `${indent} isController: ${util.inspect(this.#isController, {colors: true})}\n`
+ `${indent} tile: ${util.inspect(this.#tile, false, depth + 1)}\n`
+ `${this.#movingTo != null ? `${indent} movingTo: ${util.inspect(this.#movingTo, false, depth + 1)}\n` : ''}`
+ `${HSign.identify(this.#sign) ? `${indent} sign: HSign.\x1b[36m${HSign.identify(this.#sign)}\x1b[0m\n` : ''}`
+ `${HStance.identify(this.#stance) ? `${indent} stance: HStance.\x1b[36m${HStance.identify(this.#stance)}\x1b[0m\n` : ''}`
+ `${HAction.identify(this.#action) ? `${indent} action: HAction.\x1b[36m${HAction.identify(this.#action)}\x1b[0m\n` : ''}`
+ `${HFacing.identify(this.#bodyFacing) ? `${indent} bodyFacing: HFacing.\x1b[36m${HFacing.identify(this.#bodyFacing)}\x1b[0m\n` : ''}`
+ `${HFacing.identify(this.#headFacing) ? `${indent} headFacing: HFacing.\x1b[36m${HFacing.identify(this.#headFacing)}\x1b[0m\n` : ''}`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HEntityUpdate.constructor: packet must be an instance of HPacket");
}
this.#index = packet.readInteger();
this.#tile = new HPoint(packet.readInteger(), packet.readInteger(), Number.parseFloat(packet.readString()));
let action;
[ this.#headFacing, this.#bodyFacing, action ] = packet.read('iiS');
let actionData = action.split("/");
for(let actionInfo of actionData) {
let actionValues = actionInfo.split(" ");
if(actionValues.length < 2) continue;
if(actionValues[0] === "") continue;
switch(actionValues[0]) {
case "flatctrl":
this.#isController = true;
this.action = HAction.None;
break;
case "mv":
let values = actionValues[1].split(",");
if(values.length >= 3)
this.#movingTo = new HPoint(Number.parseInt(values[0]), Number.parseInt(values[1]), Number.parseFloat(values[2]));
this.#action = HAction.Move;
break;
case "sit":
this.#action = HAction.Sit;
this.#stance = HAction.Sit;
break;
case "sign":
this.#sign = Number.parseInt(actionValues[1]);
this.#action = HAction.Sign;
break;
}
}
}
static parse(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HEntityUpdate.parse: packet must be an instance of HPacket");
}
packet.resetReadIndex();
let updates = [];
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
updates.push(new HEntityUpdate(packet));
}
return updates;
}
getIndex() {
console.error("\x1b[31mHEntityUpdate.getIndex(): Deprecated method used, use the getter HEntity.index instead\x1b[0m");
return this.#index;
}
isController() {
console.error("\x1b[31mHEntityUpdate.isController(): Deprecated method used, use the getter HEntity.isController instead\x1b[0m");
return this.#isController;
}
getTile() {
console.error("\x1b[31mHEntityUpdate.getTile(): Deprecated method used, use the getter HEntity.tile instead\x1b[0m");
return this.#tile;
}
getMovingTo() {
console.error("\x1b[31mHEntityUpdate.getMovingTo(): Deprecated method used, use the getter HEntity.movingTo instead\x1b[0m");
return this.#movingTo;
}
getSign() {
console.error("\x1b[31mHEntityUpdate.getSign(): Deprecated method used, use the getter HEntity.sign instead\x1b[0m");
return this.#sign;
}
getStance() {
console.error("\x1b[31mHEntityUpdate.getStance(): Deprecated method used, use the getter HEntity.stance instead\x1b[0m");
return this.#stance;
}
getAction() {
console.error("\x1b[31mHEntityUpdate.getAction(): Deprecated method used, use the getter HEntity.action instead\x1b[0m");
return this.#action;
}
getHeadFacing() {
console.error("\x1b[31mHEntityUpdate.getHeadFacing(): Deprecated method used, use the getter HEntity.headFacing instead\x1b[0m");
return this.#headFacing;
}
getBodyFacing() {
console.error("\x1b[31mHEntityUpdate.getBodyFacing(): Deprecated method used, use the getter HEntity.bodyFacing instead\x1b[0m");
return this.#bodyFacing;
}
get index() {
return this.#index;
}
set index(val) {
if(!Number.isInteger(val)) {
throw new Error('HEntityUpdate.index: must be an integer')
}
this.#index = val;
}
get isController() {
return this.#isController;
}
set isController(val) {
if(typeof val != 'boolean') {
throw new Error('HEntityUpdate.isController: must be a boolean')
}
this.#isController = val;
}
get tile() {
return this.#tile;
}
set tile(val) {
if(!(val instanceof HPoint)) {
throw new Error('HEntityUpdate.tile: must be an instance of HPoint')
}
this.#tile = val
}
get movingTo() {
return this.#movingTo;
}
set movingTo(val) {
if(!(val instanceof HPoint) && val != null) {
throw new Error('HEntityUpdate.movingTo: must be an instance of HPoint or null')
}
this.#movingTo = val
}
get sign() {
return this.#sign;
}
set sign(val) {
if(!HSign.identify(val) && val != null) {
throw new Error('HEntityUpdate.sign: must be a value of HSign or null')
}
this.#sign = val
}
get stance() {
return this.#stance;
}
set stance(val) {
if(!HStance.identify(val) && val != null) {
throw new Error('HEntityUpdate.stance: must be a value of HStance or null')
}
this.#stance = val
}
get action() {
return this.#action;
}
set action(val) {
if(!HAction.identify(val) && val != null) {
throw new Error('HEntityUpdate.action: must be a value of HAction or null')
}
this.#action = val
}
get headFacing() {
return this.#headFacing;
}
set headFacing(val) {
if(!HFacing.identify(val)) {
throw new Error('HEntityUpdate.headFacing: must be a value of HFacing')
}
this.#headFacing = val
}
get bodyFacing() {
return this.#bodyFacing;
}
set bodyFacing(val) {
if(!HFacing.identify(val)) {
throw new Error('HEntityUpdate.bodyFacing: must be a value of HFacing')
}
this.#bodyFacing = val
}
}
@@ -0,0 +1,10 @@
export enum HFacing {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest
}
@@ -0,0 +1,22 @@
/**
* Possible facing directions
* @readonly
* @enum {number}
*/
const HFacing = Object.freeze({
North: 0,
NorthEast: 1,
East: 2,
SouthEast: 3,
South: 4,
SouthWest: 5,
West: 6,
NorthWest: 7,
identify(val) {
for (let key in this)
if (this[key] === val)
return key;
}
});
export { HFacing };
@@ -0,0 +1,170 @@
import { HPacket } from "../../protocol/hpacket";
import { HFacing } from "./hfacing";
import { HPoint } from "./hpoint";
export class HFloorItem {
constructor(packet: HPacket);
/**
* Append floor item to a packet
* @param packet Packet to be appended to
*/
appendToPacket(packet: HPacket): void;
/**
* Parse all floor items from a packet
* @param packet Packet to parse from
*/
static parse(packet: HPacket): HFloorItem[];
/**
* Construct packet with header id containing all floor items
* @param floorItems Floor items to add to packet
* @param headerId Header id of packet
*/
static constructPacket(floorItems: HFloorItem[], headerId: number): HPacket;
/**
* Get id of floor item
*/
getId(): number;
/**
* Get type id of floor item
*/
get typeId(): number;
/**
* Get usage policy of floor item
*/
get usagePolicy(): number;
/**
* Get owner id of floor item
*/
get ownerId(): number;
/**
* Get owner name of floor item
*/
get ownerName(): string;
/**
* Get seconds to expiration
*/
get secondsToExpiration(): number;
/**
* Get stuff category of floor item
*/
get stuffCategory(): number;
/**
* Get direction in which floor item is facing
*/
get facing(): HFacing;
/**
* Get position of floor item
*/
get tile(): HPoint;
/**
* Get sizeZ of floor item
*/
get sizeZ(): number;
/**
* Get extra of floor item
*/
get extra(): number;
/**
* Get stuff of floor item
*/
get stuff(): any[];
/**
* Get staticClass of floor item
*/
get staticClass(): string | undefined;
/**
* Set owner name of floor item
* @param val Owner name to be set
*/
set ownerName(val: String);
/**
* Set id of floor item
* @param val Id to be set
*/
set id(val: number);
/**
* Set type id of floor item
* @param val Type id to be set
*/
set typeId(val: number);
/**
* Set position of floor item
* @param val Position to be set
*/
set tile(val: HPoint);
/**
* Set sizeZ of floor item
* @param val Value to be set
*/
set sizeZ(val: number);
/**
* Set extra of floor item
* @param val Value to be set
*/
set extra(val: number);
/**
* Set direction in which floor item is facing
* @param val Direction to set
*/
set facing(val: HFacing);
/**
* Set stuff category of floor item
* @param val Category to set
*/
set stuffCategory(val: number);
/**
* Set seconds to expiration of floor item
* @param val Seconds to expiration to be set
*/
set secondsToExpiration(val: number);
/**
* Set usage policy of floor item
* @param val Usage policy to be set
*/
set usagePolicy(val: number);
/**
* Set owner id of floor item
* @param val Owner id to be set
*/
set ownerId(val: number);
/**
* Set stuff of floor item
* @param val Stuff to be set
*/
set stuff(val: any[]);
/**
* Set static class of floor item
* @param val Value to be set
*/
set staticClass(val: string | undefined);
}
@@ -0,0 +1,441 @@
import { HFacing } from './hfacing.js';
import util from "util";
import { HPacket } from "../../protocol/hpacket.js";
import { HPoint } from "./hpoint.js";
import { HStuff } from "./hstuff.js";
export class HFloorItem {
#id;
#typeId;
#tile;
#sizeZ;
#extra;
#facing;
#stuffCategory;
#secondsToExpiration;
#usagePolicy;
#ownerId;
#ownerName;
#stuff;
#staticClass = undefined;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HFloorItem {\n`
+ `${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} typeId: ${util.inspect(this.#typeId, {colors: true})}\n`
+ `${indent} tile: ${util.inspect(this.#tile, false, depth + 1)}\n`
+ `${indent} sizeZ: ${util.inspect(this.#sizeZ, {colors: true})}\n`
+ `${indent} extra: ${util.inspect(this.#extra, {colors: true})}\n`
+ `${HFacing.identify(this.#facing) ? `${indent} facing: HFacing.\x1b[36m${HFacing.identify(this.#facing)}\x1b[0m\n` : ''}`
+ `${indent} category: ${util.inspect(this.#stuffCategory, {colors: true})}\n`
+ `${indent} secondsToExpiration: ${util.inspect(this.#secondsToExpiration, {colors: true})}\n`
+ `${indent} usagePolicy: ${util.inspect(this.#usagePolicy, {colors: true})}\n`
+ `${indent} ownerId: ${util.inspect(this.#ownerId, {colors: true})}\n`
+ `${indent} ownerName: ${util.inspect(this.#ownerName, {colors: true})}\n`
+ `${indent} stuff: ${util.inspect(this.#stuff, {colors: true})}\n`
+ `${this.#staticClass ? `${indent} staticClass: ${util.inspect(this.#staticClass, {colors: true})}\n` : ''}`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFloorItem.constructor: Invalid argument(s) passed");
}
let x, y;
[ this.#id, this.#typeId, x, y, this.#facing ] = packet.read('iiiii');
this.#tile = new HPoint(x, y, Number.parseFloat(packet.readString()));
this.#sizeZ = Number.parseFloat(packet.readString());
[ this.#extra, this.#stuffCategory ] = packet.read('ii');
this.#stuff = HStuff.readData(packet, this.#stuffCategory);
[ this.#secondsToExpiration, this.#usagePolicy, this.#ownerId ] = packet.read('iii');
if(this.#typeId < 0) {
this.#staticClass = packet.readString();
}
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFloorItem.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iiiiiSSii',
this.#id,
this.#typeId,
this.#tile.x,
this.#tile.y,
this.#facing,
`${this.#tile.z}`,
`${this.#sizeZ}`,
this.#extra,
this.#stuffCategory);
HStuff.appendData(packet, this.#stuffCategory, this.#stuff);
packet.append('iii',
this.#secondsToExpiration,
this.#usagePolicy,
this.#ownerId);
if(this.#typeId < 0) {
packet.appendString(this.#staticClass || "");
}
}
static parse(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFloorItem.parse: packet must be an instance of HPacket");
}
packet.resetReadIndex();
let ownersCount = packet.readInteger();
let owners = new Map();
for(let i = 0; i < ownersCount; i++) {
owners.set(...packet.read('iS'))
}
let furniture = [];
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
let furni = new HFloorItem(packet);
furni.#ownerName = owners.get(furni.#ownerId);
furniture.push(furni);
}
return furniture;
}
static constructPacket(floorItems, headerId) {
if(!(Array.isArray(floorItems) || !Number.isInteger(headerId))) {
throw new Error("HFloorItem.constructPacket: headerId must be an integer");
}
if(!(Array.isArray(floorItems))) {
throw new Error("HFloorItem.constructPacket: floorItems must be an array of HFloorItem instances");
}
let owners = new Map();
for(let floorItem of floorItems) {
if(!(floorItem instanceof HFloorItem)) {
throw new Error("HFloorItem.constructPacket: floorItems must be an array of HFloorItem instances");
}
owners.set(floorItem.#ownerId, floorItem.#ownerName);
}
let packet = new HPacket(headerId);
packet.appendInt(owners.size);
for(let ownerEntry of Array.from(owners.entries())) {
packet.append('iS', ...ownerEntry);
}
packet.appendInt(floorItems.length);
for(let floorItem of floorItems) {
floorItem.appendToPacket(packet);
}
return packet;
}
getId() {
console.error("\x1b[31mHFloorItem.getId(): Deprecated method used, use the getter HFloorItem.id instead\x1b[0m");
return this.#id;
}
get id() {
return this.#id;
}
getTypeId() {
console.error("\x1b[31mHFloorItem.getTypeId(): Deprecated method used, use the getter HFloorItem.typeId instead\x1b[0m");
return this.#typeId;
}
get typeId() {
return this.#typeId;
}
getUsagePolicy() {
console.error("\x1b[31mHFloorItem.getUsagePolicy(): Deprecated method used, use the getter HFloorItem.usagePolicy instead\x1b[0m");
return this.#usagePolicy;
}
get usagePolicy() {
return this.#usagePolicy;
}
getOwnerId() {
console.error("\x1b[31mHFloorItem.getOwnerId(): Deprecated method used, use the getter HFloorItem.ownerId instead\x1b[0m");
return this.#ownerId;
}
get ownerId() {
return this.#ownerId;
}
getOwnerName() {
console.error("\x1b[31mHFloorItem.getOwnerName(): Deprecated method used, use the getter HFloorItem.ownerName instead\x1b[0m");
return this.#ownerName;
}
get ownerName() {
return this.#ownerName;
}
getSecondsToExpiration() {
console.error("\x1b[31mHFloorItem.getSecondsToExpiration(): Deprecated method used, use the getter HFloorItem.secondsToExpiration instead\x1b[0m");
return this.#secondsToExpiration;
}
get secondsToExpiration() {
return this.#secondsToExpiration;
}
getCategory() {
console.error("\x1b[31mHFloorItem.getCategory(): Deprecated method used, use the getter HFloorItem.category instead\x1b[0m");
return this.#stuffCategory;
}
get category() {
return this.#stuffCategory;
}
get sizeZ() {
return this.#sizeZ;
}
get extra() {
return this.#extra;
}
getFacing() {
console.error("\x1b[31mHFloorItem.getFacing(): Deprecated method used, use the getter HFloorItem.facing instead\x1b[0m");
return this.#facing;
}
get facing() {
return this.#facing;
}
getTile() {
console.error("\x1b[31mHFloorItem.getTile(): Deprecated method used, use the getter HFloorItem.tile instead\x1b[0m");
return this.#tile;
}
get tile() {
return this.#tile;
}
getStuff() {
console.error("\x1b[31mHFloorItem.getStuff(): Deprecated method used, use the getter HFloorItem.stuff instead\x1b[0m");
return this.#stuff;
}
get stuff() {
return this.#stuff;
}
get staticClass() {
return this.#staticClass;
}
setOwnerName(ownerName) {
console.error("\x1b[31mHFloorItem.setOwnerName(): Deprecated method used, use the setter HFloorItem.ownerName = ... instead\x1b[0m");
if(typeof ownerName !== 'string') {
throw new Error("HFloorItem.setOwnerName: ownerName must be a string");
}
this.#ownerName = ownerName;
}
set ownerName(val) {
if(typeof val !== 'string') {
throw new Error("HFloorItem.ownerName: must be a string");
}
this.#ownerName = val;
}
setId(id) {
console.error("\x1b[31mHFloorItem.setId(): Deprecated method used, use the setter HFloorItem.id = ... instead\x1b[0m");
if(!Number.isInteger(id)) {
throw new Error("HFloorItem.setId: id must be an integer");
}
this.#id = id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error("HFloorItem.id: must be an integer");
}
this.#id = val;
}
setTypeId(typeId) {
console.error("\x1b[31mHFloorItem.setTypeId(): Deprecated method used, use the setter HFloorItem.typeId = ... instead\x1b[0m");
if(!Number.isInteger(typeId)) {
throw new Error("HFloorItem.setTypeId: typeId must be an integer");
}
this.#typeId = typeId;
}
set typeId(val) {
if(!Number.isInteger(val)) {
throw new Error("HFloorItem.typeId: must be an integer");
}
this.#typeId = val;
}
setTile(tile) {
console.error("\x1b[31mHFloorItem.setTile(): Deprecated method used, use the setter HFloorItem.tile = ... instead\x1b[0m");
if(!(tile instanceof HPoint)) {
throw new Error("HFloorItem.setTile: tile must be an instance of HPoint");
}
this.#tile = tile;
}
set tile(val) {
if(!(val instanceof HPoint)) {
throw new Error("HFloorItem.tile: must be an instance of HPoint");
}
this.#tile = val;
}
set sizeZ(val) {
if(Number.isNaN(val) || typeof val !== 'number') {
throw new Error("HFloorItem.sizeZ: must be a double");
}
this.#sizeZ = val;
}
setFacing(facing) {
console.error("\x1b[31mHFloorItem.SetFacing(): Deprecated method used, use the setter HFloorItem.facing = ... instead\x1b[0m");
if(!HFacing.identify(facing)) {
throw new Error("HFloorItem.setFacing: facing must be a value of HFacing");
}
this.#facing = facing;
}
set facing(val) {
if(!HFacing.identify(val)) {
throw new Error("HFloorItem.facing: must be a value of HFacing");
}
this.#facing = val;
}
setCategory(category) {
console.error("\x1b[31mHFloorItem.setCategory(): Deprecated method used, use the setter HFloorItem.stuffCategory = ... instead\x1b[0m");
if(!Number.isInteger(category)) {
throw new Error("HFloorItem.setCategory: category must be an integer");
}
this.#stuffCategory = category;
}
set stuffCategory(val) {
if(!Number.isInteger(val)) {
throw new Error("HFloorItem.stuffCategory: must be an integer");
}
this.#stuffCategory = val;
}
setSecondsToExpiration(secondsToExpiration) {
console.error("\x1b[31mHFloorItem.setSecondsToExpiration(): Deprecated method used, use the setter HFloorItem.secondsToExpiration = ... instead\x1b[0m");
if(!Number.isInteger(secondsToExpiration)) {
throw new Error("HFloorItem.setSecondsToExpiration: secondsToExpiration must be an integer");
}
this.#secondsToExpiration = secondsToExpiration;
}
set secondsToExpiration(val) {
if(!Number.isInteger(val)) {
throw new Error("HFloorItem.secondsToExpiration: must be an integer");
}
this.#secondsToExpiration = val;
}
setUsagePolicy(usagePolicy) {
console.error("\x1b[31mHFloorItem.setUsagePolicy(): Deprecated method used, use the setter HFloorItem.usagePolicy = ... instead\x1b[0m");
if(!Number.isInteger(usagePolicy)) {
throw new Error("HFloorItem.setUsagePolicy: usagePolicy must be an integer");
}
this.#usagePolicy = usagePolicy;
}
set usagePolicy(val) {
if(!Number.isInteger(val)) {
throw new Error("HFloorItem.usagePolicy: must be an integer");
}
this.#usagePolicy = val;
}
setOwnerId(ownerId) {
console.error("\x1b[31mHFloorItem.setOwnerId(): Deprecated method used, use the setter HFloorItem.ownerId = ... instead\x1b[0m");
if(!Number.isInteger(ownerId)) {
throw new Error("HFloorItem.setOwnerId: ownerId must be an integer");
}
this.#ownerId = ownerId;
}
set ownerId(val) {
if(!Number.isInteger(val)) {
throw new Error("HFloorItem.ownerId: must be an integer");
}
this.#ownerId = val;
}
setStuff(stuff) {
console.error("\x1b[31mHFloorItem.setStuff(): Deprecated method used, use the setter HFloorItem.stuff = ... instead\x1b[0m");
if(!Array.isArray(stuff)) {
throw new Error("HFloorItem.setStuff: stuff must be an array");
}
this.#stuff = stuff;
}
set stuff(val) {
if(!Array.isArray(val)) {
throw new Error("HFloorItem.stuff: must be an array");
}
this.#stuff = val;
}
set staticClass(val) {
if(typeof val !== 'string' && typeof val !== 'undefined') {
throw new Error("HFloorItem.staticClass: must be a string or undefined");
}
this.#staticClass = val;
}
}
@@ -0,0 +1,202 @@
import { HPacket } from "../../protocol/hpacket";
import { HGender } from "./hgender";
import { HRelationshipStatus } from "./hrelationshipstatus";
export class HFriend {
constructor(packet: HPacket);
constructor(packet: HPacket, categories: Map<number, string>);
/**
* Parse all friends from fragment packet
* @param packet Packet to parse from
*/
static parseFromFragment(packet: HPacket): HFriend[];
/**
* Parse all friends from update packet
* @param packet Packet to parse from
*/
static parseFromUpdate(packet: HPacket): HFriend[];
/**
* Construct fragment packets from friends with headerId
* @param friends Array of friends
* @param headerId HeaderId to assign to packet
*/
static constructFragmentPackets(friends, headerId): HPacket[];
/**
* Construct update packet from friends with headerId
* @param friends Array of friends
* @param headerId HeaderId to assign to packet
*/
static constructUpdatePacket(friends, headerId): HPacket;
/**
* Append friend to packet
* @param packet Packet to append to
*/
appendToPacket(packet: HPacket): void;
/**
* Get removed friend ids from update packet
* @param packet Packet to parse from
*/
static getRemovedFriendIdsFromUpdate(packet: HPacket): number[];
/**
* Get categories from update packet
* @param packet Packet to parse from
*/
static getCategoriesFromUpdate(packet: HPacket): Map<number, string>;
/**
* Get id of friend
*/
get id(): number;
/**
* Set id of friend
*/
set id(val: number);
/**
* Get name of friend
*/
get name(): string;
/**
* Set name of friend
*/
set name(val: string);
/**
* Get gender of friend
*/
get gender(): HGender;
/**
* Set gender of friend
*/
set gender(val: HGender);
/**
* Get online status of friend
*/
get online(): boolean;
/**
* Set online status of friend
*/
set online(val: boolean);
/**
* Get whether following is allowed for friend
*/
get followingAllowed(): boolean;
/**
* Set whether following is allowed for friend
*/
set followingAllowed(val: boolean);
/**
* Get figure string of friend
*/
get figure(): string;
/**
* Set figure string of friend
*/
set figure(val: string);
/**
* Get category id of friend
*/
get categoryId(): number;
/**
* Set category id of friend
*/
set categoryId(val: number);
/**
* Get category name of friend
*/
get categoryName(): string;
/**
* Set category name of friend
*/
set categoryName(val: string);
/**
* Get motto of friend
*/
get motto(): string;
/**
* Set motto of friend
*/
set motto(val: string);
/**
* Get real name of friend
*/
get realName(): string;
/**
* Set real name of friend
*/
set realName(val: string);
/**
* Get facebook id of friend
*/
get facebookId(): string;
/**
* Set facebook id of friend
*/
set facebookId(val: string);
/**
* Get persisted message user of friend
*/
get persistedMessageUser(): boolean;
/**
* Set persisted message user of friend
*/
set persistedMessageUser(val: boolean);
/**
* Get whether friend is a vip member
*/
get vipMember(): boolean;
/**
* Set whether friend is a vip member
*/
set vipMember(val: boolean);
/**
* Get whether friend is a pocket Habbo user
*/
get pocketHabboUser(): boolean;
/**
* Set whether friend is a pocket Habbo user
*/
set pocketHabboUser(val: boolean);
/**
* Get relationship status of friend
*/
get relationshipStatus(): HRelationshipStatus;
/**
* Set relationship status of friend
*/
set relationshipStatus(val: HRelationshipStatus);
}
@@ -0,0 +1,429 @@
import { HPacket } from "../../protocol/hpacket.js";
import util from "util";
import { HGender } from "./hgender.js";
import { HRelationshipStatus } from "./hrelationshipstatus.js";
export class HFriend {
#categories;
#id;
#name;
#gender;
#online;
#followingAllowed;
#figure;
#categoryId;
#categoryName;
#motto;
#realName;
#facebookId;
#persistedMessageUser;
#vipMember;
#pocketHabboUser;
#relationshipStatus;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HFriend {\n`
+ `${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} name: ${util.inspect(this.#name, {colors: true})}\n`
+ `${HGender.identify(this.#gender) ? `${indent} facing: HGender.\x1b[36m${HGender.identify(this.#gender)}\x1b[0m\n` : ''}`
+ `${indent} online: ${util.inspect(this.#online, {colors: true})}\n`
+ `${indent} followingAllowed: ${util.inspect(this.#followingAllowed, {colors: true})}\n`
+ `${indent} figure: ${util.inspect(this.#figure, {colors: true})}\n`
+ `${indent} categoryId: ${util.inspect(this.#categoryId, {colors: true})}\n`
+ `${this.#categoryName ? `${indent} categoryName: ${util.inspect(this.#categoryName, {colors: true})}\n` : ''}`
+ `${indent} motto: ${util.inspect(this.#motto, {colors: true})}\n`
+ `${indent} realName: ${util.inspect(this.#realName, {colors: true})}\n`
+ `${indent} facebookId: ${util.inspect(this.#facebookId, {colors: true})}\n`
+ `${indent} persistedMessageUser: ${util.inspect(this.#persistedMessageUser, {colors: true})}\n`
+ `${indent} vipMember: ${util.inspect(this.#vipMember, {colors: true})}\n`
+ `${indent} pocketHabboUser: ${util.inspect(this.#pocketHabboUser, {colors: true})}\n`
+ `${HRelationshipStatus.identify(this.#relationshipStatus) ? `${indent} facing: HRelationshipStatus.\x1b[36m${HRelationshipStatus.identify(this.#relationshipStatus)}\x1b[0m\n` : ''}`
+ `${indent}}`
}
constructor(packet, categories = new Map()) {
if(!(packet instanceof HPacket)) {
throw new Error("HFriend.constructor: packet must be an instance of HPacket");
}
if(!(categories instanceof Map)) {
throw new Error("HFriend.constructor: categories must be an instance of Map");
}
this.#categories = categories;
let genderIdentifier;
[ this.#id, this.#name, genderIdentifier, this.#online, this.#followingAllowed, this.#figure,
this.#categoryId, this.#motto, this.#realName, this.#facebookId, this.#persistedMessageUser,
this.#vipMember, this.#pocketHabboUser, this.#relationshipStatus ] = packet.read('iSiBBSiSSSBBBs');
this.#gender = genderIdentifier === 0 ? HGender.Female : HGender.Male;
this.#categoryName = categories.get(this.#categoryId);
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFriend.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iSiBBSiSSSBBBs',
this.#id,
this.#name,
this.#gender === HGender.Female ? 0 : 1,
this.#online,
this.#followingAllowed,
this.#figure,
this.#categoryId,
this.#motto,
this.#realName,
this.#facebookId,
this.#persistedMessageUser,
this.#vipMember,
this.#pocketHabboUser,
this.#relationshipStatus);
}
static parseFromFragment(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFriend.parseFromFragment: packet must be an instance of HPacket");
}
packet.resetReadIndex();
let friends = [];
let vars = packet.read('iii');
for(let i = 0; i < vars[2]; i++) {
friends.push(new HFriend(packet));
}
return friends;
}
static constructFragmentPackets(friends, headerId) {
if(!Number.isInteger(headerId)) {
throw new Error("HFriend.constructFragmentPacket: headerId must be an integer");
}
if(!Array.isArray(friends)) {
throw new Error("HFriend.constructFragmentPacket: friends must be an array of HFriend instances")
}
let packetCount = Math.ceil(friends.length / 100);
let packets = [];
for(let i = 0; i < packetCount; i++) {
let packet = new HPacket(headerId)
.append('iii',
packetCount,
i,
i === packetCount - 1 && friends.length % 100 !== 0 ? friends.length % 100 : 100);
for(let j = i * 100; j < friends.length && j < (i + 1) * 100; j++) {
if(!(friends[j] instanceof HFriend)) {
throw new Error("HFriend.constructFragmentPacket: friends must be an array of HFriend instances")
}
friends[j].appendToPacket(packet)
}
packets.push(packet);
}
return packets;
}
static parseFromUpdate(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFriend.parseFromUpdate: packet must be an instance of HPacket");
}
packet.resetReadIndex();
let categories = new Map();
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
categories.set(...packet.read('iS'));
}
n = packet.readInteger();
let friends = [];
for (let i = 0; i < n; i++) {
if(packet.readInteger() !== -1) {
friends.push(new HFriend(packet, categories));
} else {
packet.readInteger();
}
}
return friends;
}
static getRemovedFriendIdsFromUpdate(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFriend.getRemovedFriendIdsFromUpdate: packet must be an instance of HPacket");
}
packet.resetReadIndex();
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
packet.read('iS');
}
n = packet.readInteger();
let removedIds = [];
for(let i = 0; i < n; i++) {
if(packet.readInteger() !== -1) {
new HFriend(packet);
} else {
removedIds.push(packet.readInteger());
}
}
return removedIds;
}
static getCategoriesFromUpdate(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HFriend.getCategoriesFromUpdate: packet must be an instance of HPacket");
}
packet.resetReadIndex();
let n = packet.readInteger();
let categories = new Map();
for(let i = 0; i < n; i++) {
categories.set(packet.readInteger(), packet.readString());
}
return categories;
}
static constructUpdatePacket(friends, headerId) {
if(!Number.isInteger(headerId)) {
throw new Error("HFriend.constructUpdatePacket: headerId must be an integer");
}
if(!Array.isArray(friends)) {
throw new Error("HFriend.constructUpdatePacket: friends must be an array of HFriend instances")
}
let categories = new Map();
for(let friend of friends) {
if(!(friend instanceof HFriend)) {
throw new Error("HFriend.constructUpdatePacket: friends must be an array of HFriend instances")
}
categories = new Map([...categories, ...friend.#categories]);
if(friend.categoryName) {
categories.set(friend.categoryId, friend.categoryName);
}
}
let packet = new HPacket(headerId)
.appendInt(categories.size);
for(let category of [...categories]) {
packet.append('iS', ...category);
}
packet.appendInt(friends.length);
for(let friend of friends) {
friend.appendToPacket(packet);
}
return packet;
}
getId() {
console.error("\x1b[31mHFriend.getId(): Deprecated method used, use the getter HFriend.id instead\x1b[0m");
return this.#id;
}
get id() {
return this.#id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error("HFriend.id: must be an integer");
}
this.#id = val;
}
getName() {
console.error("\x1b[31mHFriend.getName(): Deprecated method used, use the getter HFriend.name instead\x1b[0m");
return this.#name;
}
get name() {
return this.#name;
}
set name(val) {
if(typeof val != 'string') {
throw new Error("HFriend.name: must be a string");
}
this.#name = val;
}
get gender() {
return this.#gender;
}
set gender(val) {
if(!HGender.identify(val)) {
throw new Error("HFriend.gender: must be a value of HGender")
}
this.#gender = val;
}
get online() {
return this.#online;
}
set online(val) {
if(typeof val !== 'boolean') {
throw new Error("HFriend.online: must be a boolean")
}
this.#online = val;
}
get followingAllowed() {
return this.#followingAllowed;
}
set followingAllowed(val) {
if(typeof val !== 'boolean') {
throw new Error("HFriend.followingAllowed: must be a boolean")
}
this.#followingAllowed = val;
}
getFigure() {
return this.#figure;
}
get figure() {
return this.#figure;
}
set figure(val) {
if(typeof val != 'string') {
throw new Error("HFriend.figure: must be a string");
}
this.#figure = val;
}
get categoryId() {
return this.#categoryId;
}
set categoryId(val) {
if(!Number.isInteger()) {
throw new Error("HFriend.categoryId: must be an integer");
}
this.#categoryId = val;
}
get categoryName() {
return this.#categoryName;
}
set categoryName(val) {
if(typeof val != 'string') {
throw new Error("HFriend.categoryName: must be a string");
}
this.#categoryName = val;
}
getMotto() {
console.error("\x1b[31mHFriend.getMotto(): Deprecated method used, use the getter HFriend.motto instead\x1b[0m");
return this.#motto;
}
get motto() {
return this.#motto;
}
set motto(val) {
if(typeof val != 'string') {
throw new Error("HFriend.motto: must be a string");
}
this.#motto = val;
}
get realName() {
return this.#realName;
}
set realName(val) {
if(typeof val != 'string') {
throw new Error("HFriend.realName: must be a string");
}
this.#realName = val;
}
get facebookId() {
return this.#facebookId;
}
set facebookId(val) {
if(typeof val != 'string') {
throw new Error("HFriend.facebookId: must be a string");
}
this.#facebookId = val;
}
get persistedMessageUser() {
return this.#persistedMessageUser;
}
set persistedMessageUser(val) {
if(typeof val != 'boolean') {
throw new Error("HFriend.persistedMessageUser: must be a boolean");
}
this.#persistedMessageUser = val;
}
get vipMember() {
return this.#vipMember;
}
set vipMember(val) {
if(typeof val != 'boolean') {
throw new Error("HFriend.vipMember: must be a boolean");
}
this.#vipMember = val;
}
get pocketHabboUser() {
return this.#pocketHabboUser;
}
set pocketHabboUser(val) {
if(typeof val != 'boolean') {
throw new Error("HFriend.pocketHabboUser: must be a boolean");
}
this.#pocketHabboUser = val;
}
get relationshipStatus() {
return this.#relationshipStatus;
}
set relationshipStatus(val) {
if(!HRelationshipStatus.identify(val)) {
throw new Error("HFriend.relationshipStatus: must be a value of HRelationshipStatus");
}
this.#relationshipStatus = val;
}
}
@@ -0,0 +1,5 @@
export enum HGender {
Unisex = 'U',
Male = 'M',
Female = 'F'
}
@@ -0,0 +1,17 @@
/**
* Entity genders
* @readonly
* @enum {string}
*/
const HGender = Object.freeze({
Unisex: 'U',
Male: 'M',
Female: 'F',
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HGender };
@@ -0,0 +1,97 @@
import { HPacket } from "../../protocol/hpacket";
export class HGroup {
constructor(packet: HPacket);
/**
* Construct packet with group
* @param headerId Header id of packet to construct
*/
constructPacket(headerId: number): HPacket;
/**
* Append group to packet
* @param packet Packet to append group to
*/
appendToPacket(packet: HPacket): void;
/**
* Get group id
*/
get id(): number;
/**
* Set group id
*/
set id(val: number);
/**
* Get group name
*/
getName(): string;
/**
* Set group name
*/
setName(val: string);
/**
* Get group badge code
*/
getBadgeCode(): string;
/**
* Set group badge code
*/
setBadgeCode(val: string);
/**
* Get primary color
*/
getPrimaryColor(): string;
/**
* Set primary color
*/
setPrimaryColor(val: string);
/**
* Get secondary color
*/
getSecondaryColor(): string;
/**
* Set secondary color
*/
setSecondaryColor(val: string);
/**
* Is favorite group
*/
get isFavorite(): string;
/**
* Set is favorite group
*/
set isFavorite(val: string);
/**
* Get id of group owner
*/
get ownerId(): number;
/**
* Set id of group owner
*/
set ownerId(val: number);
/**
* Check if group has a forum
*/
get hasForum(): boolean;
/**
* Set whether group has a forum
*/
set hasForum(val: boolean);
}
@@ -0,0 +1,199 @@
import { HPacket } from "../../protocol/hpacket.js";
import util from "util";
export class HGroup {
#id;
#name;
#badgeCode;
#primaryColor;
#secondaryColor;
#isFavorite;
#ownerId;
#hasForum;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HGroup {\n`
+ `${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} name: ${util.inspect(this.#name, {colors: true})}\n`
+ `${indent} badgeCode: ${util.inspect(this.#badgeCode, {colors: true})}\n`
+ `${indent} primaryColor: ${util.inspect(this.#primaryColor, {colors: true})}\n`
+ `${indent} secondaryColor: ${util.inspect(this.#secondaryColor, {colors: true})}\n`
+ `${indent} isFavorite: ${util.inspect(this.#isFavorite, {colors: true})}\n`
+ `${indent} ownerId: ${util.inspect(this.#ownerId, {colors: true})}\n`
+ `${indent} hasForum: ${util.inspect(this.#hasForum, {colors: true})}\n`
+ `${indent}}`
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HGroup.constructor: packet must be an instance of HPacket");
}
[ this.#id, this.#name, this.#badgeCode, this.#primaryColor, this.#secondaryColor,
this.#isFavorite, this.#ownerId, this.#hasForum ] = packet.read('iSSSSBiB');
}
constructPacket(headerId) {
if(!Number.isInteger(headerId)) {
throw new Error("HGroup.constructPacket: headerId must be an integer")
}
let packet = new HPacket(headerId);
this.appendToPacket(packet);
return packet;
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HGroup.appendToPacket: packet must be an instance of HPacket")
}
packet.append('iSSSSBiB',
this.#id,
this.#name,
this.#badgeCode,
this.#primaryColor,
this.#secondaryColor,
this.#isFavorite,
this.#ownerId,
this.#hasForum);
}
getId() {
console.error("\x1b[31mHGroup.getId(): Deprecated method used, use the getter HGroup.id instead\x1b[0m");
return this.#id;
}
get id() {
return this.#id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error("HGroup.id: must be an integer");
}
this.#id = val;
}
getName() {
console.error("\x1b[31mHGroup.getName(): Deprecated method used, use the getter HGroup.name instead\x1b[0m");
return this.#name;
}
get name() {
return this.#name;
}
set name(val) {
if(typeof val != 'string') {
throw new Error("HGroup.name: must be a string");
}
this.#name = val;
}
getBadgeCode() {
console.error("\x1b[31mHGroup.getBadgeCode(): Deprecated method used, use the getter HGroup.badgeCode instead\x1b[0m");
return this.#badgeCode;
}
get badgeCode() {
return this.#badgeCode;
}
set badgeCode(val) {
if(typeof val != 'string') {
throw new Error("HGroup.badgeCode: must be a string");
}
this.#badgeCode = val;
}
getPrimaryColor() {
console.error("\x1b[31mHGroup.getPrimaryColor(): Deprecated method used, use the getter HGroup.primaryColor instead\x1b[0m");
return this.#primaryColor;
}
get primaryColor() {
return this.#primaryColor;
}
set primaryColor(val) {
if(typeof val != 'string') {
throw new Error("HGroup.primaryColor: must be a string");
}
this.#primaryColor = val;
}
getSecondaryColor() {
console.error("\x1b[31mHGroup.getSecondaryColor(): Deprecated method used, use the getter HGroup.secondaryColor instead\x1b[0m");
return this.#secondaryColor;
}
get secondaryColor() {
return this.#secondaryColor;
}
set secondaryColor(val) {
if(typeof val != 'string') {
throw new Error("HGroup.secondaryColor: must be a string");
}
this.#secondaryColor = val;
}
isFavorite() {
console.error("\x1b[31mHGroup.isFavorite(): Deprecated method used, use the getter HGroup.favorite instead\x1b[0m");
return this.#isFavorite;
}
get isFavorite() {
return this.#isFavorite;
}
set isFavorite(val) {
if(typeof val != 'boolean') {
throw new Error("HGroup.isFavorite: must be a boolean");
}
this.#isFavorite = val;
}
getOwnerId() {
console.error("\x1b[31mHGroup.getOwnerId(): Deprecated method used, use the getter HGroup.ownerId instead\x1b[0m");
return this.#ownerId;
}
get ownerId() {
return this.#ownerId;
}
set ownerId(val) {
if(!Number.isInteger(val)) {
throw new Error("HGroup.ownerId: must be an integer");
}
this.#ownerId = val;
}
hasForum() {
console.error("\x1b[31mHGroup.hasForum(): Deprecated method used, use the getter HGroup.hasForum instead\x1b[0m");
return this.#hasForum;
}
get hasForum() {
return this.#hasForum;
}
set hasForum(val) {
if(typeof val != 'boolean') {
throw new Error("HGroup.hasForum: must be a boolean");
}
this.#hasForum = val;
}
}
@@ -0,0 +1,213 @@
import { HPacket } from "../../protocol/hpacket";
import { HSpecialType } from "./hspecialtype";
import { HProductType } from "./hproducttype";
export class HInventoryItem {
constructor(packet: HPacket);
/**
* Append inventory item to a packet
* @param packet Packet to be appended to
*/
appendToPacket(packet: HPacket): void;
/**
* Parse all inventory items from a packet
* @param packet Packet to parse from
*/
static parse(packet: HPacket): HInventoryItem[];
/**
* Construct inventory packets (600 items per max)
* @param inventoryItems Inventory items to append to packet
* @param headerId Header id of packet
*/
static constructPackets(inventoryItems: HInventoryItem[], headerId: number): HPacket[];
/**
* Get itemId of inventory item
*/
get itemId(): number;
/**
* Set itemId of inventory item
* @param val Value to be set
*/
set itemId(val: number);
/**
* Get furnitype (WALL or FLOOR) of inventory item
*/
get furniType(): HProductType;
/**
* Set furnitype (WALL or FLOOR) of inventory item
* @param val Value to be set
*/
set furniType(val: HProductType);
/**
* Get id of inventory item
*/
get id(): number;
/**
* Set id of inventory item
* @param val Value to be set
*/
set id(val: number);
/**
* Get type id of inventory item
*/
get typeId(): number;
/**
* Set type id of inventory item
* @param val Value to be set
*/
set typeId(val: number);
/**
* Get category of inventory item
*/
get category(): HSpecialType;
/**
* Set category of inventory item
* @param val Value to be set
*/
set category(val: HSpecialType);
/**
* Get stuff category of inventory item
*/
get stuffCategory(): number;
/**
* Set stuff category of inventory item
* @param val Value to be set
*/
set stuffCategory(val: number);
/**
* Get stuff of inventory item
*/
get stuff(): any[];
/**
* Get stuff of inventory item
* @param val Value to be set
*/
set stuff(val: any[]);
/**
* Check if inventory item is recyclable
*/
get isRecyclable(): boolean;
/**
* Set whether inventory item is recyclable
* @param val Value to be set
*/
set isRecyclable(val: boolean);
/**
* Check if inventory item is tradeable
*/
get isTradeable(): boolean;
/**
* Set whether inventory item is tradeable
* @param val Value to be set
*/
set isTradeable(val: boolean);
/**
* Check if inventory item is groupable
*/
get isGroupable(): boolean;
/**
* Set whether inventory item is groupable
* @param val Value to be set
*/
set isGroupable(val: boolean);
/**
* Check if inventory item is sellable
*/
get isSellable(): boolean;
/**
* Set whether inventory item is sellable
* @param val Value to be set
*/
set isSellable(val: boolean);
/**
* Get amount of seconds to expiration of inventory item
*/
get secondsToExpiration(): number;
/**
* Set amount of seconds to expiration of inventory item
* @param val Value to be set
*/
set secondsToExpiration(val: number);
/**
* Check if inventory item is rented
*/
get isRented(): boolean;
/**
* Set whether inventory item is rented
* @param val Value to be set
*/
set isRented(val: boolean);
/**
* Check if rent period of inventory item has started
*/
get hasRentPeriodStarted(): boolean;
/**
* Set whether rent period of inventory item has started
* @param val Value to be set
*/
set hasRentPeriodStarted(val: boolean);
/**
* Get room id of inventory item
*/
get roomId(): number;
/**
* Get room id of inventory item
* @param val Value to be set
*/
set roomId(val: number);
/**
* Get slot id of inventory item (Floor items only)
*/
get slotId(): string | undefined;
/**
* Set slot id of inventory item (Floor items only)
* @param val Value to be set
*/
set slotId(val: string | undefined);
/**
* Get extra of inventory item
*/
get extra(): number;
/**
* Set extra of inventory item
* @param val Value to be set
*/
set extra(val: number | undefined);
}
@@ -0,0 +1,516 @@
import { HPacket } from "../../protocol/hpacket.js";
import util from "util";
import { HStuff } from "./hstuff.js";
import { HProductType } from "./hproducttype.js";
import { HSpecialType } from "./hspecialtype.js";
export class HInventoryItem {
#itemId;
#furniType;
#id;
#typeId;
#category;
#stuffCategory;
#stuff;
#isRecyclable;
#isTradeable;
#isGroupable;
#isSellable;
#secondsToExpiration;
#isRented;
#hasRentPeriodStarted;
#roomId;
#slotId;
#extra;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HInventoryItem {\n`
+ `${indent} itemId: ${util.inspect(this.#id, {colors: true})}\n`
+ `${HProductType.identify(this.#furniType) ? `${indent} furniType: HProductType.\x1b[36m${HProductType.identify(this.#furniType)}\x1b[0m\n` : ''}`
+ `${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} typeId: ${util.inspect(this.#typeId, {colors: true})}\n`
+ `${indent} category: ${util.inspect(this.#category, {colors: true})}\n`
+ `${indent} stuffCategory: ${util.inspect(this.#stuffCategory, {colors: true})}\n`
+ `${indent} stuff: ${util.inspect(this.#stuff, {colors: true})}\n`
+ `${indent} isRecyclable: ${util.inspect(this.#isRecyclable, {colors: true})}\n`
+ `${indent} isTradeable: ${util.inspect(this.#isTradeable, {colors: true})}\n`
+ `${indent} isGroupable: ${util.inspect(this.#isGroupable, {colors: true})}\n`
+ `${indent} isSellable: ${util.inspect(this.#isSellable, {colors: true})}\n`
+ `${indent} secondsToExpiration: ${util.inspect(this.#secondsToExpiration, {colors: true})}\n`
+ `${indent} isRented: ${util.inspect(this.#isRented, {colors: true})}\n`
+ `${indent} hasRentPeriodStarted: ${util.inspect(this.#hasRentPeriodStarted, {colors: true})}\n`
+ `${indent} roomId: ${util.inspect(this.#roomId, {colors: true})}\n`
+ `${this.#furniType === HProductType.FloorItem ? `${indent} slotId: ${util.inspect(this.#slotId, {colors: true})}\n` : ''}`
+ `${this.#furniType === HProductType.FloorItem ? `${indent} extra: ${util.inspect(this.#extra, {colors: true})}\n` : ''}`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HInventoryItem.constructor: packet must be an instance of HPacket");
}
[ this.#itemId, this.#furniType, this.#id, this.#typeId, this.#category,
this.#stuffCategory ] = packet.read('iSiiii');
this.#stuff = HStuff.readData(packet, this.#stuffCategory);
[ this.#isRecyclable, this.#isTradeable, this.#isGroupable, this.#isSellable, this.#secondsToExpiration,
this.#hasRentPeriodStarted, this.#roomId ] = packet.read('BBBBiBi');
if(this.#furniType === HProductType.FloorItem) {
[ this.#slotId, this.#extra ] = packet.read('Si');
}
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HInventoryItem.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iSiiii',
this.#itemId,
this.#furniType,
this.#id,
this.#typeId,
this.#category,
this.#stuffCategory);
HStuff.appendData(packet, this.#stuffCategory, this.#stuff);
packet.append('BBBBiBi',
this.#isRecyclable,
this.#isTradeable,
this.#isGroupable,
this.#isSellable,
this.#secondsToExpiration,
this.#hasRentPeriodStarted,
this.#roomId);
if(this.#furniType === HProductType.FloorItem) {
packet.append('Si',
this.#slotId || "",
this.#extra || 0);
}
}
static parse(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HInventoryItem.parse: packet must be an instance of HPacket");
}
let items = [];
packet.readIndex = 14;
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
items.push(new HInventoryItem(packet));
}
return items;
}
static constructPackets(inventoryItems, headerId) {
if(!Array.isArray(inventoryItems)) {
throw new Error("HInventoryItem.constructPackets: inventoryItems must be an array of HInventoryItem instances");
}
if(!Number.isInteger(headerId)) {
throw new Error("HInventoryItem.constructPackets: headerId must be an integer");
}
let packetCount = Math.ceil(inventoryItems.length / 600);
let packets = [];
for(let i = 0; i < packetCount; i++) {
let packet = new HPacket(headerId)
.append('iii',
packetCount,
i,
i === packetCount - 1 && inventoryItems.length % 600 !== 0 ? inventoryItems.length % 600 : 600);
for(let j = i * 600; j < inventoryItems.length && j < (i + 1) * 100; j++) {
if(!(inventoryItems[j] instanceof HInventoryItem)) {
throw new Error("HInventoryItem.constructPackets: inventoryItems must be an array of HInventoryItem instances");
}
inventoryItems[j].appendToPacket(packet);
}
packets.push(packet);
}
return packets;
}
getFurniType() {
console.error("\x1b[31mHInventoryItem.getFurniType(): Deprecated method used, use the getter HInventoryItem.furniType instead\x1b[0m");
return this.#furniType;
}
getId() {
console.error("\x1b[31mHInventoryItem.getId(): Deprecated method used, use the getter HInventoryItem.id instead\x1b[0m");
return this.#id;
}
getTypeId() {
console.error("\x1b[31mHInventoryItem.getTypeId(): Deprecated method used, use the getter HInventoryItem.typeId instead\x1b[0m");
return this.#typeId;
}
getCategory() {
console.error("\x1b[31mHInventoryItem.getCategory(): Deprecated method used, use the getter HInventoryItem.category instead\x1b[0m");
return this.#stuffCategory;
}
getStuff() {
console.error("\x1b[31mHInventoryItem.getStuff(): Deprecated method used, use the getter HInventoryItem.stuff instead\x1b[0m");
return this.#stuff;
}
isGroupable() {
console.error("\x1b[31mHInventoryItem.isGroupable(): Deprecated method used, use the getter HInventoryItem.isGroupable instead\x1b[0m");
return this.#isGroupable;
}
isTradeable() {
console.error("\x1b[31mHInventoryItem.isTradeable(): Deprecated method used, use the getter HInventoryItem.isTradeable instead\x1b[0m");
return this.#isTradeable;
}
isAllowedOnMarketplace() {
console.error("\x1b[31mHInventoryItem.isAllowedOnMarketplace(): Deprecated method used, use the getter HInventoryItem.isSellable instead\x1b[0m");
return this.#isSellable;
}
getSecondsToExpiration() {
console.error("\x1b[31mHInventoryItem.getSecondsToExpiration(): Deprecated method used, use the getter HInventoryItem.secondsToExpiration instead\x1b[0m");
return this.#secondsToExpiration;
}
hasRentPeriodStarted() {
console.error("\x1b[31mHInventoryItem.hasRentPeriodStarted(): Deprecated method used, use the getter HInventoryItem.hasRentPeriodStarted instead\x1b[0m");
return this.#hasRentPeriodStarted;
}
getRoomId() {
console.error("\x1b[31mHInventoryItem.getRoomId(): Deprecated method used, use the getter HInventoryItem.roomId instead\x1b[0m");
return this.#roomId;
}
getSlotId() {
console.error("\x1b[31mHInventoryItem.getSlotId(): Deprecated method used, use the getter HInventoryItem.slotId instead\x1b[0m");
return this.#slotId;
}
setFurniType(furniType) {
console.error("\x1b[31mHInventoryItem.setFurniType(): Deprecated method used, use the setter HInventoryItem.furniType = ... instead\x1b[0m");
if(!HProductType.identify(furniType)) {
throw new Error("HInventoryItem.setFurniType: furnitype must be a value of HProductType");
}
this.#furniType = furniType;
}
setId(id) {
console.error("\x1b[31mHInventoryItem.setId(): Deprecated method used, use the setter HInventoryItem.id = ... instead\x1b[0m");
if(!Number.isInteger(id)) {
throw new Error("HInventoryItem.setId: id must be an integer");
}
this.#id = id;
}
setTypeId(typeId) {
console.error("\x1b[31mHInventoryItem.setTypeId(): Deprecated method used, use the setter HInventoryItem.typeId = ... instead\x1b[0m");
if(!Number.isInteger(typeId)) {
throw new Error("HInventoryItem.setTypeId: typeId must be an integer");
}
this.#typeId = typeId;
}
setCategory(category) {
console.error("\x1b[31mHInventoryItem.setCategory(): Deprecated method used, use the setter HInventoryItem.category = ... instead\x1b[0m");
if(!Number.isInteger(category)) {
throw new Error("HInventoryItem.setCategory: category must be an integer");
}
this.#stuffCategory = category;
}
setStuff(stuff) {
console.error("\x1b[31mHInventoryItem.setStuff(): Deprecated method used, use the setter HInventoryItem.stuff = ... instead\x1b[0m");
if(!Array.isArray(stuff)) {
throw new Error("HInventoryItem.setStuff: stuff must be an array");
}
this.#stuff = stuff;
}
setIsGroupable(isGroupable) {
console.error("\x1b[31mHInventoryItem.setIsGroupable(): Deprecated method used, use the setter HInventoryItem.isGroupable = ... instead\x1b[0m");
if(typeof isGroupable !== 'boolean') {
throw new Error("HInventoryItem.setIsGroupable: isGroupable must be a boolean");
}
this.#isGroupable = isGroupable;
}
setIsTradeable(isTradeable) {
console.error("\x1b[31mHInventoryItem.setIsTradeable(): Deprecated method used, use the setter HInventoryItem.isTradeable = ... instead\x1b[0m");
if(typeof isTradeable !== 'boolean') {
throw new Error("HInventoryItem.setIsTradeable: isTradeable must be a boolean");
}
this.#isTradeable = isTradeable;
}
setIsAllowedOnMarketplace(isAllowedOnMarketplace) {
console.error("\x1b[31mHInventoryItem.setIsAllowedOnMarketplace(): Deprecated method used, use the setter HInventoryItem.isSellable = ... instead\x1b[0m");
if(typeof isAllowedOnMarketplace !== 'boolean') {
throw new Error("HInventoryItem.setIsAllowedOnMarketPlace: isAllowedOnMarketplace must be a boolean");
}
this.#isSellable = isAllowedOnMarketplace;
}
setSecondsToExpiration(secondsToExpiration) {
console.error("\x1b[31mHInventoryItem.setSecondsToExpiration(): Deprecated method used, use the setter HInventoryItem.secondsToExpiration = ... instead\x1b[0m");
if(!Number.isInteger(secondsToExpiration)) {
throw new Error("HInventoryItem.setSecondsToExpiration: secondsToExpiration must be an integer");
}
this.#secondsToExpiration = secondsToExpiration;
}
setHasRentPeriodStarted(hasRentPeriodStarted) {
console.error("\x1b[31mHInventoryItem.setHasRentPeriodStarted(): Deprecated method used, use the setter HInventoryItem.hasRentPeriodStarted = ... instead\x1b[0m");
if(typeof hasRentPeriodStarted !== 'boolean') {
throw new Error("HInventoryItem.setHasRentPeriodStarted: hasRentPeriodStarted must be a boolean");
}
this.#hasRentPeriodStarted = hasRentPeriodStarted;
}
setRoomId(roomId) {
console.error("\x1b[31mHInventoryItem.setRoomId(): Deprecated method used, use the setter HInventoryItem.roomId = ... instead\x1b[0m");
if(!Number.isInteger(roomId)) {
throw new Error("HInventoryItem.setRoomId: roomId must be an integer");
}
this.#roomId = roomId;
}
setSlotId(slotId) {
console.error("\x1b[31mHInventoryItem.setSlotId(): Deprecated method used, use the setter HInventoryItem.slotId = ... instead\x1b[0m");
if(typeof slotId !== 'string' && typeof slotId !== 'undefined') {
throw new Error("HInventoryItem.setSlotId: slotId must be a string or undefined");
}
this.#slotId = slotId;
}
get itemId() {
return this.#itemId;
}
set itemId(val) {
if(!Number.isInteger(val)) {
throw new Error("HInventoryItem.itemId: must be an integer");
}
this.#itemId = val;
}
get furniType() {
return this.#furniType;
}
set furniType(val) {
if(!HProductType.identify(val)) {
throw new Error("HInventoryItem.furniType: must be a value of HProductType");
}
this.#furniType = val;
}
get id() {
return this.#id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error("HInventoryItem.id: must be an integer");
}
this.#id = val;
}
get typeId() {
return this.#typeId;
}
set typeId(val) {
if(!Number.isInteger(val)) {
throw new Error("HInventoryItem.typeId: must be an integer");
}
this.#typeId = val;
}
get category() {
return this.#category;
}
set category(val) {
if(!HSpecialType.identify(val)) {
throw new Error("HInventoryItem.category: must be a value of HSpecialType");
}
this.#category = val;
}
get stuffCategory() {
return this.#stuffCategory;
}
set stuffCategory(val) {
if(!Number.isInteger(val)) {
throw new Error("HInventoryItem.stuffCategory: must be an integer");
}
this.#stuffCategory = val;
}
get stuff() {
return this.#stuff;
}
set stuff(val) {
if(!Array.isArray(val)) {
throw new Error("HInventoryItem.stuff: must be an array");
}
this.#stuff = val;
}
get isRecyclable() {
return this.#isRecyclable;
}
set isRecyclable(val) {
if(typeof val !== 'boolean') {
throw new Error("HInventoryItem.isRecyclable: must be a boolean");
}
this.#isRecyclable = val;
}
get isTradeable() {
return this.#isTradeable;
}
set isTradeable(val) {
if(typeof val !== 'boolean') {
throw new Error("HInventoryItem.isTradeable: must be a boolean");
}
this.#isTradeable = val;
}
get isGroupable() {
return this.#isGroupable;
}
set isGroupable(val) {
if(typeof val !== 'boolean') {
throw new Error("HInventoryItem.isGroupable: must be a boolean");
}
this.#isGroupable = val;
}
get isSellable() {
return this.#isSellable;
}
set isSellable(val) {
if(typeof val !== 'boolean') {
throw new Error("HInventoryItem.isSellable: must be a boolean");
}
this.#isSellable = val;
}
get secondsToExpiration() {
return this.#secondsToExpiration;
}
set secondsToExpiration(val) {
if(!Number.isInteger(val)) {
throw new Error("HInventoryItem.secondsToExpiration: must be an integer");
}
this.#secondsToExpiration = val;
}
get isRented() {
return this.#isRented;
}
set isRented(val) {
if(typeof val !== 'boolean') {
throw new Error("HInventoryItem.isRented: must be a boolean");
}
this.#isRented = val;
}
get hasRentPeriodStarted() {
return this.#hasRentPeriodStarted;
}
set hasRentPeriodStarted(val) {
if(typeof val !== 'boolean') {
throw new Error("HInventoryItem.hasRentPeriodStarted: must be a boolean");
}
this.#hasRentPeriodStarted = val;
}
get roomId() {
return this.#roomId;
}
set roomId(val) {
if(!Number.isInteger(val)) {
throw new Error("HInventoryItem.roomId: must be an integer");
}
this.#roomId = val;
}
get slotId() {
return this.#slotId;
}
set slotId(val) {
if(typeof val !== 'string' && typeof val !== 'undefined') {
throw new Error("HInventoryItem.slotId: must be a string or undefined")
}
this.#slotId = val;
}
get extra() {
return this.#extra;
}
set extra(val) {
if(!Number.isInteger(val) && typeof val !== 'undefined') {
throw new Error("HInventoryItem.extra: must be an integer or undefined");
}
this.#extra = val;
}
}
@@ -0,0 +1,48 @@
export class HPoint {
constructor(x: number, y: number);
constructor(x: number, y: number, z: number);
/**
* Get X coordinate of point
*/
get x(): number;
/**
* Set X coordinate of point
* @param val Value to be set
*/
set x(val: number);
/**
* Get Y coordinate of point
*/
get y(): number;
/**
* Set Y coordinate of point
* @param val Value to be set
*/
set y(val: number);
/**
* Get Z coordinate / height of point
*/
get z(): number;
/**
* Set Z coordinate of point
* @param val Value to be set
*/
set z(val: number);
/**
* Check if point equals other point
* @param point value to compare it to
*/
equals(point: HPoint): boolean;
/**
* Express point in a string
*/
toString(): string;
}
@@ -0,0 +1,95 @@
import util from "util";
export class HPoint {
#x;
#y;
#z;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `HPoint {\n`
+ `${indent} x: ${util.inspect(this.#x, {colors: true})}\n`
+ `${indent} y: ${util.inspect(this.#y, {colors: true})}\n`
+ `${indent} z: ${util.inspect(this.#z, {colors: true})}\n`
+ `${indent}}`;
}
constructor(x, y, z = 0) {
if(!Number.isInteger(x)) {
throw new Error("HPoint.constructor: x must be an integer");
}
if(!Number.isInteger(y)) {
throw new Error("HPoint.constructor: y must be an integer");
}
if(Number.isNaN(z) || typeof z !== 'number') {
throw new Error("HPoint.constructor: z must be a double");
}
this.#x = x;
this.#y = y;
this.#z = z;
}
getX() {
console.error("\x1b[31mHPoint.getX(): Deprecated method used, use the getter HPoint.x instead\x1b[0m");
return this.#x;
}
getY() {
console.error("\x1b[31mHPoint.getY(): Deprecated method used, use the getter HPoint.y instead\x1b[0m");
return this.#y;
}
getZ() {
console.error("\x1b[31mHPoint.getZ(): Deprecated method used, use the getter HPoint.z instead\x1b[0m");
return this.#z;
}
get x() {
return this.#x;
}
set x(val) {
if(!Number.isInteger(val)) {
throw new Error("HPoint.x: must be an integer");
}
this.#x = val;
}
get y() {
return this.#y;
}
set y(val) {
if(!Number.isInteger(val)) {
throw new Error("HPoint.y: must be an integer");
}
this.#y = val;
}
get z() {
return this.#z;
}
set z(val) {
if(Number.isNaN(val) || typeof val !== 'number') {
throw new Error("HPoint.z: must be a double");
}
this.#z = val;
}
equals(point) {
if(!(point instanceof HPoint)) {
throw new Error("HPoint.equals: point must be an instance of HPoint");
}
return this.#x === point.#x && this.#y === point.#y && this.#z === point.#z;
}
toString() {
return `(${this.#x}, ${this.#y}, ${this.#z})`;
}
}
@@ -0,0 +1,6 @@
export enum HProductType {
WallItem = 'I',
FloorItem = 'S',
Effect = 'E',
Badge = 'B'
}
@@ -0,0 +1,19 @@
/**
* Product types
* @readonly
* @enum {string}
*/
const HProductType = Object.freeze({
WallItem: 'I',
FloorItem: 'S',
Effect: 'E',
Badge: 'B',
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HProductType };
@@ -0,0 +1,6 @@
export enum HRelationshipStatus {
None,
Heart,
Smiley,
Skull
}
@@ -0,0 +1,18 @@
/**
* Possible relationship statuses
* @readonly
* @enum {number}
*/
const HRelationshipStatus = Object.freeze({
None: 0,
Heart: 1,
Smiley: 2,
Skull: 3,
identify(val) {
for (let key in this)
if (this[key] === val)
return key;
}
});
export { HRelationshipStatus };
@@ -0,0 +1,21 @@
export enum HSign {
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Heart,
Skull,
Exclamation,
Soccerball,
Smiley,
Redcard,
Yellowcard,
Invisible
}
@@ -0,0 +1,33 @@
/**
* Holdable signs
* @readonly
* @enum {number}
*/
const HSign = Object.freeze({
Zero: 0,
One: 1,
Two: 2,
Three: 3,
Four: 4,
Five: 5,
Six: 6,
Seven: 7,
Eight: 8,
Nine: 9,
Ten: 10,
Heart: 11,
Skull: 12,
Exclamation: 13,
Soccerball: 14,
Smiley: 15,
Redcard: 16,
Yellowcard: 17,
Invisible: 18,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HSign };
@@ -0,0 +1,25 @@
export enum HSpecialType {
Default = 1,
WallPaper = 2,
FloorPaint = 3,
LandScape = 4,
PostIt = 5,
Poster = 6,
SoundSet = 7,
TraxSong = 8,
Present = 9,
EcotronBox = 10,
Trophy = 11,
CreditFurni = 12,
PetShampoo = 13,
PetCustomPart = 14,
PetCustomPartShampoo = 15,
PetSaddle = 16,
GuildFurni = 17,
GameFurni = 18,
MonsterplantSeed = 19,
MonsterplantRevival = 20,
MonsterplantRebreed = 21,
MonsterplantFertilize = 22,
FigurePurchasableSet = 23
}
@@ -0,0 +1,38 @@
/**
* Furni special type
* @readonly
* @enum {number}
*/
const HSpecialType = Object.freeze({
Default: 1,
WallPaper: 2,
FloorPaint: 3,
LandScape: 4,
PostIt: 5,
Poster: 6,
SoundSet: 7,
TraxSong: 8,
Present: 9,
EcotronBox: 10,
Trophy: 11,
CreditFurni: 12,
PetShampoo: 13,
PetCustomPart: 14,
PetCustomPartShampoo: 15,
PetSaddle: 16,
GuildFurni: 17,
GameFurni: 18,
MonsterplantSeed: 19,
MonsterplantRevival: 20,
MonsterplantRebreed: 21,
MonsterplantFertilize: 22,
FigurePurchasableSet: 23,
identify(val) {
for (let key in this)
if (this[key] === val)
return key;
}
});
export { HSpecialType };
@@ -0,0 +1,5 @@
export enum HStance {
Stand,
Sit,
Lay
}
@@ -0,0 +1,17 @@
/**
* Entity stances
* @readonly
* @enum {number}
*/
const HStance = Object.freeze({
Stand: 0,
Sit: 1,
Lay: 2,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HStance };
@@ -0,0 +1,18 @@
import { HPacket } from "../../protocol/hpacket";
export class HStuff {
/**
* Read stuff from packet
* @param packet Packet to read from
* @param category Stuff category
*/
static readData(packet: HPacket, category: number): any[];
/**
* Append stuff to packet
* @param packet Packet to append to
* @param category Stuff category
* @param stuff Stuff to append
*/
static appendData(packet: HPacket, category: number, stuff: any[]): void;
}
@@ -0,0 +1,146 @@
import { HPacket } from "../../protocol/hpacket.js";
export class HStuff {
static readData(packet, category) {
if(!(packet instanceof HPacket)) {
throw new Error("HStuff.readData: packet must be an instance of Packet");
}
if(!Number.isInteger(category)) {
throw new Error("HStuff.readData: category must be an integer");
}
let values = [];
switch(category & 0xFF) {
case 0: /* LegacyStuffData */
values.push(packet.readString());
break;
case 1: /* MapStuffData */
let nMap = packet.readInteger();
values.push(nMap);
for(let i = 0; i < nMap; i++) {
values.push(...packet.read('SS'));
}
break;
case 2: /* StringArrayStuffData */
let nString = packet.readInteger();
values.push(nString);
for(let i = 0; i < nString; i++) {
values.push(packet.readString());
}
break;
case 3: /* VoteResultStuffData */
values.push(...packet.read('Si'));
break;
case 5: /* IntArrayStuffData */
let nInt = packet.readInteger();
values.push(nInt);
for(let i = 0; i < nInt; i++) {
values.push(packet.readInteger());
}
break;
case 6: /* HighScoreStuffData */
values.push(...packet.read('Sii'));
let nScore = packet.readInteger();
values.push(nScore);
for(let i = 0; i < nScore; i++) {
values.push(packet.readInteger());
let nWinner = packet.readInteger();
values.push(nWinner);
for(let j = 0; j < nWinner; j++) {
values.push(packet.readString());
}
}
break;
case 7: /* CrackableStuffData */
values.push(...packet.read('Sii'));
}
if((category & 0xFF00 & 0x100) > 0) {
values.push(...packet.read('ii'));
}
return values;
}
static appendData(packet, category, stuff) {
if(!(packet instanceof HPacket)) {
throw new Error("HStuff.appendData: packet must be an instance of Packet");
}
if(!Number.isInteger(category)) {
throw new Error("HStuff.appendData: category must be an integer");
}
if(!Array.isArray(stuff)) {
throw new Error("HStuff.appendData: stuff must be an array")
}
switch(category & 0xFF) {
case 0: /* LegacyStuffData */
packet.appendString(stuff[0]);
break;
case 1: /* MapStuffData */
let nMap = stuff[0];
packet.appendInt(nMap);
for(let i = 0; i < nMap; i++) {
packet.append('SS',
stuff[1 + i * 2],
stuff[2 + i * 2])
}
break;
case 2: /* StringArrayStuffData */
let nString = stuff[0];
packet.appendInt(nString);
for(let i = 0; i < nString; i++) {
packet.appendString(stuff[1 + i]);
}
break;
case 3: /* VoteResultStuffData */
packet.append('Si', ...stuff.slice(0, 2));
break;
case 5: /* IntArrayStuffData */
let nInt = stuff[0];
packet.appendInt(nInt);
for(let i = 0; i < nInt; i++) {
packet.appendInt(stuff[1 + i]);
}
break;
case 6: /* HighScoreStuffData */
packet.append('Sii', ...stuff.slice(0, 3));
let nScore = stuff[3];
packet.appendInt(nScore);
let index = 4;
for(let i = 0; i < nScore; i++) {
packet.appendInt(stuff[index++]);
let nWinner = stuff[index++];
packet.appendInt(nWinner);
for(let j = 0; j < nWinner; j++) {
packet.appendString(stuff[index++]);
}
}
break;
case 7: /* CrackableStuffData */
packet.append('Sii', ...stuff.slice(0, 3));
}
if((category & 0xFF00 & 0x100) > 0) {
packet.append('ii', ...stuff.slice(-2, -1));
}
}
}
@@ -0,0 +1,143 @@
import { HPacket } from "../../protocol/hpacket";
import { HGroup } from "./hgroup";
export class HUserProfile {
constructor(packet: HPacket);
/**
* Construct a packet containing the user profile
* @param headerId Header id to assign to created packet
*/
constructPacket(headerId: number): HPacket;
/**
* Get user id
*/
get id(): number;
/**
* Set user id
* @param val Value to be set
*/
set id(val: number);
/**
* Get username
*/
get username(): string;
/**
* Set username
*/
set username(val: string);
/**
* Get user motto
*/
get motto(): string;
/**
* Set user motto
*/
set motto(val: string);
/**
* Get user figure string
*/
get figure(): string;
/**
* Set user figure string
*/
set figure(val: string);
/**
* Get creation date of account
*/
get creationDate(): string;
/**
* Set creation date of account
*/
set creationDate(val: string);
/**
* Get achievement score
*/
get achievementsScore(): number;
/**
* Set achievement score
*/
set achievementsScore(val: number);
/**
* Get friend count
*/
get friendCount(): number;
/**
* Set friend count
*/
set friendCount(val: number);
/**
* Is friend of user
*/
get isFriend(): boolean;
/**
* Set whether you are shown as friend of user
*/
set isFriend(val: boolean);
/**
* Friend request has been send out
*/
get isRequestedFriend(): boolean;
/**
* Set whether friend request has been send out
*/
set isRequestedFriend(val: boolean);
/**
* Is user online
*/
get isOnline(): boolean;
/**
* Set whether user appears as online
*/
set isOnline(val: boolean);
/**
* Get all groups from user
*/
get groups(): HGroup[];
/**
* Set all groups from user
*/
set groups(val: HGroup[]);
/**
* Check when user was last online
*/
get lastAccessSince(): number;
/**
* Set when user was last online
*/
set lastAccessSince(val: number);
/**
* Check if profile is public
*/
get openProfile(): boolean;
/**
* Set if profile is public
*/
set openProfile(val: boolean);
}
@@ -0,0 +1,358 @@
import { HPacket } from "../../protocol/hpacket.js";
import util from "util";
import { HGroup } from "./hgroup.js";
export class HUserProfile {
#id;
#username;
#motto;
#figure;
#creationDate;
#achievementScore;
#friendCount;
#isFriend;
#isFriendRequestSent;
#isOnline;
#groups = [];
#lastAccessSince;
#openProfile;
#accountLevel
#starGemCount
#unknownBoolean1 = false;
#unknownBoolean2 = false;
#unknownBoolean3 = false;
#unknownInt = 0;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HUserProfile {\n`
+ `${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} username: ${util.inspect(this.#username, {colors: true})}\n`
+ `${indent} motto: ${util.inspect(this.#motto, {colors: true})}\n`
+ `${indent} figure: ${util.inspect(this.#figure, {colors: true})}\n`
+ `${indent} creationDate: ${util.inspect(this.#creationDate, {colors: true})}\n`
+ `${indent} achievementScore: ${util.inspect(this.#achievementScore, {colors: true})}\n`
+ `${indent} friendCount: ${util.inspect(this.#friendCount, {colors: true})}\n`
+ `${indent} isFriend: ${util.inspect(this.#isFriend, {colors: true})}\n`
+ `${indent} isFriendRequestSent: ${util.inspect(this.#isFriendRequestSent, {colors: true})}\n`
+ `${indent} isOnline: ${util.inspect(this.#isOnline, {colors: true})}\n`
+ `${indent} groups: ${util.inspect(this.#groups, {colors: true, maxArrayLength: 0})}\n`
+ `${indent} lastAccessSince: ${util.inspect(this.#lastAccessSince, {colors: true})}\n`
+ `${indent} openProfile: ${util.inspect(this.#openProfile, {colors: true})}\n`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HUserProfile.constructor: packet must be an instance of HPacket");
}
[ this.#id, this.#username, this.#figure, this.#motto, this.#creationDate,
this.#achievementScore, this.#friendCount, this.#isFriend,
this.#isFriendRequestSent, this.#isOnline ] = packet.read('iSSSSiiBBB');
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
this.#groups.push(new HGroup(packet));
}
[ this.#lastAccessSince, this.#openProfile ] = packet.read('iB');
if (packet.readIndex < packet.getBytesLength()) {
[ this.#unknownBoolean1, this.#accountLevel, this.#unknownInt, this.#starGemCount,
this.#unknownBoolean2, this.#unknownBoolean3 ] = packet.read('BiiiBB');
}
}
constructPacket(headerId) {
if(!Number.isInteger(headerId)) {
throw new Error("HUserProfile.constructPacket: headerId must be an integer");
}
let packet = new HPacket(headerId)
.append('iSSSSiiBBBi',
this.#id,
this.#username,
this.#figure,
this.#motto,
this.#creationDate,
this.#achievementScore,
this.#friendCount,
this.#isFriend,
this.#isFriendRequestSent,
this.#isOnline,
this.#groups.length);
for(let group of this.#groups) {
group.appendToPacket(packet);
}
packet.append('iB',
this.#lastAccessSince,
this.#openProfile);
if (this.#accountLevel && this.#starGemCount) {
packet.append('BiiiBB',
this.#unknownBoolean1,
this.#accountLevel,
this.#unknownInt,
this.#starGemCount,
this.#unknownBoolean2,
this.#unknownBoolean3);
}
return packet;
}
get id() {
return this.#id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error("HUserProfile.id: must be an integer");
}
this.#id = val;
}
get username() {
return this.#username;
}
set username(val) {
if(typeof val !== 'string') {
throw new Error("HUserProfile.username: must be a string");
}
this.#username = val;
}
get motto() {
return this.#motto;
}
set motto(val) {
if(typeof val !== 'string') {
throw new Error("HUserProfile.motto: must be a string");
}
this.#motto = val;
}
get figure() {
return this.#figure;
}
set figure(val) {
if(typeof val !== 'string') {
throw new Error("HUserProfile.figure: must be a string");
}
this.#figure = val;
}
get creationDate() {
return this.#creationDate;
}
set creationDate(val) {
if(typeof val !== 'string') {
throw new Error("HUserProfile.creationData: must be a string");
}
this.#creationDate = val;
}
get achievementScore() {
return this.#achievementScore;
}
set achievementScore(val) {
if(!Number.isInteger(val)) {
throw new Error("HUserProfile.achievementScore: must be an integer");
}
this.#achievementScore = val;
}
get friendCount() {
return this.#friendCount;
}
set friendCount(val) {
if(!Number.isInteger(val)) {
throw new Error("HUserProfile.friendCount: must be an integer");
}
this.#friendCount = val;
}
get isFriend() {
return this.#isFriend;
}
set isFriend(val) {
if(typeof val !== 'boolean') {
throw new Error("HUserProfile.isFriend: must be a boolean");
}
this.#isFriend = val;
}
get isFriendRequestSent() {
return this.#isFriendRequestSent;
}
set isFriendRequestSent(val) {
if(typeof val !== 'boolean') {
throw new Error("HUserProfile.isFriendRequestSent: must be a boolean");
}
this.#isFriendRequestSent = val;
}
get isOnline() {
return this.#isOnline;
}
set isOnline(val) {
if(typeof val !== 'boolean') {
throw new Error("HUserProfile.isOnline: must be a boolean");
}
this.#isOnline = val;
}
get groups() {
return this.#groups;
}
set groups(val) {
if(!Array.isArray(val) || val.filter(g => !(g instanceof HGroup)).length > 0) {
throw new Error("HUserProfile.groups: must be an array of HGroup instances");
}
this.#groups = val;
}
get lastAccessSince() {
return this.#lastAccessSince;
}
set lastAccessSince(val) {
if(!Number.isInteger(val)) {
throw new Error("HUserProfile.lastAccessSince: must be an integer");
}
this.#lastAccessSince = val;
}
get openProfile() {
return this.#openProfile;
}
set openProfile(val) {
if(typeof val !== 'boolean') {
throw new Error("HUserProfile.isOpenProfile: must be a boolean");
}
this.#openProfile = val;
}
get accountLevel() {
return this.#accountLevel;
}
set accountLevel(val) {
if(!Number.isInteger(val)) {
throw new Error("HUserProfile.accountLevel: must be an integer");
}
this.#accountLevel = val;
}
get starGemCount() {
return this.#starGemCount;
}
set starGemCount(val) {
if(!Number.isInteger(val)) {
throw new Error("HUserProfile.starGemCount: must be an integer");
}
this.#starGemCount = val;
}
getId() {
console.error("\x1b[31mHUserProfile.getId(): Deprecated method used, use the getter HUserProfile.id instead\x1b[0m");
return this.#id;
}
getUsername() {
console.error("\x1b[31mHUserProfile.getUsername(): Deprecated method used, use the getter HUserProfile.username instead\x1b[0m");
return this.#username;
}
getMotto() {
console.error("\x1b[31mHUserProfile.getUsername(): Deprecated method used, use the getter HUserProfile.username instead\x1b[0m");
return this.#motto;
}
getFigure() {
console.error("\x1b[31mHUserProfile.getFigure(): Deprecated method used, use the getter HUserProfile.figure instead\x1b[0m");
return this.#figure;
}
getCreationDate() {
console.error("\x1b[31mHUserProfile.getCreationDate(): Deprecated method used, use the getter HUserProfile.creationDate instead\x1b[0m");
return this.#creationDate;
}
getAchievementScore() {
console.error("\x1b[31mHUserProfile.getAchievementScore(): Deprecated method used, use the getter HUserProfile.achievementScore instead\x1b[0m");
return this.#achievementScore;
}
getFriendCount() {
console.error("\x1b[31mHUserProfile.getFriendCount(): Deprecated method used, use the getter HUserProfile.friendCount instead\x1b[0m");
return this.#friendCount;
}
isFriend() {
console.error("\x1b[31mHUserProfile.isFriend(): Deprecated method used, use the getter HUserProfile.isFriend instead\x1b[0m");
return this.#isFriend;
}
isRequestedFriend() {
console.error("\x1b[31mHUserProfile.isRequestedFriend(): Deprecated method used, use the getter HUserProfile.isRequestedFriend instead\x1b[0m");
return this.#isFriendRequestSent;
}
isOnline() {
console.error("\x1b[31mHUserProfile.isOnline(): Deprecated method used, use the getter HUserProfile.isOnline instead\x1b[0m");
return this.#isOnline;
}
getGroups() {
console.error("\x1b[31mHUserProfile.getGroups(): Deprecated method used, use the getter HUserProfile.groups instead\x1b[0m");
return this.#groups;
}
getLastAccessSince() {
console.error("\x1b[31mHUserProfile.getLastAccessSince(): Deprecated method used, use the getter HUserProfile.lastAccessSince instead\x1b[0m");
return this.#lastAccessSince;
}
isOpenProfile() {
console.error("\x1b[31mHUserProfile.isOpenProfile(): Deprecated method used, use the getter HUserProfile.isOpenProfile instead\x1b[0m");
return this.#openProfile;
}
}
@@ -0,0 +1,113 @@
import { HPacket } from "../../protocol/hpacket";
export class HWallItem {
constructor(packet: HPacket);
/**
* Append wall item to packet
* @param packet Packet to append to
*/
appendToPacket(packet: HPacket): void;
/**
* Parse all wall items from packet
* @param packet Packet to parse from
*/
static parse(packet: HPacket): HWallItem[];
/**
* Construct packet with header id containing all wall items
* @param wallItems Wall items to add to packet
* @param headerId Header id for packet
*/
static constructPacket(wallItems: HWallItem[], headerId: number): HPacket;
/**
* Get id of wall item
*/
get id(): number;
/**
* Get type id of wall item
*/
get typeId(): number;
/**
* Get usage policy of wall item
*/
get usagePolicy(): number;
/**
* Get owner id of wall item
*/
get ownerId(): number;
/**
* Get owner name of wall item
*/
get ownerName(): string;
/**
* Get state of wall item
*/
get state(): string;
/**
* Get location of wall item
*/
get location(): string;
/**
* Get seconds to expiration of wall item
*/
get secondsToExpiration(): number;
/**
* Set owner name of wall item
* @param val Owner name to be set
*/
set ownerName(val: string);
/**
* Set id of wall item
* @param val Id to be set
*/
set id(val: number);
/**
* Set type id of wall item
* @param val Type id to be set
*/
set typeId(val: number);
/**
* Set state of wall item
* @param val State to be set
*/
set state(val: string);
/**
* Set location of wall item
* @param val Location to be set
*/
set location(val: string);
/**
* Set usage policy of wall item
* @param val Usage policy to be set
*/
set usagePolicy(val: number);
/**
* Set seconds to expiration of wall item
* @param val Seconds to expiration to be set
*/
set secondsToExpiration(val: number);
/**
* Set owner id of wall item
* @param val Owner id to be set
*/
set ownerId(val: number);
}
@@ -0,0 +1,322 @@
import { HPacket } from "../../protocol/hpacket.js";
import util from "util";
export class HWallItem {
#id;
#typeId;
#state;
#location;
#usagePolicy;
#secondsToExpiration;
#ownerId;
#ownerName;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HWallItem {\n`
+ `${indent} id: ${util.inspect(this.#id, {colors: true})}\n`
+ `${indent} typeId: ${util.inspect(this.#typeId, {colors: true})}\n`
+ `${indent} state: ${util.inspect(this.#state, {colors: true})}\n`
+ `${indent} location: ${util.inspect(this.#location, {colors: true})}\n`
+ `${indent} usagePolicy: ${util.inspect(this.#usagePolicy, {colors: true})}\n`
+ `${indent} secondsToExpiration: ${util.inspect(this.#secondsToExpiration, {colors: true})}\n`
+ `${indent} ownerId: ${util.inspect(this.#ownerId, {colors: true})}\n`
+ `${indent} ownerName: ${util.inspect(this.#ownerName, {colors: true})}\n`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HWallItem.constructor: packet must be an instance of HPacket");
}
let idString;
[ idString, this.#typeId, this.#location, this.#state, this.#secondsToExpiration,
this.#usagePolicy, this.#ownerId ] = packet.read('SiSSiii');
this.#id = Number.parseInt(idString);
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HWallItem.appendToPacket: packet must be an instance of HPacket");
}
packet.append('SiSSiii',
`${this.#id}`,
this.#typeId,
this.#location,
this.#state,
this.#secondsToExpiration,
this.#usagePolicy,
this.#ownerId);
}
static parse(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HWallItem.parse: packet must be an instance of HPacket");
}
let ownersCount = packet.readInteger();
let owners = new Map();
for(let i = 0; i < ownersCount; i++) {
owners.set(...packet.read('iS'));
}
let furniture = [];
let n = packet.readInteger();
for(let i = 0; i < n; i++) {
let furni = new HWallItem(packet);
furni.#ownerName = owners.get(furni.#ownerId);
furniture.push(furni);
}
return furniture;
}
static constructPacket(wallItems, headerId) {
if(!Number.isInteger(headerId)) {
throw new Error("HWallItem.constructPacket: headerId must be an integer")
}
if(!Array.isArray(wallItems)) {
throw new Error("HWallItem.constructPacket: wallItems must be an array of HWallItem instances")
}
let owners = new Map();
for(let wallItem of wallItems) {
if(!(wallItem instanceof HWallItem)) {
throw new Error("HWallItem.constructPacket: wallItems must be an array of HWallItem instances")
}
owners.set(wallItem.#ownerId, wallItem.#ownerName);
}
let packet = new HPacket(headerId)
.appendInt(owners.size);
for(let ownerEntry of Array.from(owners.entries())) {
packet.append('iS', ...ownerEntry);
}
packet.appendInt(wallItems.length);
for(let wallItem of wallItems) {
wallItem.appendToPacket(packet);
}
return packet;
}
get id() {
return this.#id;
}
set id(val) {
if(!Number.isInteger(val)) {
throw new Error("HWallItem.id: must be an integer");
}
this.#id = val;
}
get typeId() {
return this.#typeId;
}
set typeId(val) {
if(!Number.isInteger(val)) {
throw new Error("HWallItem.typeId: must be an integer");
}
this.#typeId = val;
}
get usagePolicy() {
return this.#usagePolicy;
}
set usagePolicy(val) {
if(!Number.isInteger(val)) {
throw new Error("HWallItem.usagePolicy: must be an integer");
}
this.#usagePolicy = val;
}
get ownerId() {
return this.#ownerId;
}
set ownerId(val) {
if(!Number.isInteger(val)) {
throw new Error("HWallItem.ownerId: must be an integer");
}
this.#ownerId = val;
}
get ownerName() {
return this.#ownerName
}
set ownerName(val) {
if(typeof val !== 'string') {
throw new Error("HWallItem.ownerName: must be a string");
}
this.#ownerName = val;
}
get state() {
return this.#state
}
set state(val) {
if(typeof val !== 'string') {
throw new Error("HWallItem.state: must be a string");
}
this.#state = val;
}
get location() {
return this.#location
}
set location(val) {
if(typeof val !== 'string') {
throw new Error("HWallItem.location: must be a string");
}
this.#location = val;
}
get secondsToExpiration() {
return this.#secondsToExpiration;
}
set secondsToExpiration(val) {
if(!Number.isInteger(val)) {
throw new Error("HWallItem.secondsToExpiration: must be an integer");
}
this.#secondsToExpiration = val;
}
getId() {
console.error("\x1b[31mHWallItem.getId(): Deprecated method used, use the getter HWallItem.id instead\x1b[0m");
return this.#id;
}
getTypeId() {
console.error("\x1b[31mHWallItem.getTypeId(): Deprecated method used, use the getter HWallItem.typeId instead\x1b[0m");
return this.#typeId;
}
getUsagePolicy() {
console.error("\x1b[31mHWallItem.getUsagePolicy(): Deprecated method used, use the getter HWallItem.usagePolicy instead\x1b[0m");
return this.#usagePolicy;
}
getOwnerId() {
console.error("\x1b[31mHWallItem.getOwnerId(): Deprecated method used, use the getter HWallItem.ownerId instead\x1b[0m");
return this.#ownerId;
}
getOwnerName() {
console.error("\x1b[31mHWallItem.getOwnerName(): Deprecated method used, use the getter HWallItem.ownerName instead\x1b[0m");
return this.#ownerName;
}
getState() {
console.error("\x1b[31mHWallItem.getState(): Deprecated method used, use the getter HWallItem.state instead\x1b[0m");
return this.#state;
}
getLocation() {
console.error("\x1b[31mHWallItem.getLocation(): Deprecated method used, use the getter HWallItem.location instead\x1b[0m");
return this.#location;
}
getSecondsToExpiration() {
console.error("\x1b[31mHWallItem.getSecondsToExpiration(): Deprecated method used, use the getter HWallItem.secondsToExpiration instead\x1b[0m");
return this.#secondsToExpiration;
}
setOwnerName(ownerName) {
console.error("\x1b[31mHWallItem.setOwnerName(): Deprecated method used, use the setter HWallItem.ownerName = ... instead\x1b[0m");
if(typeof ownerName !== 'string') {
throw new Error("HWallItem.setOwnerName: ownerName must be a string");
}
this.#ownerName = ownerName;
}
setId(id) {
console.error("\x1b[31mHWallItem.setId(): Deprecated method used, use the setter HWallItem.id = ... instead\x1b[0m");
if(!Number.isInteger(id)) {
throw new Error("HWallItem.setId: id must be an integer");
}
this.#id = id;
}
setTypeId(typeId) {
console.error("\x1b[31mHWallItem.setTypeId(): Deprecated method used, use the setter HWallItem.typeId = ... instead\x1b[0m");
if(!Number.isInteger(typeId)) {
throw new Error("HWallItem.setTypeId: typeId must be an integer");
}
this.#typeId = typeId;
}
setState(state) {
console.error("\x1b[31mHWallItem.setState(): Deprecated method used, use the setter HWallItem.state = ... instead\x1b[0m");
if(typeof state !== 'string') {
throw new Error("HWallItem.setState: state must be a string");
}
this.#state = state;
}
setLocation(location) {
console.error("\x1b[31mHWallItem.setLocation(): Deprecated method used, use the setter HWallItem.location = ... instead\x1b[0m");
if(typeof location !== 'string') {
throw new Error("HWallItem.setLocation: location must be a string");
}
this.#location = location;
}
setUsagePolicy(usagePolicy) {
console.error("\x1b[31mHWallItem.setUsagePolicy(): Deprecated method used, use the setter HWallItem.usagePolicy = ... instead\x1b[0m");
if(!Number.isInteger(usagePolicy)) {
throw new Error("HWallItem.setUsagePolicy: usagePolicy must be an integer");
}
this.#usagePolicy = usagePolicy;
}
setSecondsToExpiration(secondsToExpiration) {
console.error("\x1b[31mHWallItem.setSecondsToExpiration(): Deprecated method used, use the setter HWallItem.secondsToExpiration = ... instead\x1b[0m");
if(!Number.isInteger(secondsToExpiration)) {
throw new Error("HWallItem.setSecondsToExpiration: secondsToExpiration must be an integer");
}
this.#secondsToExpiration = secondsToExpiration;
}
setOwnerId(ownerId) {
console.error("\x1b[31mHWallItem.setOwnerId(): Deprecated method used, use the setter HWallItem.ownerId = ... instead\x1b[0m");
if(!Number.isInteger(ownerId)) {
throw new Error("HWallItem.setOwnerId: ownerId must be an integer");
}
this.#ownerId = ownerId;
}
}
@@ -0,0 +1,26 @@
import { HPacket } from "../../../protocol/hpacket";
import { HNavigatorRoom } from "./hnavigatorroom";
export class HNavigatorBlock {
constructor(packet: HPacket);
appendToPacket(packet: HPacket): void;
get searchCode(): string;
set searchCode(val: string);
get text(): string;
set text(val: string);
get actionAllowed(): number;
set actionAllowed(val: number);
get forceClosed(): boolean;
set forceClosed(val: boolean);
get viewMode(): number;
set viewMode(val: number);
get rooms(): HNavigatorRoom[];
set rooms(val: HNavigatorRoom[]);
}
@@ -0,0 +1,123 @@
import { HPacket } from "../../../protocol/hpacket.js";
import { HNavigatorRoom } from "./hnavigatorroom.js";
import util from "util";
export class HNavigatorBlock {
#searchCode;
#text;
#actionAllowed;
#forceClosed;
#viewMode;
#rooms = [];
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HNavigatorBlock {\n`
+ `${indent} searchCode: ${util.inspect(this.#searchCode, {colors: true})}\n`
+ `${indent} text: ${util.inspect(this.#text, {colors: true})}\n`
+ `${indent} actionAllowed: ${util.inspect(this.#actionAllowed, {colors: true})}\n`
+ `${indent} forceClosed: ${util.inspect(this.#forceClosed, {colors: true})}\n`
+ `${indent} viewMode: ${util.inspect(this.#viewMode, {colors: true})}\n`
+ `${indent} rooms: ${util.inspect(this.#rooms, {colors: true, maxArrayLength: 0})}\n`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HNavigatorBlock.constructor: packet must be an instance of HPacket");
}
[ this.#searchCode, this.#text, this.#actionAllowed, this.#forceClosed, this.#viewMode ]
= packet.read('SSiBi');
let count = packet.readInteger();
for (let i = 0; i < count; i++)
this.#rooms.push(new HNavigatorRoom(packet));
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HNavigatorBlock.appendToPacket: packet must be an instance of HPacket");
}
packet.append('SSiBi',
this.#searchCode, this.#text, this.#actionAllowed, this.#forceClosed, this.#viewMode);
packet.appendInt(this.#rooms.length);
for (let room of this.#rooms) {
room.appendToPacket(packet);
}
}
get searchCode() {
return this.#searchCode;
}
set searchCode(val) {
if(typeof val != 'string') {
throw new Error('HNavigatorBlock.searchCode: must be a string');
}
this.#searchCode = val;
}
get text () {
return this.#text ;
}
set text (val) {
if(typeof val != 'string') {
throw new Error('HNavigatorBlock.text : must be a string');
}
this.#text = val;
}
get actionAllowed() {
return this.#actionAllowed;
}
set actionAllowed(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorBlock.actionAllowed: must be an integer')
}
this.#actionAllowed = val;
}
get forceClosed() {
return this.#forceClosed;
}
set forceClosed(val) {
if(typeof val != 'boolean') {
throw new Error('HNavigatorBlock.forceClosed: must be a boolean');
}
this.#forceClosed = val;
}
get viewMode() {
return this.#viewMode;
}
set viewMode(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorBlock.viewMode: must be an integer')
}
this.#viewMode = val;
}
get rooms() {
return this.#rooms;
}
set rooms(val) {
if(!Array.isArray(val) || val.any(v => !(v instanceof HNavigatorRoom))) {
throw new Error('HNavigatorBlock.rooms: must be an array of HNavigatorRoom instances')
}
this.#rooms = val;
}
}
@@ -0,0 +1,76 @@
import { HPacket } from "../../../protocol/hpacket";
export class HNavigatorRoom {
constructor(packet: HPacket);
appendToPacket(packet: HPacket): void;
get flatId(): number;
set flatId(val: number);
get roomName(): string;
set roomName(val: string);
get ownerId(): number;
set ownerId(val: number);
get ownerName(): string;
set ownerName(val: string);
get doorMode(): number;
set doorMode(val: number);
get userCount(): number;
set userCount(val: number);
get maxUserCount(): number;
set maxUserCount(val: number);
get description(): string;
set description(val: string);
get tradeMode(): number;
set tradeMode(val: number);
get score(): number;
set score(val: number);
get ranking(): number;
set ranking(val: number);
get categoryId(): number;
set categoryId(val: number);
get tags(): string[];
set tags(val: string[]);
get officialRoomPicRef(): string | undefined;
set officialRoomPicRef(val: string | undefined);
get groupId(): number | undefined;
set groupId(val: number | undefined);
get groupName(): string | undefined;
set groupName(val: string | undefined);
get groupBadgeCode(): string | undefined;
set groupBadgeCode(val: string | undefined);
get roomAdName(): string | undefined;
set roomAdName(val: string | undefined);
get roomAdDescription(): string | undefined;
set roomAdDescription(val: string | undefined);
get roomAdExpiresInMin(): number | undefined;
set roomAdExpiresInMin(val: number | undefined);
get showOwner(): boolean;
set showOwner(val: boolean);
get allowPets(): boolean;
set allowPets(val: boolean);
get displayRoomEntryAd(): boolean;
set displayRoomEntryAd(val: boolean);
}
@@ -0,0 +1,412 @@
import { HPacket } from "../../../protocol/hpacket.js";
import util from "util";
export class HNavigatorRoom {
#flatId;
#roomName;
#ownerId;
#ownerName;
#doorMode;
#userCount;
#maxUserCount;
#description;
#tradeMode;
#score;
#ranking;
#categoryId;
#tags;
#officialRoomPicRef;
#groupId;
#groupName;
#groupBadgeCode;
#roomAdName;
#roomAdDescription;
#roomAdExpiresInMin;
#showOwner;
#allowPets;
#displayRoomEntryAd;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HNavigatorRoom {\n`
+ `${indent} flatId: ${util.inspect(this.#flatId, {colors: true})}\n`
+ `${indent} roomName: ${util.inspect(this.#roomName, {colors: true})}\n`
+ `${indent} ownerId: ${util.inspect(this.#ownerId, {colors: true})}\n`
+ `${indent} ownerName: ${util.inspect(this.#ownerName, {colors: true})}\n`
+ `${indent} doorMode: ${util.inspect(this.#doorMode, {colors: true})}\n`
+ `${indent} userCount: ${util.inspect(this.#userCount, {colors: true})}\n`
+ `${indent} maxUserCount: ${util.inspect(this.#maxUserCount, {colors: true})}\n`
+ `${indent} description: ${util.inspect(this.#description, {colors: true})}\n`
+ `${indent} tradeMode: ${util.inspect(this.#tradeMode, {colors: true})}\n`
+ `${indent} score: ${util.inspect(this.#score, {colors: true})}\n`
+ `${indent} ranking: ${util.inspect(this.#ranking, {colors: true})}\n`
+ `${indent} categoryId: ${util.inspect(this.#categoryId, {colors: true})}\n`
+ `${indent} tags: ${util.inspect(this.#tags, {colors: true})}\n`
+ `${this.#officialRoomPicRef !== undefined ? `${indent} officialRoomPicRef: ${util.inspect(this.#officialRoomPicRef, {colors: true})}\n` : ''}`
+ `${this.#groupId !== undefined && this.#groupName !== undefined && this.#groupBadgeCode !== undefined ? `${indent} groupId: ${util.inspect(this.#groupId, {colors: true})}\n` : ''}`
+ `${this.#groupId !== undefined && this.#groupName !== undefined && this.#groupBadgeCode !== undefined ? `${indent} groupName: ${util.inspect(this.#groupName, {colors: true})}\n` : ''}`
+ `${this.#groupId !== undefined && this.#groupName !== undefined && this.#groupBadgeCode !== undefined ? `${indent} groupBadgeCode: ${util.inspect(this.#groupBadgeCode, {colors: true})}\n` : ''}`
+ `${this.#roomAdName !== undefined && this.#roomAdDescription !== undefined && this.#roomAdExpiresInMin !== undefined ? `${indent} roomAdName: ${util.inspect(this.#roomAdName, {colors: true})}\n` : ''}`
+ `${this.#roomAdName !== undefined && this.#roomAdDescription !== undefined && this.#roomAdExpiresInMin !== undefined ? `${indent} roomAdDescription: ${util.inspect(this.#roomAdDescription, {colors: true})}\n` : ''}`
+ `${this.#roomAdName !== undefined && this.#roomAdDescription !== undefined && this.#roomAdExpiresInMin !== undefined ? `${indent} roomAdExpiresInMin: ${util.inspect(this.#roomAdExpiresInMin, {colors: true})}\n` : ''}`
+ `${indent} showOwner: ${util.inspect(this.#showOwner, {colors: true})}\n`
+ `${indent} allowPets: ${util.inspect(this.#allowPets, {colors: true})}\n`
+ `${indent} displayRoomEntryAd: ${util.inspect(this.#displayRoomEntryAd, {colors: true})}\n`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HNavigatorRoom.constructor: packet must be an instance of HPacket");
}
[ this.#flatId, this.#roomName, this.#ownerId, this.#ownerName, this.#doorMode, this.#userCount,
this.#maxUserCount, this.#description, this.#tradeMode, this.#score, this.#ranking, this.#categoryId ]
= packet.read('iSiSiiiSiiii');
this.#tags = packet.read('S'.repeat(packet.readInteger()));
let multiUse = packet.readInteger();
if ((multiUse & 1) > 0)
this.#officialRoomPicRef = packet.readString();
if ((multiUse & 2) > 0) {
this.#groupId = packet.readInteger();
this.#groupName = packet.readString();
this.#groupBadgeCode = packet.readString();
}
if ((multiUse & 4) > 0) {
this.#roomAdName = packet.readString();
this.#roomAdDescription = packet.readString();
this.#roomAdExpiresInMin = packet.readInteger();
}
this.#showOwner = (multiUse & 8) > 0;
this.#allowPets = (multiUse & 16) > 0;
this.#displayRoomEntryAd = (multiUse & 32) > 0;
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HNavigatorRoom.appendToPacket: packet must be an instance of HPacket");
}
packet.append('iSiSiiiSiiii',
this.#flatId, this.#roomName, this.#ownerId, this.#ownerName, this.#doorMode, this.#userCount,
this.#maxUserCount, this.#description, this.#tradeMode, this.#score, this.#ranking, this.#categoryId);
packet.appendInt(this.#tags.length);
packet.append('S'.repeat(this.#tags.length), ...this.#tags);
let multiUse = 0;
let objectsToAppend = [];
let structureToAppend = "";
if (this.#officialRoomPicRef !== undefined) {
multiUse |= 1;
objectsToAppend.push(this.#officialRoomPicRef);
structureToAppend += "S";
}
if (this.#groupId !== undefined && this.#groupName !== undefined && this.#groupBadgeCode !== undefined) {
multiUse |= 2;
objectsToAppend.push(this.#groupId, this.#groupName, this.#groupBadgeCode);
structureToAppend += "iSS";
}
if (this.#roomAdName !== undefined && this.#roomAdDescription !== undefined && this.#roomAdExpiresInMin !== undefined) {
multiUse |= 4;
objectsToAppend.push(this.#roomAdName, this.#roomAdDescription, this.#roomAdExpiresInMin);
structureToAppend += "SSi"
}
if (this.#showOwner) multiUse |= 8;
if (this.#allowPets) multiUse |= 16;
if (this.#displayRoomEntryAd) multiUse |= 32;
packet.appendInt(multiUse);
packet.append(structureToAppend, ...objectsToAppend);
}
get flatId() {
return this.#flatId;
}
set flatId(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.flatId: must be an integer')
}
this.#flatId = val;
}
get roomName() {
return this.#roomName;
}
set roomName(val) {
if(typeof val != 'string') {
throw new Error('HNavigatorRoom.roomName: must be a string');
}
this.#roomName = val;
}
get ownerId() {
return this.#ownerId;
}
set ownerId(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.ownerId: must be an integer')
}
this.#ownerId = val;
}
get ownerName() {
return this.#ownerName;
}
set ownerName(val) {
if(typeof val != 'string') {
throw new Error('HNavigatorRoom.ownerName: must be a string');
}
this.#ownerName = val;
}
get doorMode() {
return this.#doorMode;
}
set doorMode(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.doorMode: must be an integer')
}
this.#doorMode = val;
}
get userCount() {
return this.#userCount;
}
set userCount(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.userCount: must be an integer')
}
this.#userCount = val;
}
get maxUserCount() {
return this.#maxUserCount;
}
set maxUserCount(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.maxUserCount: must be an integer')
}
this.#maxUserCount = val;
}
get description() {
return this.#description;
}
set description(val) {
if(typeof val != 'string') {
throw new Error('HNavigatorRoom.description: must be a string');
}
this.#description = val;
}
get tradeMode() {
return this.#tradeMode;
}
set tradeMode(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.tradeMode: must be an integer')
}
this.#tradeMode = val;
}
get score() {
return this.#score;
}
set score(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.score: must be an integer')
}
this.#score = val;
}
get ranking() {
return this.#ranking;
}
set ranking(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.ranking: must be an integer')
}
this.#ranking = val;
}
get categoryId() {
return this.#categoryId;
}
set categoryId(val) {
if(!Number.isInteger(val)) {
throw new Error('HNavigatorRoom.categoryId: must be an integer')
}
this.#categoryId = val;
}
get tags() {
return this.#tags;
}
set tags(val) {
if(!Array.isArray(val) || val.any(v => typeof v != 'string')) {
throw new Error('HNavigatorRoom.tags: must be an array of string')
}
this.#tags = val;
}
get officialRoomPicRef() {
return this.#officialRoomPicRef;
}
set officialRoomPicRef(val) {
if(typeof val != 'string' && val !== undefined) {
throw new Error('HNavigatorRoom.officialRoomPicRef: must be a string or undefined');
}
this.#officialRoomPicRef = val;
}
get groupId() {
return this.#groupId;
}
set groupId(val) {
if(!Number.isInteger(val) && val !== undefined) {
throw new Error('HNavigatorRoom.groupId: must be an integer or undefined')
}
this.#groupId = val;
}
get groupName() {
return this.#groupName;
}
set groupName(val) {
if(typeof val != 'string' && val !== undefined) {
throw new Error('HNavigatorRoom.groupName: must be a string or undefined');
}
this.#groupName = val;
}
get groupBadgeCode() {
return this.#groupBadgeCode;
}
set groupBadgeCode(val) {
if(typeof val != 'string' && val !== undefined) {
throw new Error('HNavigatorRoom.groupBadgeCode: must be a string or undefined');
}
this.#groupBadgeCode = val;
}
get roomAdName() {
return this.#roomAdName;
}
set roomAdName(val) {
if(typeof val != 'string' && val !== undefined) {
throw new Error('HNavigatorRoom.roomAdName: must be a string or undefined');
}
this.#roomAdName = val;
}
get roomAdDescription() {
return this.#roomAdDescription;
}
set roomAdDescription(val) {
if(typeof val != 'string' && val !== undefined) {
throw new Error('HNavigatorRoom.roomAdDescription: must be a string or undefined');
}
this.#roomAdDescription = val;
}
get roomAdExpiresInMin() {
return this.#roomAdExpiresInMin;
}
set roomAdExpiresInMin(val) {
if(!Number.isInteger(val) && val !== undefined) {
throw new Error('HNavigatorRoom.roomAdExpiresInMin: must be an integer or undefined')
}
this.#roomAdExpiresInMin = val;
}
get showOwner() {
return this.#showOwner;
}
set showOwner(val) {
if(typeof val != 'boolean') {
throw new Error('HNavigatorRoom.showOwner: must be a boolean');
}
this.#showOwner = val;
}
get allowPets() {
return this.#allowPets;
}
set allowPets(val) {
if(typeof val != 'boolean') {
throw new Error('HNavigatorRoom.allowPets: must be a boolean');
}
this.#allowPets = val;
}
get displayRoomEntryAd() {
return this.#displayRoomEntryAd;
}
set displayRoomEntryAd(val) {
if(typeof val != 'boolean') {
throw new Error('HNavigatorRoom.displayRoomEntryAd: must be a boolean');
}
this.#displayRoomEntryAd = val;
}
}
@@ -0,0 +1,18 @@
import { HPacket } from "../../../protocol/hpacket";
import { HNavigatorBlock } from "./hnavigatorblock";
export class HNavigatorSearchResult {
constructor(packet: HPacket);
appendToPacket(packet: HPacket): void;
get searchCode(): string;
set searchCode(val: string);
get filteringData(): string;
set filteringData(val: string);
get blocks(): HNavigatorBlock[];
set blocks(val: HNavigatorBlock[]);
}
@@ -0,0 +1,79 @@
import { HPacket } from "../../../protocol/hpacket.js";
import { HNavigatorBlock } from "./hnavigatorblock.js";
import util from "util";
export class HNavigatorSearchResult {
#searchCode;
#filteringData;
#blocks = [];
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HNavigatorSearchResult {\n`
+ `${indent} searchCode: ${util.inspect(this.#searchCode, {colors: true})}\n`
+ `${indent} filteringData: ${util.inspect(this.#filteringData, {colors: true})}\n`
+ `${indent} blocks: ${util.inspect(this.#blocks, {colors: true, maxArrayLength: 0})}\n`
+ `${indent}}`;
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HNavigatorSearchResult.constructor: packet must be an instance of HPacket");
}
[ this.#searchCode, this.#filteringData ] = packet.read('SS');
let count = packet.readInteger();
for (let i = 0; i < count; i++)
this.#blocks.push(new HNavigatorBlock(packet));
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HNavigatorSearchResult.appendToPacket: packet must be an instance of HPacket");
}
packet.append('SS',
this.#searchCode, this.#filteringData);
packet.appendInt(this.#blocks.length);
for (let block of this.#blocks)
block.appendToPacket(packet);
}
get searchCode() {
return this.#searchCode;
}
set searchCode(val) {
if(typeof val != 'string') {
throw new Error('HNavigatorSearchResult.searchCode: must be a string');
}
this.#searchCode = val;
}
get filteringData() {
return this.#filteringData;
}
set filteringData(val) {
if(typeof val != 'string') {
throw new Error('HNavigatorSearchResult.filteringData: must be a string');
}
this.#filteringData = val;
}
get blocks() {
return this.#blocks;
}
set blocks(val) {
if(!Array.isArray(val) || val.any(v => !(v instanceof HNavigatorBlock))) {
throw new Error('HNavigatorSearchResult.blocks: must be an array of HNavigatorBlock instances')
}
this.#blocks = val;
}
}
@@ -0,0 +1,41 @@
import { HPacket } from "../../../protocol/hpacket";
export class HRoomChatSettings {
constructor(packet: HPacket);
/**
* Append the room chat settings to an existing packet
* @param packet Packet to append to
*/
appendToPacket(packet: HPacket): void;
/**
* What chat mode does the room use
*/
get mode(): number;
set mode(val: number);
/**
* How wide are the bubbles
*/
get bubbleWidth(): number;
set bubbleWidth(val: number);
/**
* What is the scrollspeed
*/
get scrollSpeed(): number;
set scrollSpeed(val: number);
/**
* What is the hear range
*/
get fullHearRange(): number;
set fullHearRange(val: number);
/**
* What is the flood sensitivity level
*/
get floodSensitivity(): number;
set floodSensitivity(val: number);
}
@@ -0,0 +1,99 @@
import util from "util";
import { HPacket } from "../../../protocol/hpacket.js";
export class HRoomChatSettings {
#mode;
#bubbleWidth;
#scrollSpeed;
#fullHearRange;
#floodSensitivity;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HRoomChatSettings {\n`
+ `${indent} mode: ${util.inspect(this.#mode, {colors: true})}\n`
+ `${indent} bubbleWidth: ${util.inspect(this.#bubbleWidth, {colors: true})}\n`
+ `${indent} scrollSpeed: ${util.inspect(this.#scrollSpeed, {colors: true})}\n`
+ `${indent} fullHearRange: ${util.inspect(this.#fullHearRange, {colors: true})}\n`
+ `${indent} floodSensitivity: ${util.inspect(this.#floodSensitivity, {colors: true})}\n`
+ `${indent}}`
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HRoomChatSettings.constructor: packet must be an instance of HPacket");
}
[ this.#mode, this.#bubbleWidth, this.#scrollSpeed, this.#fullHearRange, this.#floodSensitivity ]
= packet.read('iiiii');
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HRoomModSettings.appendToPacket: packet must be an instance of HPacket")
}
packet.append('iiiii',
this.#mode, this.#bubbleWidth, this.#scrollSpeed, this.#fullHearRange, this.#floodSensitivity);
}
get mode() {
return this.#mode;
}
set mode(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomChatSettings.mode: must be an integer")
}
this.#mode = val;
}
get bubbleWidth() {
return this.#bubbleWidth;
}
set bubbleWidth(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomChatSettings.bubbleWidth: must be an integer")
}
this.#bubbleWidth = val;
}
get scrollSpeed() {
return this.#scrollSpeed;
}
set scrollSpeed(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomChatSettings.scrollSpeed: must be an integer")
}
this.#scrollSpeed = val;
}
get fullHearRange() {
return this.#fullHearRange;
}
set fullHearRange(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomChatSettings.fullHearRange: must be an integer")
}
this.#fullHearRange = val;
}
get floodSensitivity() {
return this.#floodSensitivity;
}
set floodSensitivity(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomChatSettings.floodSensitivity: must be an integer")
}
this.#floodSensitivity = val;
}
}
@@ -0,0 +1,29 @@
import { HPacket } from "../../../protocol/hpacket";
export class HRoomModSettings {
constructor(packet: HPacket);
/**
* Append the room moderation settings to an existing packet
* @param packet Packet to append to
*/
appendToPacket(packet: HPacket): void;
/**
* Which moderation level do you need to mute people
*/
get whoCanMute(): number;
set whoCanMute(val: number);
/**
* Which moderation level do you need to kick people
*/
get whoCanKick(): number;
set whoCanKick(val: number);
/**
* Which moderation level do you need to ban people
*/
get whoCanBan(): number;
set whoCanBan(val: number);
}
@@ -0,0 +1,70 @@
import util from "util";
import { HPacket } from "../../../protocol/hpacket.js";
export class HRoomModSettings {
#whoCanMute;
#whoCanKick;
#whoCanBan;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HRoomModSettings {\n`
+ `${indent} whoCanMute: ${util.inspect(this.#whoCanMute, {colors: true})}\n`
+ `${indent} whoCanKick: ${util.inspect(this.#whoCanKick, {colors: true})}\n`
+ `${indent} whoCanBan: ${util.inspect(this.#whoCanBan, {colors: true})}\n`
+ `${indent}}`
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HRoomModSettings.constructor: packet must be an instance of HPacket");
}
[ this.#whoCanMute, this.#whoCanKick, this.#whoCanBan ] = packet.read('iii');
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HRoomModSettings.appendToPacket: packet must be an instance of HPacket")
}
packet.append('iii',
this.#whoCanMute, this.#whoCanKick, this.#whoCanBan);
}
get whoCanMute() {
return this.#whoCanMute;
}
set whoCanMute(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomModSettings.whoCanMute: must be an integer in the range of [0, 5]")
}
this.#whoCanMute = val;
}
get whoCanKick() {
return this.#whoCanKick;
}
set whoCanKick(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomModSettings.whoCanKick: must be an integer in the range of [0, 5]")
}
this.#whoCanKick = val;
}
get whoCanBan() {
return this.#whoCanBan;
}
set whoCanBan(val) {
if(!Number.isInteger(val)) {
throw new Error("HRoomModSettings.whoCanBan: must be an integer in the range of [0, 5]")
}
this.#whoCanBan = val;
}
}
@@ -0,0 +1,74 @@
import { HPacket } from "../../../protocol/hpacket";
import { HNavigatorRoom } from "../navigator/hnavigatorroom";
import { HRoomModSettings } from "./hroommodsettings";
import { HRoomChatSettings } from "./hroomchatsettings";
export class HRoomResult {
constructor(packet: HPacket);
/**
* Construct packet with header id containing the room result
* @param headerId Header id of packet
*/
constructPacket(headerId: number): HPacket;
/**
* Append the room result to an existing packet
* @param packet Packet to append to
*/
appendToPacket(packet: HPacket): void;
/**
* Whether you would be entering the room
*/
get isEnterRoom(): boolean;
set isEnterRoom(val: boolean);
/**
* The room data
*/
get data(): HNavigatorRoom;
set data(val: HNavigatorRoom);
/**
* Whether you come from another room (using a teleport)
*/
get isRoomForward(): boolean;
set isRoomForward(val: boolean);
/**
* Whether the room is a staff pick
*/
get isStaffPick(): boolean;
set isStaffPick(val: boolean);
/**
* Whether you are a member of the room's group
*/
get isGroupMember(): boolean;
set isGroupMember(val: boolean);
/**
* Whether room mute is enable in the room
*/
get allInRoomMuted(): boolean;
set allInRoomMuted(val: boolean);
/**
* Who can mute, kick and/or ban
*/
get moderationSettings(): HRoomModSettings;
set moderationSettings(val: HRoomModSettings);
/**
* Whether you can mute other people
*/
get youCanMute(): boolean;
set youCanMute(val: boolean);
/**
* The chat settings
*/
get chatSettings(): HRoomChatSettings;
set chatSettings(val: HRoomChatSettings);
}
@@ -0,0 +1,178 @@
import { HPacket } from "../../../protocol/hpacket.js";
import util from "util";
import { HNavigatorRoom } from "../navigator/hnavigatorroom.js";
import { HRoomModSettings } from "./hroommodsettings.js";
import { HRoomChatSettings } from "./hroomchatsettings.js";
export class HRoomResult {
#isEnterRoom;
#data;
#isRoomForward;
#isStaffPick;
#isGroupMember;
#moderationSettings;
#allInRoomMuted;
#youCanMute;
#chatSettings;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HRoomResult {\n`
+ `${indent} isEnterRoom: ${util.inspect(this.#isEnterRoom, {colors: true})}\n`
+ `${indent} data: ${util.inspect(this.#data, {colors: true})}\n`
+ `${indent} isRoomForward: ${util.inspect(this.#isRoomForward, {colors: true})}\n`
+ `${indent} isStaffPick: ${util.inspect(this.#isStaffPick, {colors: true})}\n`
+ `${indent} isGroupMember: ${util.inspect(this.#isGroupMember, {colors: true})}\n`
+ `${indent} moderationSettings: ${util.inspect(this.#moderationSettings, {colors: true})}\n`
+ `${indent} allInRoomMuted: ${util.inspect(this.#allInRoomMuted, {colors: true})}\n`
+ `${indent} youCanMute: ${util.inspect(this.#youCanMute, {colors: true})}\n`
+ `${indent} chatSettings: ${util.inspect(this.#chatSettings, {colors: true})}\n`
+ `${indent}}`
}
constructor(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HRoomResult.constructor: packet must be an instance of HPacket");
}
this.#isEnterRoom = packet.readBoolean();
this.#data = new HNavigatorRoom(packet);
[ this.#isRoomForward, this.#isStaffPick, this.#isGroupMember, this.#allInRoomMuted ]
= packet.read('BBBB');
this.#moderationSettings = new HRoomModSettings(packet);
this.#youCanMute = packet.readBoolean();
this.#chatSettings = new HRoomChatSettings(packet);
}
constructPacket(headerId) {
if(!Number.isInteger(headerId)) {
throw new Error("HRoomResult.constructPacket: headerId must be an integer")
}
let packet = new HPacket(headerId);
this.appendToPacket(packet);
return packet;
}
appendToPacket(packet) {
if(!(packet instanceof HPacket)) {
throw new Error("HRoomResult.appendToPacket: packet must be an instance of HPacket")
}
packet.appendBoolean(this.#isEnterRoom);
this.#data.appendToPacket(packet);
packet.append('BBBB',
this.#isRoomForward, this.#isStaffPick, this.#isGroupMember, this.#allInRoomMuted)
this.#moderationSettings.appendToPacket(packet);
packet.appendBoolean(this.#youCanMute);
this.#chatSettings.appendToPacket(packet);
}
get isEnterRoom() {
return this.#isEnterRoom;
}
set isEnterRoom(val) {
if(typeof val != 'boolean') {
throw new Error("HRoomResult.isEnterRoom: must be a boolean");
}
this.#isEnterRoom = val;
}
get data() {
return this.#data;
}
set data(val) {
if(!(val instanceof HNavigatorRoom)) {
throw new Error("HRoomResult.data: must be an instance of HNavigatorRoom");
}
this.#data = val;
}
get isRoomForward() {
return this.#isRoomForward;
}
set isRoomForward(val) {
if(typeof val != 'boolean') {
throw new Error("HRoomResult.isRoomForward: must be a boolean");
}
this.#isRoomForward = val;
}
get isStaffPick() {
return this.#isStaffPick;
}
set isStaffPick(val) {
if(typeof val != 'boolean') {
throw new Error("HRoomResult.isStaffPick: must be a boolean");
}
this.#isStaffPick = val;
}
get isGroupMember() {
return this.#isGroupMember;
}
set isGroupMember(val) {
if(typeof val != 'boolean') {
throw new Error("HRoomResult.isGroupMember: must be a boolean");
}
this.#isGroupMember = val;
}
get allInRoomMuted() {
return this.#allInRoomMuted;
}
set allInRoomMuted(val) {
if(typeof val != 'boolean') {
throw new Error("HRoomResult.allInRoomMuted: must be a boolean");
}
this.#allInRoomMuted = val;
}
get moderationSettings() {
return this.#moderationSettings;
}
set moderationSettings(val) {
if(!(val instanceof HRoomModSettings)) {
throw new Error("HRoomResult.moderationSettings: must be an instance of HRoomModSettings");
}
this.#moderationSettings = val;
}
get youCanMute() {
return this.#youCanMute;
}
set youCanMute(val) {
if(typeof val != 'boolean') {
throw new Error("HRoomResult.youCanMute: must be a boolean");
}
this.#youCanMute = val;
}
get chatSettings() {
return this.#chatSettings;
}
set chatSettings(val) {
if(!(val instanceof HRoomChatSettings)) {
throw new Error("HRoomResult.chatSettings: must be an instance of HRoomChatSettings");
}
this.#chatSettings = val;
}
}
@@ -0,0 +1,51 @@
import { Hotel } from "./hotel";
export type FurniData = {
roomitemtypes: {
furnitype: FloorItemData[]
},
wallitemtypes: {
furnitype: WallItemData[]
},
getFloorItemByTypeId(id: number): FloorItemData,
getWallItemByTypeId(id: number): WallItemData,
getFloorItemByClassName(classname: string): FloorItemData,
getWallItemByClassName(classname: string): WallItemData
};
export namespace FurniDataUtils {
export function fetch(hotel: Hotel): Promise<FurniData>;
}
export type WallItemData = {
id: number;
classname: string;
revision: number;
category: string;
name: string;
description: string;
adurl: string;
specialtype: number;
furniline: string;
environment: string;
rare: boolean;
offerid: number;
buyout: boolean;
rentofferid: number;
rentbuyout: boolean;
bc: boolean;
excludeddynamic: boolean;
}
export type FloorItemData = WallItemData & {
defaultdir: number;
xdim: number;
ydim: number;
partcolors: {
color: string[];
};
customparams: string;
canstandon: boolean;
cansiton: boolean;
canlayon: boolean;
}
@@ -0,0 +1,46 @@
import fetch from 'node-fetch';
import { Hotel } from "./hotel.js";
const FurniDataUtils = Object.freeze({
async fetch(hotel) {
if (!Hotel.identify(hotel))
throw new Error("FurniData.fetch: hotel must be a value of Hotel");
let furniData = await (await fetch(`${hotel}gamedata/furnidata_json/1`)).json();
furniData.getFloorItemByTypeId = getFloorItemByTypeId;
furniData.getWallItemByTypeId = getWallItemByTypeId;
furniData.getFloorItemByClassName = getFloorItemByClassName;
furniData.getWallItemByClassName = getWallItemByClassName;
return freeze(furniData);
}
});
function freeze(furniData) {
furniData.roomitemtypes.furnitype = furniData.roomitemtypes.furnitype.map(item => Object.freeze(item));
furniData.wallitemtypes.furnitype = furniData.wallitemtypes.furnitype.map(item => Object.freeze(item));
furniData.roomitemtypes.furnitype = Object.freeze(furniData.roomitemtypes.furnitype);
furniData.wallitemtypes.furnitype = Object.freeze(furniData.wallitemtypes.furnitype);
furniData.roomitemtypes = Object.freeze(furniData.roomitemtypes);
furniData.wallitemtypes = Object.freeze(furniData.wallitemtypes);
return Object.freeze(furniData);
}
function getFloorItemByTypeId(id) {
return this.roomitemtypes.furnitype.find(item => item.id === id);
}
function getWallItemByTypeId(id) {
return this.wallitemtypes.furnitype.find(item => item.id === id);
}
function getFloorItemByClassName(classname) {
return this.roomitemtypes.furnitype.find(item => item.classname === classname);
}
function getWallItemByClassName(classname) {
return this.wallitemtypes.furnitype.find(item => item.classname === classname);
}
export { FurniDataUtils };
@@ -0,0 +1,16 @@
export enum Hotel {
NL = 'https://www.habbo.nl/',
ES = 'https://www.habbo.es/',
DE = 'https://www.habbo.de/',
FR = 'https://www.habbo.fr/',
IT = 'https://www.habbo.it/',
FI = 'https://www.habbo.fi/',
COM = 'https://www.habbo.com/',
COMTR = 'https://www.habbo.com.tr/',
COMBR = 'https://www.habbo.com.br/',
SANDBOX = 'https://sandbox.habbo.com/'
}
export namespace Hotel {
export function fromHost(host: string): Hotel | null;
}
@@ -0,0 +1,47 @@
const Hotel = Object.freeze({
NL: 'https://www.habbo.nl/',
ES: 'https://www.habbo.es/',
DE: 'https://www.habbo.de/',
FR: 'https://www.habbo.fr/',
IT: 'https://www.habbo.it/',
FI: 'https://www.habbo.fi/',
COM: 'https://www.habbo.com/',
COMTR: 'https://www.habbo.com.tr/',
COMBR: 'https://www.habbo.com.br/',
SANDBOX: 'https://sandbox.habbo.com/',
fromHost(host) {
switch (host) {
case 'game-nl.habbo.com':
return Hotel.NL;
case 'game-es.habbo.com':
return Hotel.ES;
case 'game-de.habbo.com':
return Hotel.DE;
case 'game-fr.habbo.com':
return Hotel.FR;
case 'game-it.habbo.com':
return Hotel.IT;
case 'game-fi.habbo.com':
return Hotel.FI;
case 'game-us.habbo.com':
return Hotel.COM;
case 'game-br.habbo.com':
return Hotel.COMBR;
case 'game-tr.habbo.com':
return Hotel.COMTR;
case 'game-s2.habbo.com':
return Hotel.SANDBOX;
}
return null;
},
identify(hotel) {
for(let key in this)
if(this[key] === hotel)
return key;
}
});
export { Hotel };
@@ -0,0 +1,29 @@
import { HDirection } from "../../../protocol/hdirection";
import { HPacket } from "../../../protocol/hpacket";
export class AwaitingPacket {
constructor(headerName: string, direction: HDirection, maxWaitingTimeMillis: number);
constructor(headerName: string, direction: HDirection, maxWaitingTimeMillis: number, setBlocked: boolean);
/**
* 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;
}
@@ -0,0 +1,112 @@
import { HDirection } from "../../../protocol/hdirection.js";
import { HPacket } from "../../../protocol/hpacket.js";
import { HMessage } from "../../../protocol/hmessage.js";
export class AwaitingPacket {
#headerName;
#direction;
#packet;
#received = false;
#conditions = [];
#start;
#minWait = 0;
#setBlocked = false;
constructor(headerName, direction, maxWaitingTimeMillis, setBlocked = false) {
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 (typeof setBlocked !== 'boolean') {
throw new Error("AwaitingPacket.constructor: setBlocked must be a boolean");
}
if(maxWaitingTimeMillis < 30) {
maxWaitingTimeMillis = 30;
}
setTimeout(() => {
this.#received = true;
}, maxWaitingTimeMillis);
this.#start = Date.now();
this.#direction = direction;
this.#headerName = headerName;
this.#setBlocked = setBlocked;
}
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();
}
get blocksHMessage() {
return this.#setBlocked;
}
}
@@ -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[]): Promise<HPacket | undefined>;
/**
* Asynchronously await multiple packets
* @param packets
*/
awaitMultiplePackets(...packets: AwaitingPacket[]): Promise<(HPacket | undefined)[]>;
/**
* Clear all awaiting packets
*/
clear();
}
@@ -0,0 +1,103 @@
import { Extension } from "../../extension.js";
import { AwaitingPacket } from "./awaitingpacket.js";
import { HDirection } from "../../../protocol/hdirection.js";
export 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 => {
if (p.blocksHMessage)
hMessage.blocked = true;
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 => {
if (p.blocksHMessage)
hMessage.blocked = true;
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 = [];
}
}
@@ -0,0 +1,39 @@
import { HDirection } from "../../protocol/hdirection.js";
import { Extension } from "../extension.js";
import { HInventoryItem } from "../parsers/hinventoryitem.js";
export class GInventory {
#items = new Map();
#loaded = false;
#loadedListener;
constructor(ext, loadedListener = undefined) {
if (!(ext instanceof Extension)) {
throw new Error("GInventory.constructor: ext must be an instance of Extension");
}
if (typeof loadedListener !== "undefined" && !(typeof loadedListener === "function" || loadedListener.length === 1)) {
throw new Error("GInventory.constructor: loadedListener must be undefined or a function with 1 parameter");
}
this.#loadedListener = loadedListener;
ext.interceptByNameOrHash(HDirection.TOCLIENT, "FurniList", (hMessage) => this.#onFurniList(hMessage));
ext.interceptByNameOrHash(HDirection.TOCLIENT, "FurniListAddOrUpdate", (hMessage) => this.#onFurniListAddOrUpdate(hMessage));
ext.interceptByNameOrHash(HDirection.TOCLIENT, "FurniListRemove", (hMessage) => this.#onFurniListRemove(hMessage));
}
#onFurniList(hMessage) {
let items = HInventoryItem.parse(hMessage.getPacket());
items.forEach(item => this.#items.set(item.id, item));
}
#onFurniListAddOrUpdate(hMessage) {
let item = new HInventoryItem(hMessage.getPacket());
this.#items.set(item.id, item);
}
#onFurniListRemove(hMessage) {
let id = hMessage.getPacket().readInteger();
}
}
@@ -0,0 +1,28 @@
import { Extension } from "../../extension";
import { GHeightMapTile } from "./gheightmaptile";
export class GHeightMap {
constructor(ext: Extension);
getTileIndex(x: number, y: number): number;
getCoords(index: number): [ number, number ];
getTileValue(x: number, y: number): number;
getTileHeight(x: number, y: number): number;
isRoomTile(x: number, y: number): boolean;
isStackingBlocked(x: number, y: number): boolean;
getTile(x: number, y: number): GHeightMapTile;
get tiles(): GHeightMapTile[];
get width(): number;
get height(): number;
set changeListener(listener: (heightMap: GHeightMap) => void);
}
@@ -0,0 +1,129 @@
import { Extension } from "../../extension.js";
import { HDirection } from "../../../protocol/hdirection.js";
import util from "util";
export class GHeightMap {
#width;
#height;
#tiles;
#heightMapChangeListener;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}GHeightMap {\n`
+ `${indent} width: ${util.inspect(this.width, {colors: true})}\n`
+ `${indent} height: ${util.inspect(this.height, {colors: true})}\n`
+ `${indent} tiles: ${util.inspect(this.tiles, {colors: true, maxArrayLength: 0})}\n`
+ `${indent}}`;
}
constructor(ext) {
if (!(ext instanceof Extension)) {
throw new Error("GHeightMap.constructor: ext must be an instance of Extension");
}
ext.interceptByNameOrHash(HDirection.TOCLIENT, 'HeightMap', this.#onHeightMap.bind(this));
ext.interceptByNameOrHash(HDirection.TOCLIENT, 'HeightMapUpdate', this.#onHeightMapUpdate.bind(this));
}
#onHeightMap = (hMessage) => {
const packet = hMessage.getPacket();
let tileCount;
[ this.#width, tileCount ] = packet.read('ii');
this.#height = tileCount / this.#width;
this.#tiles = packet.read('s'.repeat(tileCount));
if (this.#heightMapChangeListener) {
this.#heightMapChangeListener(this);
}
}
#onHeightMapUpdate = (hMessage) => {
const packet = hMessage.getPacket();
const count = packet.readByte();
for (let i = 0; i < count; i++) {
const [x, y, value] = packet.read('bbs');
this.#tiles[this.getTileIndex(x, y)] = value;
}
if (this.#heightMapChangeListener) {
this.#heightMapChangeListener(this);
}
}
getTileIndex(x, y) {
return y * this.#width + x;
}
getCoords(index) {
let y = index % this.#width;
let x = (index - y) / this.#width;
return [ x, y ];
}
getTileValue(x, y) {
return this.#tiles[this.getTileIndex(x, y)];
}
#decodeTileHeight = (value) => {
return value < 0 ? -1 : Number((value & 16383) / 256);
}
#decodeIsStackingBlocked = (value) => {
return Boolean(value & 16384);
}
#decodeIsRoomTile = (value) => {
return value >= 0;
}
getTileHeight (x, y) {
if (x < 0 || x >= this.#width || y < 0 || y >= this.#height)
return -1;
return this.#decodeTileHeight(this.getTileValue(x, y));
}
isRoomTile(x, y) {
if (x < 0 || x >= this.#width || y < 0 || y >= this.#height)
return -1;
return this.#decodeIsRoomTile(this.getTileValue(x, y));
}
isStackingBlocked(x, y) {
if (x < 0 || x >= this.#width || y < 0 || y >= this.#height)
return -1;
return this.#decodeIsStackingBlocked(this.getTileValue(x, y));
}
getTile(x, y) {
return {
x: x,
y: y,
tileValue: this.getTileValue(x, y),
isRoomTile: this.isRoomTile(x, y),
tileHeight: this.getTileHeight(x, y),
isStackingBlocked: this.isStackingBlocked(x, y)
};
}
get tiles() {
return this.#tiles.map((value, index) => {
return this.getTile(...this.getCoords(index));
});
}
get width() {
return this.#width;
}
get height() {
return this.#height;
}
set changeListener(listener) {
this.#heightMapChangeListener = listener;
}
}
@@ -0,0 +1,8 @@
export interface GHeightMapTile {
x: number;
y: number;
tileValue: number;
isRoomTile: boolean;
tileHeight: number;
isStackingBlocked: boolean;
}
@@ -0,0 +1,13 @@
import { HPacket } from "../protocol/hpacket";
export class HostInfo {
constructor(packetlogger: string, version: string, attributes: Map<string, string>);
static fromPacket(packet: HPacket): HostInfo;
get packetlogger(): string;
get version(): string;
get attributes(): Map<string, string>;
}
@@ -0,0 +1,45 @@
import util from "util";
export class HostInfo {
#packetlogger;
#version;
#attributes;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HostInfo {\n`
+ `${indent} packetlogger: ${util.inspect(this.#packetlogger, {colors: true})}\n`
+ `${indent} version: ${util.inspect(this.#version, {colors: true})}\n`
+ `${indent} attributes: ${util.inspect(this.#attributes, {colors: true})}\n`
+ `${indent}}`
}
constructor(packetlogger, version, attributes) {
this.#packetlogger = packetlogger;
this.#version = version;
this.#attributes = attributes;
}
static fromPacket(packet) {
let [packetlogger, version, attributeCount] = packet.read('SSi');
let attributes = new Map();
for (let i = 0; i < attributeCount; i++) {
attributes.set(...packet.read('SS'));
}
return new HostInfo(packetlogger, version, attributes);
}
get packetlogger() {
return this.#packetlogger;
}
get version() {
return this.#version;
}
get attributes() {
return this.#attributes;
}
}
@@ -0,0 +1,4 @@
export enum HClient {
UNITY,
FLASH
}
@@ -0,0 +1,16 @@
/**
* Client type
* @readonly
* @enum {number}
*/
const HClient = Object.freeze({
UNITY: 0,
FLASH: 1,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HClient };
@@ -0,0 +1,4 @@
export enum HDirection {
TOCLIENT,
TOSERVER
}
@@ -0,0 +1,16 @@
/**
* Direction of packet
* @readonly
* @enum {number}
*/
const HDirection = Object.freeze({
TOCLIENT: 0,
TOSERVER: 1,
identify(val) {
for(let key in this)
if(this[key] === val)
return key;
}
});
export { HDirection };
@@ -0,0 +1,50 @@
import { HDirection } from "./hdirection";
import { HPacket } from "./hpacket";
export class HMessage {
constructor(fromString: String | string);
constructor(message: HMessage);
constructor(packet: HPacket, direction: HDirection, index: number);
/**
* Get the private parameter #index
*/
getIndex(): number;
/**
* Change the private parameter #isBlocked
* @param val Boolean value to be set to private parameter #isBlocked
*/
set blocked(val: boolean);
/**
* Get the private parameter #isBlocked
*/
get blocked(): boolean;
/**
* Get the private parameter #hPacket
*/
getPacket(): HPacket;
/**
* Get the private parameter #direction
*/
getDestination(): HDirection;
/**
* Returns whether private #hPacket is corrupted
*/
isCorrupted(): boolean;
/**
* Convert the message to a string
*/
stringify(): string;
/**
* Compare other hMessage to hMessage (compares packet, direction and index)
* @param message hMessage to compare with current hMessage
*/
equals(message: HMessage): boolean;
}
@@ -0,0 +1,120 @@
import { HDirection } from "./hdirection.js";
import util from "util";
import { HPacket } from "./hpacket.js";
export class HMessage {
#hPacket;
#index;
#direction;
#isBlocked;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}HMessage {\n`
+ `${indent} getPacket(): \n${util.inspect(this.#hPacket, {colors: true, depth: depth + 2})}\n`
+ `${indent} getIndex(): ${util.inspect(this.#index, {colors: true})}\n`
+ `${indent} getDestination(): HDirection.\x1b[36m${HDirection.identify(this.#direction)}\x1b[0m\n`
+ `${indent} blocked: ${util.inspect(this.#isBlocked, {colors: true})}\n`
+ `${indent} stringify(): ${util.inspect(this.stringify(), {colors: true, maxStringLength: 50})}\n`
+ `${indent}}`;
}
constructor(...args) {
if(args.length > 0) {
if(typeof(args[0]) == 'string' || args[0] instanceof String) {
this.#constructFromString(args[0]);
return;
} else if(args[0] instanceof HMessage) {
this.#constructFromHMessage(args[0]);
return;
} else if(args[0] instanceof HPacket && args.length > 2 && (args[1] === HDirection.TOCLIENT || args[1] === HDirection.TOSERVER) && typeof(args[2]) == 'number') {
this.#constructFromHPacket(args[0], args[1], args[2]);
return;
}
}
throw new Error("HMessage.constructor: Invalid constructor arguments");
}
#constructFromString = (str) => {
let parts = str.split('\t');
for(let i = 4; i < parts.length; i++) {
parts[3] += "\t" + parts[i];
}
this.#isBlocked = parts[0] === "1";
this.#index = Number(parts[1]);
this.#direction = parts[2] === "TOCLIENT" ? HDirection.TOCLIENT : HDirection.TOSERVER;
let p = new HPacket(new Uint8Array(0));
p.constructFromString(parts[3]);
this.#hPacket = p;
}
#constructFromHMessage = (hMessage) => {
this.#isBlocked = hMessage.blocked;
this.#index = hMessage.getIndex();
this.#direction = hMessage.getDestination();
this.#hPacket = new HPacket(hMessage.getPacket());
}
#constructFromHPacket = (hPacket, direction, index) => {
this.#direction = direction;
this.#hPacket = hPacket;
this.#index = index;
this.#isBlocked = false;
}
getIndex() {
console.error("\x1b[31mHMessage.getIndex(): Deprecated method used, use the getter HMessage.index instead\x1b[0m");
return this.#index;
}
get blocked() {
return this.#isBlocked;
}
set blocked(val) {
if(typeof(val) !== "boolean") {
throw new Error("HMessage.blocked: must be a boolean");
}
this.#isBlocked = val;
}
setBlocked(block) {
console.error("\x1b[31mHMessage.setBlocked(): Deprecated method used, use the getter HMessage.blocked = ... instead\x1b[0m");
if(typeof(block) !== "boolean") {
throw new Error("HMessage.setBlocked: block must be a boolean");
}
this.#isBlocked = block;
}
isBlocked() {
console.error("\x1b[31mHMessage.isBlocked(): Deprecated method used, use the getter HMessage.blocked instead\x1b[0m");
return this.#isBlocked;
}
getPacket() {
return this.#hPacket;
}
getDestination() {
return this.#direction;
}
isCorrupted() {
return this.#hPacket.isCorrupted();
}
stringify() {
return (this.#isBlocked ? "1" : "0") + "\t" + this.#index + "\t" + (this.#direction === HDirection.TOCLIENT ? "TOCLIENT" : "TOSERVER") + "\t" + this.#hPacket.stringify();
}
equals(message) {
if(!(message instanceof HMessage)) return false;
return message.#hPacket.equals(this.#hPacket) && (message.#direction === this.#direction) && (message.#index === this.#index);
}
}
@@ -0,0 +1,499 @@
import { HDirection } from "./hdirection";
import { PacketInfoManager } from "../services/packetinfo/packetinfomanager";
export class HPacket {
constructor(bytes: Uint8Array);
constructor(packet: HPacket);
constructor(packet: String | string);
constructor(headerId: number);
constructor(headerId: number, bytes: Uint8Array);
constructor(identifier: String | string, direction: HDirection);
/**
* Get the packet as a string
*/
toString(): string;
/**
* Check if the packet's structure matches
* @param structure String structure to be compared to structure of packet
*/
//TODO structureEquals(structure: String | string): boolean;
/**
* isEOF
*/
isEOF(): number;
/**
* Change the private parameter #identifier
* @param val String identifier
*/
set identifier(val: string);
/**
* Change the private parameter #direction
* @param val HDirection (TOSERVER or TOCLIENT)
*/
set identifierDirection(val: HDirection);
/**
* Get private parameter #identifier
*/
get identifier(): string;
/**
* Get private parameter #identifierDirection
*/
get identifierDirection(): HDirection;
completePacket(packetInfoManager: PacketInfoManager): void;
/**
* Checks whether packet can be send to client
*/
canSendToClient(): boolean;
/**
* Checks whether packet can be send to server
*/
canSendToServer(): boolean;
canComplete(packetInfoManager: PacketInfoManager): boolean;
/**
* Checks whether packet is complete
*/
isPacketComplete(): boolean;
/**
* Return the private parameter #packetInBytes
*/
toBytes(): Uint8Array;
/**
* Return the private parameter #readIndex
*/
get readIndex(): number;
/**
* Change the private parameter #readIndex
* @param val Read index
*/
set readIndex(val: number);
/**
* Reset the private parameter #readIndex to it's starting value (6)
*/
resetReadIndex(): void;
/**
* Check if packet is corrupted
*/
isCorrupted(): boolean;
/**
* Read the headerId from packet
*/
headerId(): number;
/**
* Read the length from packet
*/
length(): number;
/**
* Get entire length of packet
*/
getBytesLength(): number;
/**
* Read byte/UInt8 from packet
* @param index Optional read index
*/
readByte(index?: number): number;
/**
* Read short/Int16 from packet
* @param index Optional read index
*/
readShort(index?: number): number;
/**
* Read unsigned short/UInt16 from packet
* @param index Optional read index
*/
readUShort(index?: number): number;
/**
* Read integer/Int32 from packet
* @param index Optional read index
*/
readInteger(index?: number): number;
/**
* Read float/Float32 from packet
* @param index Optional read index
*/
readFloat(index?: number): number;
/**
* Read double/Float64 from packet
* @param index Optional read index
*/
readDouble(index?: number): number;
/**
* Read byte[]/UInt8Array from packet
* @param length Length to read
* @param index Optional read index
*/
readBytes(length: number, index?: number): Uint8Array;
/**
* Read long/Int64 from packet
* @param index Optional read index
*/
readLong(index?: number): bigint;
/**
* Read string from packet
* @param index Optional read index
* @param charset Optional encoding charset (default: "latin1")
*/
readString(index?: number, charset?: BufferEncoding): string;
/**
* Read long string from packet
* @param index Optional read index
* @param charset Optional encoding charset (default: "latin1")
*/
readLongString(index?: number, charset?: BufferEncoding): string;
/**
* Read boolean from packet
* @param index Optional read index
*/
readBoolean(index?: number): boolean;
/**
* Read from packet in given structure: <br>
* b: byte / UInt8 <br>
* i: int / Int32 <br>
* s: short / Int16 <br>
* u: ushort / UInt16 <br>
* l: long / Int64 <br>
* d: double / Float64 <br>
* f: float / Float32 <br>
* B: boolean <br>
* S: string
* @param structure Structure string to read
*/
read(structure: string): any[];
/**
* Replace boolean by value
* @param index Replacing index
* @param b Boolean value to place
*/
replaceBoolean(index: number, b: boolean): this;
/**
* Replace int/Int32 by value
* @param index Replacing index
* @param i int/Int32 value to place
*/
replaceInt(index: number, i: number): this;
/**
* Replace long/Int64 by value
* @param index Replacing index
* @param l long/Int64 value to place
*/
replaceLong(index: number, l: number): this;
/**
* Replace double/Float64 by value
* @param index Replacing index
* @param d double/Float64 value to place
*/
replaceDouble(index: number, d: number): this;
/**
* Replace float/Float32 by value
* @param index Replacing index
* @param f float/Float32 value to place
*/
replaceFloat(index: number, f: number): this;
/**
* Replace byte/UInt8 by value
* @param index Replacing index
* @param b byte/UInt8 value to place
*/
replaceByte(index: number, b: number): this;
/**
* Replace byte[]/UInt8Array by value
* @param index Replacing index
* @param bytes byte[]/UInt8Array value to place
*/
replaceBytes(index: number, bytes: Uint8Array): this;
/**
* Replace unsigned short/UInt16 by value
* @param index Replacing index
* @param ushort unsigned short/UInt16 value to place
*/
replaceUShort(index: number, ushort: number): this;
/**
* Replace short/Int16 by value
* @param index Replacing index
* @param s short/Int16 value to place
*/
replaceShort(index: number, s: number): this
/**
* Replace string by value
* @param index Replacing index
* @param s string value to place
* @param charset Optional encoding charset (default: "latin1")
*/
replaceString(index: number, s: String | string, charset?: BufferEncoding): this;
/**
* Replace first found string by value
* @param oldS string value to be replaced
* @param newS string value to place
*/
replaceFirstString(oldS: String | string, newS: String | string): this;
/**
* Replace x found strings by value
* @param oldS string value to be replaced
* @param newS string value to place
* @param amount amount of strings to be replaced (-1 = all)
*/
replaceXStrings(oldS: String | string, newS: String | string, amount: number): this;
/**
* Replace all found strings by value
* @param oldS string value to be replaced
* @param newS string value to place
*/
replaceAllStrings(oldS: String | string, newS: String | string): this;
/**
* Replace first found substring by value
* @param oldS string value to be replaced
* @param newS string value to place
*/
replaceFirstSubstring(oldS: String | string, newS: String | string): this;
/**
* Replace x found substrings by value
* @param oldS string value to be replaced
* @param newS string value to place
* @param amount amount of strings to be replaced (-1 = all)
*/
replaceXSubstrings(oldS: String | string, newS: String | string, amount: number): this;
/**
* Replace all found substrings by value
* @param oldS string value to be replaced
* @param newS string value to place
*/
replaceAllSubstrings(oldS: String | string, newS: String | string): this;
/**
* Replace all found integers of val by value
* @param val int/Int32 value to be replaced
* @param replacement int/Int32 value to place
*/
replaceAllIntegers(val: number, replacement: number);
/**
* Check if string can be read at index
* @param index
*/
canReadString(index: number): boolean;
/**
* Append int/Int32 at end of packet
* @param i int/Int32 value to append
*/
appendInt(i: number): this;
/**
* Append long/Int64 at end of packet
* @param l long/Int64 value to append
*/
appendLong(l: number): this;
/**
* Append double/Float64 at end of packet
* @param d double/Float64 value to append
*/
appendDouble(d: number): this;
/**
* Append float/Float32 at end of packet
* @param f float/Float32 value to append
*/
appendFloat(f: number): this;
/**
* Append byte/UInt8 at end of packet
* @param b byte/UInt8 value to append
*/
appendByte(b: number): this;
/**
* Append byte[]/UInt8Array at end of packet
* @param bytes byte[]/UInt8Array value to append
*/
appendBytes(bytes: Uint8Array): this;
/**
* Append boolean at end of packet
* @param b boolean value to append
*/
appendBoolean(b: boolean): this;
/**
* Append unsigned short/UInt16 at end of packet
* @param ushort unsigned short/UInt16 value to append
*/
appendUShort(ushort: number): this;
/**
* Append short/Int16 at end of packet
* @param s short/Int16 value to append
*/
appendShort(s: number): this;
/**
* Append string at end of packet
* @param s string value to append
* @param charset Optional encoding charset (default: "latin1")
*/
appendString(s: String | string, charset?: BufferEncoding): this;
/**
* Append long string at end of packet
* @param s long string value to append
* @param charset Optional encoding charset (default: "latin1")
*/
appendLongString(s: String | string, charset?: BufferEncoding): this;
/**
* Append objects to packet in given structure <br>
* b: byte / UInt8 <br>
* i: int / Int32 <br>
* s: short / Int16 <br>
* u: ushort / UInt16 <br>
* l: long / Int64 <br>
* d: double / Float64 <br>
* f: float / Float32 <br>
* B: boolean <br>
* S: string
* @param objects Array of objects to append
* @param structure String of objects structure
*/
append(structure: string, ...objects: any[]): this;
/**
* Insert int/Int32 at index
* @param index Index to insert at
* @param i int/Int32 value to insert
*/
insertInt(index: number, i: number): this;
/**
* Insert long/Int64 at index
* @param index Index to insert at
* @param l long/Int64 value to insert
*/
insertLong(index: number, l: number): this;
/**
* Insert double/Float64 at index
* @param index Index to insert at
* @param d double/Float64 value to insert
*/
insertDouble(index: number, d: number): this;
/**
* Insert float/Float32 at index
* @param index Index to insert at
* @param f float/Float32 value to insert
*/
insertFloat(index: number, f: number): this;
/**
* Insert byte/UInt8 at index
* @param index Index to insert at
* @param b byte/UInt8 value to insert
*/
insertByte(index: number, b: number): this;
/**
* Insert byte[]/UInt8Array at index
* @param index Index to insert at
* @param bytes byte[]/UInt8Array value to insert
*/
insertBytes(index: number, bytes: Uint8Array): this;
/**
* Insert boolean at index
* @param index Index to insert at
* @param b boolean value to insert
*/
insertBoolean(index: number, b: boolean): this;
/**
* Insert unsigned short/UInt16 at index
* @param index Index to insert at
* @param ushort unsigned short/UInt16 value to insert
*/
insertUShort(index: number, ushort: number): this;
/**
* Insert short/Int16 at index
* @param index Index to insert at
* @param s short/Int16 value to insert
*/
insertShort(index: number, s: number): this;
/**
* Insert string at index
* @param index Index to insert at
* @param s string value to insert
* @param charset Optional encoding charset (default: "latin1")
*/
insertString(index: number, s: String | string, charset?: BufferEncoding): this;
/**
* Insert objects to packet in given structure at index <br>
* b: byte / UInt8 <br>
* i: int / Int32 <br>
* s: short / Int16 <br>
* u: ushort / UInt16 <br>
* l: long / Int64 <br>
* d: double / Float64 <br>
* f: float / Float32 <br>
* B: boolean <br>
* S: string
* @param index Index to insert at
* @param objects Array of objects to insert
* @param structure String of objects structure
*/
insert(index:number, structure: string, ...objects: any[]): this;
/**
* Check if packet has been edited
*/
isReplaced(): boolean;
/**
* Fix packet length bytes
*/
fixLength(): void;
/**
* Change private parameter #isEdited to value
* @param edited boolean value to set
*/
overrideEditedField(edited: boolean): void;
/**
* Get the expression of the packet with given structure
* @param structure Structure of packet
*/
toExpression(structure: string): string;
/**
* Convert the packet to a string
*/
stringify(): string;
/**
* Read packet arguments from a string
* @param str packet string
*/
constructFromString(str: String | string): void;
/**
* Compare other hPacket to hPacket (compares private parameter #packetInBytes and private parameter #isEdited)
* @param packet hPacket to compare with current hPacket
*/
equals(packet: HPacket): boolean;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
import {HDirection} from "../../protocol/hdirection";
export class PacketInfo {
constructor(headerId: number, hash: string, name: string, structure: string, destination: HDirection, source: string);
get name(): string | null;
get hash(): string | null;
get headerId(): number;
get destination(): HDirection;
get structure(): string | null;
get source(): string;
toString(): string;
}
@@ -0,0 +1,89 @@
import util from "util";
export class PacketInfo {
#destination;
#headerId;
#hash;
#name;
#structure;
#source;
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}PacketInfo {\n`
+ `${indent} destination: ${util.inspect(this.#destination, {colors: true})}\n`
+ `${indent} headerId: ${util.inspect(this.#headerId, {colors: true})}\n`
+ `${indent} hash: ${util.inspect(this.#hash, {colors: true})}\n`
+ `${indent} name: ${util.inspect(this.#name, {colors: true})}\n`
+ `${indent} structure: ${util.inspect(this.#structure, {colors: true})}\n`
+ `${indent} source: ${util.inspect(this.#source, {colors: true})}\n`
+ `${indent}}`;
}
constructor(headerId, hash, name, structure, destination, source) {
this.#destination = destination;
this.#headerId = headerId;
this.#hash = hash;
this.#name = name;
this.#structure = structure;
this.#source = source;
}
get name() {
return this.#name;
}
getName() {
console.error("\x1b[31mPacketInfo.getName(): Deprecated method used, use the getter PacketInfo.name instead\x1b[0m");
return this.#name;
}
get hash() {
return this.#hash;
}
getHash() {
console.error("\x1b[31mPacketInfo.getHash(): Deprecated method used, use the getter PacketInfo.hash instead\x1b[0m");
return this.#hash;
}
get headerId() {
return this.#headerId;
}
getHeaderId() {
console.error("\x1b[31mPacketInfo.getHeaderId(): Deprecated method used, use the getter PacketInfo.headerId instead\x1b[0m");
return this.#headerId;
}
get destination() {
return this.#destination;
}
getDestination() {
console.error("\x1b[31mPacketInfo.getDestination(): Deprecated method used, use the getter PacketInfo.destination instead\x1b[0m");
return this.#destination;
}
get structure() {
return this.#structure;
}
getStructure() {
console.error("\x1b[31mPacketInfo.getStructure(): Deprecated method used, use the getter PacketInfo.structure instead\x1b[0m");
return this.#structure;
}
get source() {
return this.#source;
}
getSource() {
console.error("\x1b[31mPacketInfo.getSource(): Deprecated method used, use the getter PacketInfo.source instead\x1b[0m");
return this.#source;
}
toString() {
return this.#headerId + ": [" + this.#name + "][" + this.#structure + "]";
}
}
@@ -0,0 +1,19 @@
import {PacketInfo} from "./packetinfo";
import {HDirection} from "../../protocol/hdirection";
import {HPacket} from "../../protocol/hpacket";
export class PacketInfoManager {
constructor(packetInfoList: PacketInfo[]);
getAllPacketInfoFromHeaderId(direction: HDirection, headerId: number): PacketInfo[];
getAllPacketInfoFromHash(direction: HDirection, hash: string): PacketInfo[];
getAllPacketInfoFromName(direction: HDirection, name: string): PacketInfo[];
getPacketInfoFromHeaderId(direction: HDirection, headerId: number): PacketInfo | null;
getPacketInfoFromHash(direction: HDirection, hash: string): PacketInfo | null;
getPacketInfoFromName(direction: HDirection, name: string): PacketInfo | null;
get packetInfoList(): PacketInfo[];
static readFromPacket(hPacket: HPacket): PacketInfoManager;
}
@@ -0,0 +1,140 @@
import { HDirection } from "../../protocol/hdirection.js";
import { PacketInfo } from "./packetinfo.js";
import util from "util";
export class PacketInfoManager {
#headerIdToMessage_incoming = new Map();
#headerIdToMessage_outgoing = new Map();
#hashToMessage_incoming = new Map();
#hashToMessage_outgoing = new Map();
#nameToMessage_incoming = new Map();
#nameToMessage_outgoing = new Map();
#packetInfoList = [];
[util.inspect.custom](depth) {
const indent = " ".repeat(depth > 2 ? depth - 2 : 0);
return `${indent}PacketInfoManager {\n`
+ `${indent} packetInfoList: ${util.inspect(this.#packetInfoList, {colors: true, maxArrayLength: 0})}\n`
+ `${indent}}`;
}
constructor(packetInfoList) {
this.#packetInfoList = packetInfoList;
packetInfoList.forEach(packetInfo => {
this.#addMessage(packetInfo);
});
}
#addMessage = (packetInfo) => {
if(packetInfo.hash === null && packetInfo.name === null) return;
let headerIdToMessage = packetInfo.destination === HDirection.TOCLIENT ? this.#headerIdToMessage_incoming : this.#headerIdToMessage_outgoing;
let hashToMessage = packetInfo.destination === HDirection.TOCLIENT ? this.#hashToMessage_incoming : this.#hashToMessage_outgoing;
let nameToMessage = packetInfo.destination === HDirection.TOCLIENT ? this.#nameToMessage_incoming : this.#nameToMessage_outgoing;
if(!headerIdToMessage.has(packetInfo.headerId)) {
headerIdToMessage.set(packetInfo.headerId, []);
}
headerIdToMessage.get(packetInfo.headerId).push(packetInfo);
if(packetInfo.hash != null) {
if(!hashToMessage.has(packetInfo.hash)) {
hashToMessage.set(packetInfo.hash, []);
}
hashToMessage.get(packetInfo.hash).push(packetInfo);
}
if(packetInfo.name != null) {
if(!nameToMessage.has(packetInfo.name)) {
nameToMessage.set(packetInfo.name, []);
}
nameToMessage.get(packetInfo.name).push(packetInfo);
}
}
getAllPacketInfoFromHeaderId(direction, headerId) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || !Number.isInteger(headerId)) {
throw new Error("Invalid arguments passed")
}
let headerIdToMessage = direction === HDirection.TOCLIENT ? this.#headerIdToMessage_incoming : this.#headerIdToMessage_outgoing;
return headerIdToMessage.get(headerId) === undefined ? [] : headerIdToMessage.get(headerId);
}
getAllPacketInfoFromHash(direction, hash) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || typeof(hash) !== "string") {
throw new Error("Invalid arguments passed")
}
let hashToMessage = direction === HDirection.TOCLIENT ? this.#hashToMessage_incoming : this.#hashToMessage_outgoing;
return hashToMessage.get(hash) === undefined ? [] : hashToMessage.get(hash);
}
getAllPacketInfoFromName(direction, name) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || typeof(name) !== "string") {
throw new Error("Invalid arguments passed")
}
let nameToMessage = direction === HDirection.TOCLIENT ? this.#nameToMessage_incoming : this.#nameToMessage_outgoing;
return nameToMessage.get(name) === undefined ? [] : nameToMessage.get(name);
}
getPacketInfoFromHeaderId(direction, headerId) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || !Number.isInteger(headerId)) {
throw new Error("Invalid arguments passed")
}
let all = this.getAllPacketInfoFromHeaderId(direction, headerId);
return all.length === 0 ? null : all[0];
}
getPacketInfoFromHash(direction, hash) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || typeof(hash) !== "string") {
throw new Error("Invalid arguments passed")
}
let all = this.getAllPacketInfoFromHash(direction, hash);
return all.length === 0 ? null : all[0];
}
getPacketInfoFromName(direction, name) {
if(!(direction === HDirection.TOCLIENT || direction === HDirection.TOSERVER) || typeof(name) !== "string") {
throw new Error("Invalid arguments passed")
}
let all = this.getAllPacketInfoFromName(direction, name);
return all.length === 0 ? null : all[0];
}
get packetInfoList() {
return this.#packetInfoList;
}
getPacketInfoList() {
console.error("\x1b[31mPacketInfoManager.getPacketInfoList(): Deprecated method used, use the getter PacketInfoManager.packetInfoList instead\x1b[0m");
return this.#packetInfoList;
}
static readFromPacket(hPacket) {
let packetInfoList = [];
let size = hPacket.readInteger();
for(let i = 0; i < size; i++) {
let packetInfo = new PacketInfo(...hPacket.read('iSSSbS'))
packetInfoList.push(packetInfo);
}
return new PacketInfoManager(packetInfoList);
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"name": "gnode-api",
"version": "0.2.15",
"description": "Node.js G-Earth extension API",
"main": "index.js",
"type": "module",
"types": "index.d.ts",
"scripts": {
"test": "node test/test.js -p 9092"
},
"engines": {
"node": ">=15.0.0"
},
"keywords": [],
"author": "WiredSpast",
"license": "ISC",
"dependencies": {
"@types/node": "^16.4.13",
"node-fetch": "^3.2.10"
},
"directories": {
"lib": "lib"
},
"repository": "git+https://github.com/WiredSpast/G-Node.git",
"bugs": {
"url": "https://github.com/WiredSpast/G-Node/issues"
},
"homepage": "https://github.com/WiredSpast/G-Node#readme",
"files": [
"/lib",
"index.js",
"index.d.ts"
]
}