├── docs ├── CNAME ├── assets │ └── images │ │ ├── icons.png │ │ ├── widgets.png │ │ ├── icons@2x.png │ │ └── widgets@2x.png ├── interfaces │ ├── onsupermainargs.html │ ├── cdnoptions.html │ ├── inventoryitem.html │ ├── peerinventory.html │ ├── variantoptions.html │ ├── collections.html │ ├── itemsdatmeta.html │ ├── httpoptions.html │ └── itemsdat.html └── enums │ └── packetmessagetypes.html ├── .gitattributes ├── js ├── tests │ ├── items.dat │ └── server.js ├── src │ ├── Structs │ │ ├── PacketMessageTypes.js │ │ └── VariantTypes.js │ ├── Packet │ │ ├── TextPacket.js │ │ ├── TankPacket.js │ │ └── Variant.js │ ├── Data │ │ └── README.md │ ├── Redis.js │ ├── Http.js │ ├── Constants.js │ ├── CustomCache.js │ ├── NativeWrapper.js │ ├── ItemsDat.js │ ├── World.js │ ├── Server.js │ └── Peer.js └── index.js ├── native ├── lib │ ├── enet.lib │ └── enet64.lib ├── include │ └── enet │ │ ├── utility.h │ │ ├── types.h │ │ ├── time.h │ │ ├── callbacks.h │ │ ├── list.h │ │ ├── unix.h │ │ ├── win32.h │ │ └── protocol.h ├── binding.gyp └── Main.cpp ├── typedoc.json ├── package.json ├── .gitignore ├── .npmignore ├── README.md └── yarn.lock /docs/CNAME: -------------------------------------------------------------------------------- 1 | pogtopia.js.org 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /js/tests/items.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/js/tests/items.dat -------------------------------------------------------------------------------- /native/lib/enet.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/native/lib/enet.lib -------------------------------------------------------------------------------- /native/lib/enet64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/native/lib/enet64.lib -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/docs/assets/images/icons.png -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syn9673/Pogtopia/HEAD/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputFiles": ["./js"], 3 | "mode": "file", 4 | "out": "docs", 5 | "includeDeclarations": true, 6 | "excludeExternals": true, 7 | "ignoreCompilerErrors": true 8 | } -------------------------------------------------------------------------------- /js/src/Structs/PacketMessageTypes.js: -------------------------------------------------------------------------------- 1 | const PacketMessageTypes = { 2 | REQUEST_LOGIN_INFO: 0x1, 3 | STRING: 0x2, 4 | ACTION: 0x3, 5 | TANK_PACKET: 0x4 6 | } 7 | 8 | module.exports = PacketMessageTypes; -------------------------------------------------------------------------------- /js/src/Structs/VariantTypes.js: -------------------------------------------------------------------------------- 1 | const VARIANT_TYPES = { 2 | NONE: 0x0, 3 | FLOAT_1: 0x1, 4 | STRING: 0x2, 5 | FLOAT_2: 0x3, 6 | FLOAT_3: 0x4, 7 | UINT: 0x5, 8 | INT: 0x9 9 | } 10 | 11 | module.exports = VARIANT_TYPES; -------------------------------------------------------------------------------- /js/src/Packet/TextPacket.js: -------------------------------------------------------------------------------- 1 | module.exports = class TextPacket { 2 | constructor(type, text) { 3 | this.type = type || 0x1; 4 | this.text = text || ""; 5 | } 6 | 7 | static from(type, ...values) { 8 | return new TextPacket(type, values?.join("\n")); 9 | } 10 | } -------------------------------------------------------------------------------- /js/src/Data/README.md: -------------------------------------------------------------------------------- 1 | # Server.dat 2 | This file is where necessary stuff that don't need to be saved in the database is put. For example the last user id. More will be added soon. 3 | 4 | ## Structure 5 | ```c 6 | typedef struct { 7 | char[8] header; // the word "POGTOPIA" 8 | uint32_t availableUserID; // available user id 9 | } ServerDat; 10 | ``` -------------------------------------------------------------------------------- /native/include/enet/utility.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file utility.h 3 | @brief ENet utility header 4 | */ 5 | #ifndef __ENET_UTILITY_H__ 6 | #define __ENET_UTILITY_H__ 7 | 8 | #define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) 9 | #define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) 10 | #define ENET_DIFFERENCE(x, y) ((x) < (y) ? (y) - (x) : (x) - (y)) 11 | 12 | #endif /* __ENET_UTILITY_H__ */ 13 | 14 | -------------------------------------------------------------------------------- /native/include/enet/types.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file types.h 3 | @brief type definitions for ENet 4 | */ 5 | #ifndef __ENET_TYPES_H__ 6 | #define __ENET_TYPES_H__ 7 | 8 | typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ 9 | typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ 10 | typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ 11 | 12 | #endif /* __ENET_TYPES_H__ */ 13 | 14 | -------------------------------------------------------------------------------- /js/src/Redis.js: -------------------------------------------------------------------------------- 1 | const Redis = require('ioredis') 2 | 3 | class CustomRedis extends Redis { 4 | constructor(...args) { 5 | super(...args) 6 | } 7 | 8 | async set(key, val) { 9 | try { 10 | val = JSON.stringify(val) 11 | } catch(err) {} 12 | 13 | await super.set(key, val) 14 | } 15 | 16 | async get(key) { 17 | let result = await super.get(key) 18 | 19 | try { 20 | result = JSON.parse(result) 21 | } catch(err) {} 22 | 23 | return result 24 | } 25 | } 26 | 27 | module.exports = CustomRedis -------------------------------------------------------------------------------- /native/include/enet/time.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file time.h 3 | @brief ENet time constants and macros 4 | */ 5 | #ifndef __ENET_TIME_H__ 6 | #define __ENET_TIME_H__ 7 | 8 | #define ENET_TIME_OVERFLOW 86400000 9 | 10 | #define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) 11 | #define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) 12 | #define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) 13 | #define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) 14 | 15 | #define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) 16 | 17 | #endif /* __ENET_TIME_H__ */ 18 | 19 | -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Server: require("./src/Server"), 3 | Peer: require("./src/Peer"), 4 | Native: require("./src/NativeWrapper"), 5 | TankPacket: require("./src/Packet/TankPacket"), 6 | TextPacket: require("./src/Packet/TextPacket"), 7 | Variant: require("./src/Packet/Variant"), 8 | PacketMessageTypes: require("./src/Structs/PacketMessageTypes"), 9 | VariantTypes: require("./src/Structs/VariantTypes"), 10 | Http: require("./src/Http"), 11 | World: require("./src/World"), 12 | Constants: require("./src/Constants"), 13 | CustomCache: require("./src/CustomCache"), 14 | ItemsDatUtils: require("./src/ItemsDat") 15 | } -------------------------------------------------------------------------------- /native/include/enet/callbacks.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file callbacks.h 3 | @brief ENet callbacks 4 | */ 5 | #ifndef __ENET_CALLBACKS_H__ 6 | #define __ENET_CALLBACKS_H__ 7 | 8 | #include 9 | 10 | typedef struct _ENetCallbacks 11 | { 12 | void * (ENET_CALLBACK * malloc) (size_t size); 13 | void (ENET_CALLBACK * free) (void * memory); 14 | void (ENET_CALLBACK * no_memory) (void); 15 | } ENetCallbacks; 16 | 17 | /** @defgroup callbacks ENet internal callbacks 18 | @{ 19 | @ingroup private 20 | */ 21 | extern void * enet_malloc (size_t); 22 | extern void enet_free (void *); 23 | 24 | /** @} */ 25 | 26 | #endif /* __ENET_CALLBACKS_H__ */ 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pogtopia", 3 | "version": "0.3.2-github", 4 | "description": "", 5 | "main": "./js/index.js", 6 | "dependencies": { 7 | "mongodb": "^3.6.2", 8 | "node-addon-api": "^3.0.0" 9 | }, 10 | "devDependencies": {}, 11 | "optionalDependencies": { 12 | "ioredis": "^4.17.3" 13 | }, 14 | "scripts": { 15 | "test": "node ./js/tests/server.js", 16 | "install": "cd native && node-gyp clean configure build -j 8", 17 | "docs": "typedoc --options typedoc.json && cd docs && echo pogtopia.js.org > CNAME" 18 | }, 19 | "repository": { 20 | "url": "https://github.com/Alexander9673/Pogtopia" 21 | }, 22 | "author": "Alexander9673", 23 | "license": "GPL-3.0" 24 | } 25 | -------------------------------------------------------------------------------- /native/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "index", 5 | "sources": [ 6 | "Main.cpp" 7 | ], 8 | "include_dirs": [ 9 | " { 2 | let server; 3 | 4 | const packet = `server|${opts.serverIP} 5 | port|${opts.serverPort} 6 | type|1 7 | meta|undefined 8 | RTENDMARKERBS1001`; 9 | 10 | const httpServerCallback = (request, response) => { 11 | if (request.url === "/growtopia/server_data.php" && request.method?.toLowerCase() === "post") { 12 | response.writeHead(200, { 13 | "Content-Type": "text/html" 14 | }) 15 | 16 | response.write(packet, (err) => { 17 | if (err) throw new Error(err); 18 | 19 | response.end(); 20 | }); 21 | } else response.destroy(); 22 | } 23 | 24 | if (opts.httpsEnabled) 25 | server = require("https").createServer(httpServerCallback); 26 | else server = require("http").createServer(httpServerCallback); 27 | 28 | server.listen(80); // listen to port 80 since that's the port we need 29 | console.log("HTTP Server now listening at port :80"); 30 | } 31 | 32 | module.exports = { start }; -------------------------------------------------------------------------------- /js/src/Constants.js: -------------------------------------------------------------------------------- 1 | const CONSTANTS = { 2 | // Server epoch, date when pogtopia started development 3 | SERVER_EPOCH: 1597077000000n, 4 | 5 | // default skin color for players 6 | DEFAULT_SKIN: 0x8295C3FF, 7 | 8 | // Arguments for OnSuperMain 9 | OnSuperMainArgs: { 10 | arg3: "cc.cz.madkite.freedom org.aqua.gg idv.aqua.bulldog com.cih.gamecih2 com.cih.gamecih com.cih.game_cih cn.maocai.gamekiller com.gmd.speedtime org.dax.attack com.x0.strai.frep com.x0.strai.free org.cheatengine.cegui org.sbtools.gamehack com.skgames.traffikrider org.sbtoods.gamehaca com.skype.ralder org.cheatengine.cegui.xx.multi1458919170111 com.prohiro.macro me.autotouch.autotouch com.cygery.repetitouch.free com.cygery.repetitouch.pro com.proziro.zacro com.slash.gamebuster", 11 | arg4: "proto=110|choosemusic=audio/mp3/about_theme.mp3|active_holiday=7|server_tick=61370149|clash_active=1|drop_lavacheck_faster=1|isPayingUser=1|usingStoreNavigation=1|enableInventoryTab=1|bigBackpack=1|" 12 | } 13 | } 14 | 15 | module.exports = CONSTANTS -------------------------------------------------------------------------------- /js/src/CustomCache.js: -------------------------------------------------------------------------------- 1 | class CustomCache { 2 | constructor() { 3 | this.container = {} 4 | } 5 | 6 | set(key, val) { 7 | if (typeof key !== 'string') 8 | throw new TypeError('Key must be a string.') 9 | 10 | this.container[key] = val 11 | } 12 | 13 | get(key) { 14 | if (typeof key !== 'string') 15 | throw new TypeError('Key must be a string.') 16 | 17 | return this.container[key] 18 | } 19 | 20 | del(key) { 21 | if (typeof key !== 'string') 22 | throw new TypeError('Key must be a string.') 23 | 24 | delete this.container[key] 25 | } 26 | 27 | keys(pattern) { 28 | if (typeof pattern !== 'string') 29 | throw new TypeError('Pattern must be a string.') 30 | 31 | const keys = Object.keys(this.container) 32 | pattern = pattern.replace( 33 | new RegExp('\\*', 'g'), 34 | '\\w*?' 35 | ) 36 | 37 | return keys.filter( 38 | key => key.match( 39 | new RegExp(pattern, "g") 40 | ) 41 | ) 42 | } 43 | } 44 | 45 | module.exports = CustomCache -------------------------------------------------------------------------------- /native/include/enet/list.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file list.h 3 | @brief ENet list management 4 | */ 5 | #ifndef __ENET_LIST_H__ 6 | #define __ENET_LIST_H__ 7 | 8 | #include 9 | 10 | typedef struct _ENetListNode 11 | { 12 | struct _ENetListNode * next; 13 | struct _ENetListNode * previous; 14 | } ENetListNode; 15 | 16 | typedef ENetListNode * ENetListIterator; 17 | 18 | typedef struct _ENetList 19 | { 20 | ENetListNode sentinel; 21 | } ENetList; 22 | 23 | extern void enet_list_clear (ENetList *); 24 | 25 | extern ENetListIterator enet_list_insert (ENetListIterator, void *); 26 | extern void * enet_list_remove (ENetListIterator); 27 | extern ENetListIterator enet_list_move (ENetListIterator, void *, void *); 28 | 29 | extern size_t enet_list_size (ENetList *); 30 | 31 | #define enet_list_begin(list) ((list) -> sentinel.next) 32 | #define enet_list_end(list) (& (list) -> sentinel) 33 | 34 | #define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) 35 | 36 | #define enet_list_next(iterator) ((iterator) -> next) 37 | #define enet_list_previous(iterator) ((iterator) -> previous) 38 | 39 | #define enet_list_front(list) ((void *) (list) -> sentinel.next) 40 | #define enet_list_back(list) ((void *) (list) -> sentinel.previous) 41 | 42 | #endif /* __ENET_LIST_H__ */ 43 | 44 | -------------------------------------------------------------------------------- /native/include/enet/unix.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file unix.h 3 | @brief ENet Unix header 4 | */ 5 | #ifndef __ENET_UNIX_H__ 6 | #define __ENET_UNIX_H__ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef MSG_MAXIOVLEN 17 | #define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN 18 | #endif 19 | 20 | typedef int ENetSocket; 21 | 22 | #define ENET_SOCKET_NULL -1 23 | 24 | #define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ 25 | #define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ 26 | 27 | #define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ 28 | #define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ 29 | 30 | typedef struct 31 | { 32 | void * data; 33 | size_t dataLength; 34 | } ENetBuffer; 35 | 36 | #define ENET_CALLBACK 37 | 38 | #define ENET_API extern 39 | 40 | typedef fd_set ENetSocketSet; 41 | 42 | #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) 43 | #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) 44 | #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) 45 | #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) 46 | 47 | #endif /* __ENET_UNIX_H__ */ 48 | 49 | -------------------------------------------------------------------------------- /native/include/enet/win32.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file win32.h 3 | @brief ENet Win32 header 4 | */ 5 | #ifndef __ENET_WIN32_H__ 6 | #define __ENET_WIN32_H__ 7 | 8 | #ifdef _MSC_VER 9 | #ifdef ENET_BUILDING_LIB 10 | #pragma warning (disable: 4267) // size_t to int conversion 11 | #pragma warning (disable: 4244) // 64bit to 32bit int 12 | #pragma warning (disable: 4018) // signed/unsigned mismatch 13 | #pragma warning (disable: 4146) // unary minus operator applied to unsigned type 14 | #endif 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | typedef SOCKET ENetSocket; 21 | 22 | #define ENET_SOCKET_NULL INVALID_SOCKET 23 | 24 | #define ENET_HOST_TO_NET_16(value) (htons (value)) 25 | #define ENET_HOST_TO_NET_32(value) (htonl (value)) 26 | 27 | #define ENET_NET_TO_HOST_16(value) (ntohs (value)) 28 | #define ENET_NET_TO_HOST_32(value) (ntohl (value)) 29 | 30 | typedef struct 31 | { 32 | size_t dataLength; 33 | void * data; 34 | } ENetBuffer; 35 | 36 | #define ENET_CALLBACK __cdecl 37 | 38 | #ifdef ENET_DLL 39 | #ifdef ENET_BUILDING_LIB 40 | #define ENET_API __declspec( dllexport ) 41 | #else 42 | #define ENET_API __declspec( dllimport ) 43 | #endif /* ENET_BUILDING_LIB */ 44 | #else /* !ENET_DLL */ 45 | #define ENET_API extern 46 | #endif /* ENET_DLL */ 47 | 48 | typedef fd_set ENetSocketSet; 49 | 50 | #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) 51 | #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) 52 | #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) 53 | #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) 54 | 55 | #endif /* __ENET_WIN32_H__ */ 56 | 57 | 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript cache 45 | *.tsbuildinfo 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Microbundle cache 54 | .rpt2_cache/ 55 | .rts2_cache_cjs/ 56 | .rts2_cache_es/ 57 | .rts2_cache_umd/ 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | .env.test 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | 75 | # Next.js build output 76 | .next 77 | 78 | # Nuxt.js build / generate output 79 | .nuxt 80 | dist 81 | 82 | # Gatsby files 83 | .cache/ 84 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 85 | # https://nextjs.org/blog/next-9-1#public-directory-support 86 | # public 87 | 88 | # vuepress build output 89 | .vuepress/dist 90 | 91 | # Serverless directories 92 | .serverless/ 93 | 94 | # FuseBox cache 95 | .fusebox/ 96 | 97 | # DynamoDB Local files 98 | .dynamodb/ 99 | 100 | # TernJS port file 101 | .tern-port 102 | 103 | *.sln 104 | native/build 105 | node_modules 106 | *.vcxproj 107 | *.vcxproj.* 108 | .vs 109 | server.dat 110 | 111 | items.dat 112 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript cache 45 | *.tsbuildinfo 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Microbundle cache 54 | .rpt2_cache/ 55 | .rts2_cache_cjs/ 56 | .rts2_cache_es/ 57 | .rts2_cache_umd/ 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | .env.test 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | 75 | # Next.js build output 76 | .next 77 | 78 | # Nuxt.js build / generate output 79 | .nuxt 80 | dist 81 | 82 | # Gatsby files 83 | .cache/ 84 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 85 | # https://nextjs.org/blog/next-9-1#public-directory-support 86 | # public 87 | 88 | # vuepress build output 89 | .vuepress/dist 90 | 91 | # Serverless directories 92 | .serverless/ 93 | 94 | # FuseBox cache 95 | .fusebox/ 96 | 97 | # DynamoDB Local files 98 | .dynamodb/ 99 | 100 | # TernJS port file 101 | .tern-port 102 | 103 | *.sln 104 | native/build 105 | node_modules 106 | *.vcxproj 107 | *.vcxproj.* 108 | .vs 109 | server.dat 110 | 111 | items.dat 112 | docs -------------------------------------------------------------------------------- /js/src/Packet/TankPacket.js: -------------------------------------------------------------------------------- 1 | const TANK_PACKET_DEFAULT_SIZE = 60; 2 | 3 | module.exports = class TankPacket { 4 | constructor(data) { 5 | this.data = data; 6 | } 7 | 8 | static from(data) { 9 | if (Buffer.isBuffer(data)) { 10 | if (data.length < 60) 11 | throw new Error("Invalid Tank Packet length"); 12 | 13 | data = data.slice(0, -1); // remove null terminators 14 | 15 | const tank = { 16 | type: data.readUInt32LE(4), 17 | netID: data.readInt32LE(8), 18 | targetNetID: data.readInt32LE(12), 19 | state: data.readUInt32LE(16), 20 | itemInfo: data.readUInt32LE(24), 21 | playerPosX: data.readFloatLE(28), 22 | playerPosY: data.readFloatLE(32), 23 | playerSpeedX: data.readFloatLE(36), 24 | playerSpeedY: data.readFloatLE(40), 25 | playerPunchX: data.readInt32LE(48), 26 | playerPunchY: data.readInt32LE(52), 27 | extraData: data.length <= 60 ? null : () => data.slice(60) 28 | }; 29 | 30 | return new TankPacket(tank); 31 | } else return new TankPacket(data); 32 | } 33 | 34 | parse() { 35 | let buffer = Buffer.alloc(TANK_PACKET_DEFAULT_SIZE); 36 | 37 | // write the necessary data to the buffer 38 | buffer.writeUInt32LE(0x4); // message type for TankPackets 39 | buffer.writeUInt32LE(this.data.type, 4); // packet type 40 | buffer.writeInt32LE(this.data.netID ?? 0, 8); // user netID 41 | buffer.writeInt32LE(this.data.targetNetID ?? 0, 12); // netID of target user 42 | buffer.writeUInt32LE(this.data.state ?? 0x8, 16); // state of the variant packet 43 | buffer.writeUInt32LE(this.data.itemInfo ?? 0, 24); // Item info/state used or the delay (in ms) 44 | buffer.writeFloatLE(this.data.playerPosX ?? 0, 28); // position of the user on the x-axis 45 | buffer.writeFloatLE(this.data.playerPosY ?? 0, 32); // position of the user on the y-axis 46 | buffer.writeFloatLE(this.data.playerSpeedX ?? 0, 36); // speed of the user on the x-axis 47 | buffer.writeFloatLE(this.data.playerSpeedY ?? 0, 40); // speed of the user on the y-axis 48 | buffer.writeInt32LE(this.data.playerPunchX ?? 0, 48); // x position on where the punch was sent 49 | buffer.writeInt32LE(this.data.playerPunchY ?? 0, 52); // y position on where the punch was sent 50 | 51 | if (this.data.extraData && typeof this.data.extraData === "function") { 52 | const extraData = this.data.extraData(); 53 | if (!Buffer.isBuffer(extraData)) return; 54 | 55 | buffer = Buffer.concat([buffer, extraData]); // combine the two buffers 56 | buffer.writeUInt32LE(extraData.length, 56); // modify offset 56, which is tile data length 57 | } 58 | 59 | return buffer; 60 | } 61 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pogtopia 2 | A simple to use API for creating private servers for Growtopia with nodejs. 3 | 4 | ## Documentation 5 | The documentation [here](https://pogtopia.js.org) matches the code on the **Github** repo and not on NPM. 6 | 7 | ## Installations 8 | **Requirements** 9 | - `node-gyp` 10 | - Python **2.7** (3+ can be used) 11 | - Windows Build Tools/Build Essential 12 | - NodeJS v14+ 13 | - MongoDB 14 | - Redis Server 15 | 16 | **Installing the Requirements** 17 | Simply run this as Administrator in Windows Powershell, 18 | `$ npm install windows-build-tools node-gyp -g` 19 | 20 | If on Linux, simply install `build-essential` first with, 21 | `$ sudo (yum/apt-get/etc...) install build-essential` 22 | 23 | After that, install node-gyp by doing 24 | `$ npm install node-gyp -g` 25 | 26 | After installing everything (windows or linux), simply run 27 | `$ npm install pogtopia` to install the latest version on NPM, or 28 | `$ npm install Alexander9673/Pogtopia` to install the version on Github. 29 | 30 | ## Example 31 | ```js 32 | const Pogtopia = require("pogtopia"); 33 | const { readFileSync } = require("fs"); 34 | const server = new Pogtopia.Server({ 35 | server: { 36 | port: 17091, // set port to 17091, 37 | cdn: { // CDN Options for the server, this will not be updated, you will have to find the CDN yourselves. 38 | host: "ubistatic-a.akamaihd.net", 39 | url: "0098/87996/cache/" 40 | }, 41 | itemsDatFile: "/path/to/items.dat", // Path to items.dat 42 | serverDatFile: "/path/to/your/server.dat" // Optional. The path to the server.dat file 43 | } 44 | }); 45 | 46 | // uncomment this line to enable the built-in HTTP Server 47 | // Pogtopia.Http.start({ serverIP: "127.0.0.1", serverPort: 17091, httpsEnabled: false }); 48 | 49 | server.setHandler("connect", (peer) => peer.requestLoginInformation()); // request login information from the peer 50 | 51 | server.setHandler("disconnect", (peer) => {}); // handle peer disconnections 52 | 53 | server.setHandler("receive", (peer, packet) => { 54 | // handle packets here 55 | }); 56 | 57 | server.start(); 58 | ``` 59 | Check the `js/tests/server.js` file for a much better example. 60 | 61 | ## Custom Cache 62 | You can implement a custom cache instead of redis. 63 | When implementing your own cache, you have to have these methods with these return types: 64 | ```ts 65 | public set: (key: string, val: string) => void 66 | public get: (key: string) => any 67 | public del: (key: string) => void 68 | public keys: (pattern: string) => string[] 69 | ``` 70 | If you are wondering why the return type for `get` is `any`, it's because the value can be anything from js objects, to js arrays. Anything that can be parsed from a JSON string is converted. 71 | 72 | ## Questions 73 | Join our **[Discord Server](https://discord.gg/S7WKAeh)** about questions. -------------------------------------------------------------------------------- /js/tests/server.js: -------------------------------------------------------------------------------- 1 | const Pogtopia = require("../index"); 2 | const fs = require("fs"); 3 | const server = new Pogtopia.Server({ 4 | server: { 5 | port: 17091, 6 | cdn: { 7 | host: "ubistatic-a.akamaihd.net", 8 | url: "0098/87996/cache/" 9 | } 10 | } 11 | }); 12 | 13 | const itemsDatFile = fs.readFileSync(`${__dirname}/items.dat`); 14 | Pogtopia.Http.start({ serverIP: "127.0.0.1", serverPort: 17091 }); 15 | 16 | // Super simple Server example 17 | 18 | server.setItemsDat(itemsDatFile); 19 | 20 | server.setHandler("connect", (peer) => { 21 | peer.requestLoginInformation() 22 | }); 23 | 24 | server.setHandler("disconnect", async (peer) => { 25 | if (await peer.alreadyInCache()) 26 | await peer.disconnect("later"); 27 | }); 28 | 29 | server.setHandler("receive", async (peer, packet) => { 30 | if (packet.length < 4) 31 | return; 32 | 33 | const type = packet.readUInt32LE(); 34 | switch (type) { 35 | 36 | case Pogtopia.PacketMessageTypes.STRING: 37 | case Pogtopia.PacketMessageTypes.ACTION: { 38 | const data = server.stringPacketToMap(packet); 39 | 40 | if (data.has("requestedName")) { 41 | const isGuest = !data.has("tankIDName"); 42 | const uid = isGuest ? data.get("rid") : data.get("tankIDName").toLowerCase(); 43 | 44 | if (!uid) return await peer.disconnect("later"); 45 | 46 | if (isGuest) { 47 | await peer.fetch("db", { uid }); 48 | 49 | if (!peer.hasPlayerData()) // no data, lets create one for the new guest 50 | peer.create({ 51 | connectID: peer.data.connectID, 52 | displayName: `${data.get("requestedName")}_${Math.floor(Math.random() * 899) + 100}`, 53 | password: "", 54 | uid, 55 | userID: server.availableUserID++ 56 | }, true); 57 | else { 58 | // update displayname for peer 59 | peer.data.displayName = `${data.get("requestedName")}_${peer.data.displayName.split("_")[1]}`; 60 | await peer.saveToCache(); 61 | } 62 | } else { // TODO: handle non-guest accounts 63 | await peer.fetch("db", { uid }); 64 | 65 | if (!peer.data || (peer.data.password && peer.data.password !== data.get("tankIDPass"))) { 66 | peer.send(Pogtopia.Variant.from("OnConsoleMessage", "`4Oops``! That account doesn't seem to exist, please check if you typed the correct GrowID or password")) 67 | return await peer.disconnect("later"); 68 | } 69 | }; 70 | 71 | const cdn = server.getCDN(); 72 | 73 | peer.send(Pogtopia.Variant.from( 74 | "OnSuperMainStartAcceptLogonHrdxs47254722215a", 75 | server.items.hash, 76 | cdn.host, 77 | cdn.url, 78 | server.OnSuperMainArgs.arg3, 79 | server.OnSuperMainArgs.arg4 80 | )); 81 | } else { 82 | await peer.fetch("cache"); 83 | if (!peer.data) return; 84 | 85 | // handle actions 86 | if (data.get("action") === "quit") 87 | await peer.disconnect("later"); 88 | }; 89 | break; 90 | } 91 | 92 | case Pogtopia.PacketMessageTypes.TANK_PACKET: { 93 | if (packet.length < 60) break; 94 | break; 95 | } 96 | 97 | } 98 | }); 99 | 100 | server.start(); -------------------------------------------------------------------------------- /js/src/Packet/Variant.js: -------------------------------------------------------------------------------- 1 | const TankPacket = require("./TankPacket"); 2 | const VARIANT_TYPES = require("../Structs/VariantTypes"); 3 | 4 | const write4BytesToArr = (arr, int, isFloat = false) => { 5 | if (!isFloat) { 6 | arr.push(int & 0x000000ff); 7 | arr.push((int & 0x0000ff00) >> 8); 8 | arr.push((int & 0x00ff0000) >> 16); 9 | arr.push((int & 0xff000000) >> 24); 10 | } else { 11 | const floats = new Float32Array(1); 12 | floats[0] = int; 13 | 14 | const buffer = Buffer.from(floats.buffer); 15 | for (const byte of buffer) { 16 | arr.push(byte); 17 | } 18 | }; 19 | } 20 | 21 | module.exports = class Variant { 22 | constructor(options, args) { 23 | this.options = { 24 | delay: options.delay ?? 0, 25 | netID: options.netID ?? -1 26 | }; 27 | 28 | this.args = args; 29 | } 30 | 31 | parse() { 32 | return TankPacket.from({ 33 | type: 0x1, // variant 34 | netID: this.options.netID, // our netID 35 | itemInfo: this.options.delay, // the delay in ms, 36 | extraData: () => { 37 | const bytes = [null]; // the reason it's an array is so that we don't have to resize the buffer. 38 | let index = 0; // variants index are the same as array indexes. 39 | 40 | for (let i = 0; i < this.args.length; i++) 41 | switch (typeof this.args[i]) { 42 | case "string": { 43 | const str = this.args[i]; 44 | const strLen = str.length; 45 | 46 | bytes.push(index); // current index 47 | bytes.push(VARIANT_TYPES.STRING); // type of argument 48 | 49 | // write 4 bytes for the str len 50 | write4BytesToArr(bytes, strLen); 51 | 52 | for (let char of str) 53 | bytes.push(char.charCodeAt(0)); 54 | 55 | index++; 56 | break; 57 | } 58 | 59 | case "number": { 60 | const num = this.args[i]; 61 | 62 | bytes.push(index); // current index 63 | bytes.push(num < 0 ? VARIANT_TYPES.INT : VARIANT_TYPES.UINT); // type of argument 64 | 65 | // write the bytes, 4 bytes as well 66 | write4BytesToArr(bytes, num); 67 | 68 | index++; 69 | break; 70 | } 71 | 72 | case "object": { 73 | const floats = this.args[i].filter(i => typeof i === "number"); 74 | bytes.push(index); 75 | 76 | if (!Array.isArray(floats) || floats.length < 1) 77 | bytes.push(VARIANT_TYPES.NONE); 78 | else { 79 | const type = VARIANT_TYPES[`FLOAT_${floats.length}`]; 80 | bytes.push(type); 81 | 82 | for (const float of floats) 83 | write4BytesToArr(bytes, float, true); 84 | } 85 | 86 | index++; 87 | break; 88 | } 89 | } 90 | 91 | bytes[0] = index; // set the arg count 92 | 93 | return Buffer.from(bytes); 94 | } 95 | }).parse(); 96 | } 97 | 98 | static from(options, ...args) { 99 | if (Array.isArray(options) || typeof options !== "object") 100 | args.unshift(options); 101 | 102 | if (!args) args = []; 103 | 104 | return new Variant(options, args); 105 | } 106 | } -------------------------------------------------------------------------------- /js/src/NativeWrapper.js: -------------------------------------------------------------------------------- 1 | const native = require("../../native/build/Release/index"); 2 | const TextPacket = require("./Packet/TextPacket"); 3 | const Variant = require("./Packet/Variant"); 4 | const TankPacket = require("./Packet/TankPacket"); 5 | 6 | // Reason why this is not exported and added in the typings file cause it's used as Core only, not exposed to the api itself. 7 | 8 | /** 9 | * @callback Emitter 10 | * @param {string | symbol} event 11 | * @param {...any[]} args 12 | * @returns {boolean} 13 | */ 14 | 15 | module.exports = class NativeWrapper { 16 | constructor() { } 17 | 18 | /** 19 | * Initiate ENet, and sets the port to use. 20 | * @static 21 | * @param {number} port 22 | */ 23 | static Init(port) { 24 | native.init(port); 25 | } 26 | 27 | /** 28 | * Sets the Event Emitter to be used 29 | * @static 30 | * @param {Emitter} emitter 31 | */ 32 | static setEmitter(emitter) { 33 | native.set_event_emitter(emitter); 34 | } 35 | 36 | /** 37 | * Creates the ENet Host 38 | * @static 39 | */ 40 | static createHost() { 41 | native.host_create(); 42 | } 43 | 44 | /** 45 | * Receive events. This function must be ran in a loop 46 | * @static 47 | */ 48 | static receive() { 49 | native.host_event_recieve(); 50 | } 51 | 52 | /** 53 | * Sends a packet to the specific connectID. 54 | * @static 55 | * @param {Buffer|TextPacket|Variant|TankPacket} packet 56 | * @param {number} connectID 57 | */ 58 | static send(packet, connectID) { 59 | switch (packet.constructor.name) { 60 | case "Buffer": { 61 | native.send_packet(packet, connectID); 62 | break; 63 | } 64 | 65 | case "TextPacket": { 66 | const buffer = (() => { 67 | const buf = Buffer.alloc(5 + packet.text.length); 68 | 69 | buf.writeUInt32LE(packet.type); 70 | buf.write(packet.text, 4); 71 | 72 | return buf; 73 | })(); 74 | 75 | native.send_packet(buffer, connectID); 76 | break; 77 | } 78 | 79 | case "TankPacket": 80 | case "Variant": { 81 | native.send_packet(packet.parse(), connectID); 82 | break; 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Sends multiple packets to the specific connectID. 89 | * @static 90 | * @param {(Buffer|TextPacket|Variant|TankPacket)[]} packets 91 | * @param {number} connectID 92 | */ 93 | static send_multiple(packets, connectID) { 94 | packets = packets.map( 95 | packet => { 96 | let val; 97 | 98 | switch (packet.constructor.name) { 99 | case "TextPacket": { 100 | const buffer = (() => { 101 | const buf = Buffer.alloc(5 + packet.text.length); 102 | 103 | buf.writeUInt32LE(packet.type); 104 | buf.write(packet.text, 4); 105 | 106 | return buf; 107 | })(); 108 | 109 | val = buffer; 110 | break; 111 | } 112 | 113 | case "TankPacket": 114 | case "Variant": { 115 | val = packet.parse() 116 | break; 117 | } 118 | 119 | default: { 120 | val = packet; 121 | break; 122 | } 123 | } 124 | 125 | return val 126 | } 127 | ) 128 | 129 | native.send_multiple(packets, packets.length, connectID) 130 | } 131 | 132 | /** 133 | * Disconnect a peer 134 | * @param {"now"|"later"} [type] What type of disconnect method 135 | * @param {number} connectID The connectID of the peer 136 | */ 137 | static disconnect(type, connectID) { 138 | let typeAsNum; 139 | type = type?.toLowerCase(); 140 | 141 | switch (type) { 142 | case "now": { 143 | typeAsNum = 1; 144 | break; 145 | } 146 | 147 | case "later": { 148 | typeAsNum = 2; 149 | break; 150 | } 151 | 152 | default: { 153 | typeAsNum = 0; 154 | break; 155 | } 156 | } 157 | 158 | native.peer_disconnect(typeAsNum, connectID); 159 | } 160 | 161 | static isConnected(connectID) { 162 | return native.is_connected(connectID) 163 | } 164 | } -------------------------------------------------------------------------------- /native/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define NCP const Napi::CallbackInfo& info 7 | 8 | ENetHost* host; 9 | Napi::FunctionReference emit; 10 | int port; 11 | unsigned int connectID = 0; 12 | std::unordered_map peers; 13 | 14 | void Finalizer(Napi::Env env, uint8_t* data) { 15 | delete[] data; // free created data by C++ 16 | } 17 | 18 | void js_init(NCP) { 19 | 20 | auto env = info.Env(); 21 | port = info[0].As().Uint32Value(); 22 | 23 | if (enet_initialize()) 24 | return Napi::Error::New(env, "ENet Failed to Initialize").ThrowAsJavaScriptException(); 25 | 26 | std::cout << "ENet Initialized with Port: " << port << std::endl; 27 | } 28 | 29 | void js_set_event_emitter(NCP) { 30 | auto func = info[0].As(); 31 | 32 | emit = Napi::Persistent(func); 33 | std::cout << "Event Emitter Set" << std::endl; 34 | } 35 | 36 | void js_host_create(NCP) { 37 | ENetAddress address; 38 | address.port = port; 39 | address.host = ENET_HOST_ANY; 40 | 41 | host = enet_host_create(&address, 42 | 1024, 43 | 2, 44 | 0, 45 | 0); 46 | 47 | // set some compression that gt uses 48 | host->checksum = enet_crc32; 49 | enet_host_compress_with_range_coder(host); 50 | 51 | std::cout << "ENet Host created." << std::endl; 52 | } 53 | 54 | void js_host_event_recieve(NCP) { 55 | auto env = info.Env(); 56 | ENetEvent event; 57 | 58 | if (enet_host_service(host, &event, 0) > 0) { 59 | switch (event.type) { 60 | case ENET_EVENT_TYPE_CONNECT: { 61 | unsigned int connID = connectID++; 62 | 63 | // copy connectID to peer data 64 | event.peer->data = new uint8_t[sizeof(int)]; 65 | memcpy(event.peer->data, &connID, sizeof(int)); 66 | 67 | peers[connID] = event.peer; 68 | 69 | emit.Call({ 70 | Napi::String::New(env, "connect"), 71 | Napi::Number::New(env, connID) 72 | }); 73 | 74 | break; 75 | } 76 | 77 | case ENET_EVENT_TYPE_RECEIVE: { 78 | auto data = new uint8_t[event.packet->dataLength]; 79 | memcpy(data, event.packet->data, event.packet->dataLength); 80 | 81 | auto buffer = Napi::Buffer::New(env, data, event.packet->dataLength, Finalizer); 82 | emit.Call({ 83 | Napi::String::New(env, "receive"), 84 | Napi::Number::New(env, *(unsigned int*)event.peer->data), 85 | buffer 86 | }); 87 | 88 | enet_packet_destroy(event.packet); 89 | break; 90 | } 91 | 92 | case ENET_EVENT_TYPE_DISCONNECT: { 93 | auto connID = *(unsigned int*)event.peer->data; 94 | auto key = connID; 95 | 96 | emit.Call({ 97 | Napi::String::New(env, "disconnect"), 98 | Napi::Number::New(env, connID) 99 | }); 100 | 101 | if (peers.find(key) != peers.end()) 102 | peers.erase(key); 103 | 104 | delete[] event.peer->data; 105 | break; 106 | } 107 | } 108 | } 109 | } 110 | 111 | void js_send_packet(NCP) { 112 | auto buf = info[0].As>(); 113 | auto connectIDFromJS = info[1].As().Uint32Value(); 114 | 115 | auto bytes = buf.Data(); 116 | auto peer = peers[connectIDFromJS]; 117 | 118 | if (!peer) return; 119 | 120 | auto packet = enet_packet_create(bytes, buf.Length(), ENET_PACKET_FLAG_RELIABLE); 121 | 122 | enet_peer_send(peer, 0, packet); 123 | } 124 | 125 | void js_send_multiple_packets(NCP) { 126 | auto packets = info[0].ToObject(); 127 | auto count = info[1].As().Uint32Value(); 128 | auto connectIDFromJS = info[2].As().Uint32Value(); 129 | 130 | // peers 131 | auto peer = peers[connectIDFromJS]; 132 | 133 | if (!peer) return; 134 | 135 | for (int i = 0; i < count; ++i) { 136 | auto buffer = packets.Get(i).As>(); 137 | auto packet = enet_packet_create(buffer.Data(), buffer.Length(), ENET_PACKET_FLAG_RELIABLE); 138 | 139 | enet_peer_send(peer, 0, packet); 140 | } 141 | } 142 | 143 | void js_peer_disconnect(NCP) { 144 | auto type = info[0].As().Uint32Value(); 145 | auto connectIDFromJS = info[1].As().Uint32Value(); 146 | auto key = connectIDFromJS; 147 | 148 | if (peers.find(key) != peers.end()) { 149 | auto peer= peers[key]; 150 | 151 | switch (type) { 152 | case 0: { 153 | enet_peer_disconnect(peer, 0); 154 | break; 155 | } 156 | 157 | case 1: { 158 | enet_peer_disconnect_now(peer, 0); 159 | break; 160 | } 161 | 162 | case 2: { 163 | enet_peer_disconnect_later(peer, 0); 164 | break; 165 | } 166 | } 167 | } 168 | } 169 | 170 | Napi::Boolean js_is_connected(NCP) { 171 | auto connectIDFromJS = info[0].As().Uint32Value(); 172 | auto peer = peers[connectIDFromJS]; 173 | 174 | return Napi::Boolean::New(info.Env(), peer && peer->state == ENET_PEER_STATE_CONNECTED); 175 | } 176 | 177 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 178 | exports["init"] = Napi::Function::New(env, js_init); 179 | exports["set_event_emitter"] = Napi::Function::New(env, js_set_event_emitter); 180 | exports["host_create"] = Napi::Function::New(env, js_host_create); 181 | exports["host_event_recieve"] = Napi::Function::New(env, js_host_event_recieve); 182 | exports["send_packet"] = Napi::Function::New(env, js_send_packet); 183 | exports["peer_disconnect"] = Napi::Function::New(env, js_peer_disconnect); 184 | exports["send_multiple"] = Napi::Function::New(env, js_send_multiple_packets); 185 | exports["is_connected"] = Napi::Function::New(env, js_is_connected); 186 | 187 | return exports; 188 | } 189 | 190 | NODE_API_MODULE(Core, Init) -------------------------------------------------------------------------------- /js/src/ItemsDat.js: -------------------------------------------------------------------------------- 1 | const SECRET = 'PBG892FXX982ABC*' 2 | 3 | const decodeStr = 4 | (id, length, file, pos, encoded = false) => { 5 | if (!Buffer.isBuffer(file)) 6 | throw new TypeError('File must be a buffer') 7 | 8 | if (!encoded) 9 | return file.toString('utf-8', pos, pos + length) 10 | else { 11 | let str = '' 12 | 13 | for (let i = 0; i < length; i++) { 14 | str += String.fromCharCode( 15 | file[pos] ^ SECRET.charCodeAt( 16 | (id + i) % SECRET.length 17 | ) 18 | ) 19 | 20 | pos++ 21 | } 22 | 23 | return str 24 | } 25 | } 26 | 27 | const decode = 28 | (data) => { 29 | const meta = { 30 | items: [] 31 | } 32 | 33 | let mempos = 0; 34 | 35 | meta.version = data.readUIntLE(mempos, 2); 36 | mempos += 2; 37 | 38 | meta.itemCount = data.readUInt32LE(mempos); 39 | mempos += 4; 40 | 41 | for (let k = 0; k < meta.itemCount; k++) { 42 | const item = {}; 43 | 44 | item.id = data.readIntLE(mempos, 4) 45 | mempos += 4; 46 | 47 | item.editableType = data[mempos] 48 | mempos += 1; 49 | 50 | item.itemCategory = data[mempos] 51 | mempos += 1; 52 | 53 | item.actionType = data[mempos] 54 | mempos += 1; 55 | 56 | item.hitSoundType = data[mempos]; 57 | mempos += 1; 58 | 59 | const nameLength = data.readInt16LE(mempos); 60 | mempos += 2; 61 | 62 | item.name = decodeStr(item.id, nameLength, data, mempos, true) 63 | mempos += nameLength 64 | 65 | const textureLength = data.readInt16LE(mempos); 66 | mempos += 2; 67 | 68 | item.texture = decodeStr(item.id, textureLength, data, mempos) 69 | mempos += textureLength 70 | 71 | item.textureHash = data.readIntLE(mempos, 4); 72 | mempos += 4; 73 | 74 | item.itemKind = data[mempos]; 75 | mempos += 1; 76 | 77 | // skip val 1 78 | mempos += 4; 79 | 80 | item.textureX = data[mempos]; 81 | mempos += 1; 82 | 83 | item.textureY = data[mempos]; 84 | mempos += 1; 85 | 86 | item.spreadType = data[mempos]; 87 | mempos += 1; 88 | 89 | item.isStripeyWallpaper = data[mempos]; 90 | mempos += 1; 91 | 92 | item.collisionType = data[mempos]; 93 | mempos += 1; 94 | 95 | item.breakHits = data[mempos] / 6; 96 | mempos += 1; 97 | 98 | item.resetStateAfter = data.readIntLE(mempos, 4); 99 | mempos += 4; 100 | 101 | item.clothingType = data[mempos]; 102 | mempos += 1; 103 | 104 | item.rarity = data.readIntLE(mempos, 2); 105 | mempos += 2; 106 | 107 | item.maxAmount = data[mempos]; 108 | mempos += 1; 109 | 110 | const extraFileLength = data.readInt16LE(mempos); 111 | mempos += 2; 112 | 113 | item.extraFile = decodeStr(item.id, extraFileLength, data, mempos) 114 | mempos += extraFileLength 115 | 116 | item.extraFileHash = data.readIntLE(mempos, 4); 117 | mempos += 4; 118 | 119 | item.audioVolume = data.readIntLE(mempos, 4); 120 | mempos += 4; 121 | 122 | const petNameLength = data.readInt16LE(mempos); 123 | mempos += 2; 124 | 125 | item.petName = decodeStr(item.id, petNameLength, data, mempos) 126 | mempos += petNameLength 127 | 128 | const petPrefixLength = data.readInt16LE(mempos); 129 | mempos += 2; 130 | 131 | item.petPrefix = decodeStr(item.id, petPrefixLength, data, mempos) 132 | mempos += petPrefixLength 133 | 134 | const petSuffixLength = data.readInt16LE(mempos); 135 | mempos += 2; 136 | 137 | item.petSuffix = decodeStr(item.id, petSuffixLength, data, mempos) 138 | mempos += petSuffixLength 139 | 140 | const petAbilityLength = data.readInt16LE(mempos); 141 | mempos += 2; 142 | 143 | item.petAbility = decodeStr(item.id, petAbilityLength, data, mempos) 144 | mempos += petAbilityLength 145 | 146 | item.seedBase = data[mempos]; 147 | mempos += 1; 148 | 149 | item.seedOverlay = data[mempos]; 150 | mempos += 1; 151 | 152 | item.treeBase = data[mempos]; 153 | mempos += 1; 154 | 155 | item.treeLeaves = data[mempos]; 156 | mempos += 1; 157 | 158 | 159 | item.seedColor = data.readIntLE(mempos, 4); 160 | mempos += 4; 161 | 162 | item.seedOverlayColor = data.readIntLE(mempos, 4); 163 | mempos += 4; 164 | 165 | mempos += 4; /* Ingredients Ignored */ 166 | 167 | item.growTime = data.readIntLE(mempos, 4); 168 | mempos += 4; 169 | 170 | // skip val2 171 | mempos += 2; 172 | 173 | item.isRayman = data.readIntLE(mempos, 2); 174 | mempos += 2; 175 | 176 | const extraOptionsLength = data.readInt16LE(mempos); 177 | mempos += 2; 178 | 179 | item.extraOptions = decodeStr(item.id, extraOptionsLength, data, mempos) 180 | mempos += extraOptionsLength 181 | 182 | const textureTwoLength = data.readInt16LE(mempos); 183 | mempos += 2; 184 | 185 | item.texture2 = decodeStr(item.id, textureTwoLength, data, mempos) 186 | mempos += textureTwoLength 187 | 188 | const extraOptionsTwoLength = data.readInt16LE(mempos); 189 | mempos += 2; 190 | 191 | item.extraOptions2 = decodeStr(item.id, extraOptionsTwoLength, data, mempos) 192 | mempos += extraOptionsTwoLength 193 | 194 | item.unknownOptions = data.slice(mempos, mempos + 80); 195 | mempos += 80; 196 | 197 | item.punchOptions = ""; 198 | 199 | if (meta.version >= 11) { 200 | const punchOptionsLength = data.readInt16LE(mempos); 201 | mempos += 2; 202 | 203 | item.punchOptions = decodeStr(item.id, punchOptionsLength, data, mempos) 204 | mempos += punchOptionsLength 205 | } 206 | 207 | meta.items.push(item); 208 | } 209 | 210 | return meta; 211 | } 212 | 213 | module.exports = { 214 | decode 215 | } -------------------------------------------------------------------------------- /native/include/enet/protocol.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file protocol.h 3 | @brief ENet protocol 4 | */ 5 | #ifndef __ENET_PROTOCOL_H__ 6 | #define __ENET_PROTOCOL_H__ 7 | 8 | #include "enet/types.h" 9 | 10 | enum 11 | { 12 | ENET_PROTOCOL_MINIMUM_MTU = 576, 13 | ENET_PROTOCOL_MAXIMUM_MTU = 4096, 14 | ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, 15 | ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, 16 | ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, 17 | ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, 18 | ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, 19 | ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, 20 | ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 21 | }; 22 | 23 | typedef enum _ENetProtocolCommand 24 | { 25 | ENET_PROTOCOL_COMMAND_NONE = 0, 26 | ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, 27 | ENET_PROTOCOL_COMMAND_CONNECT = 2, 28 | ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, 29 | ENET_PROTOCOL_COMMAND_DISCONNECT = 4, 30 | ENET_PROTOCOL_COMMAND_PING = 5, 31 | ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, 32 | ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, 33 | ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, 34 | ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, 35 | ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, 36 | ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, 37 | ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, 38 | ENET_PROTOCOL_COMMAND_COUNT = 13, 39 | 40 | ENET_PROTOCOL_COMMAND_MASK = 0x0F 41 | } ENetProtocolCommand; 42 | 43 | typedef enum _ENetProtocolFlag 44 | { 45 | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), 46 | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), 47 | 48 | ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), 49 | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), 50 | ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, 51 | 52 | ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), 53 | ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 54 | } ENetProtocolFlag; 55 | 56 | #ifdef _MSC_VER 57 | #pragma pack(push, 1) 58 | #define ENET_PACKED 59 | #elif defined(__GNUC__) || defined(__clang__) 60 | #define ENET_PACKED __attribute__ ((packed)) 61 | #else 62 | #define ENET_PACKED 63 | #endif 64 | 65 | typedef struct _ENetProtocolHeader 66 | { 67 | enet_uint16 peerID; 68 | enet_uint16 sentTime; 69 | } ENET_PACKED ENetProtocolHeader; 70 | 71 | typedef struct _ENetProtocolCommandHeader 72 | { 73 | enet_uint8 command; 74 | enet_uint8 channelID; 75 | enet_uint16 reliableSequenceNumber; 76 | } ENET_PACKED ENetProtocolCommandHeader; 77 | 78 | typedef struct _ENetProtocolAcknowledge 79 | { 80 | ENetProtocolCommandHeader header; 81 | enet_uint16 receivedReliableSequenceNumber; 82 | enet_uint16 receivedSentTime; 83 | } ENET_PACKED ENetProtocolAcknowledge; 84 | 85 | typedef struct _ENetProtocolConnect 86 | { 87 | ENetProtocolCommandHeader header; 88 | enet_uint16 outgoingPeerID; 89 | enet_uint8 incomingSessionID; 90 | enet_uint8 outgoingSessionID; 91 | enet_uint32 mtu; 92 | enet_uint32 windowSize; 93 | enet_uint32 channelCount; 94 | enet_uint32 incomingBandwidth; 95 | enet_uint32 outgoingBandwidth; 96 | enet_uint32 packetThrottleInterval; 97 | enet_uint32 packetThrottleAcceleration; 98 | enet_uint32 packetThrottleDeceleration; 99 | enet_uint32 connectID; 100 | enet_uint32 data; 101 | } ENET_PACKED ENetProtocolConnect; 102 | 103 | typedef struct _ENetProtocolVerifyConnect 104 | { 105 | ENetProtocolCommandHeader header; 106 | enet_uint16 outgoingPeerID; 107 | enet_uint8 incomingSessionID; 108 | enet_uint8 outgoingSessionID; 109 | enet_uint32 mtu; 110 | enet_uint32 windowSize; 111 | enet_uint32 channelCount; 112 | enet_uint32 incomingBandwidth; 113 | enet_uint32 outgoingBandwidth; 114 | enet_uint32 packetThrottleInterval; 115 | enet_uint32 packetThrottleAcceleration; 116 | enet_uint32 packetThrottleDeceleration; 117 | enet_uint32 connectID; 118 | } ENET_PACKED ENetProtocolVerifyConnect; 119 | 120 | typedef struct _ENetProtocolBandwidthLimit 121 | { 122 | ENetProtocolCommandHeader header; 123 | enet_uint32 incomingBandwidth; 124 | enet_uint32 outgoingBandwidth; 125 | } ENET_PACKED ENetProtocolBandwidthLimit; 126 | 127 | typedef struct _ENetProtocolThrottleConfigure 128 | { 129 | ENetProtocolCommandHeader header; 130 | enet_uint32 packetThrottleInterval; 131 | enet_uint32 packetThrottleAcceleration; 132 | enet_uint32 packetThrottleDeceleration; 133 | } ENET_PACKED ENetProtocolThrottleConfigure; 134 | 135 | typedef struct _ENetProtocolDisconnect 136 | { 137 | ENetProtocolCommandHeader header; 138 | enet_uint32 data; 139 | } ENET_PACKED ENetProtocolDisconnect; 140 | 141 | typedef struct _ENetProtocolPing 142 | { 143 | ENetProtocolCommandHeader header; 144 | } ENET_PACKED ENetProtocolPing; 145 | 146 | typedef struct _ENetProtocolSendReliable 147 | { 148 | ENetProtocolCommandHeader header; 149 | enet_uint16 dataLength; 150 | } ENET_PACKED ENetProtocolSendReliable; 151 | 152 | typedef struct _ENetProtocolSendUnreliable 153 | { 154 | ENetProtocolCommandHeader header; 155 | enet_uint16 unreliableSequenceNumber; 156 | enet_uint16 dataLength; 157 | } ENET_PACKED ENetProtocolSendUnreliable; 158 | 159 | typedef struct _ENetProtocolSendUnsequenced 160 | { 161 | ENetProtocolCommandHeader header; 162 | enet_uint16 unsequencedGroup; 163 | enet_uint16 dataLength; 164 | } ENET_PACKED ENetProtocolSendUnsequenced; 165 | 166 | typedef struct _ENetProtocolSendFragment 167 | { 168 | ENetProtocolCommandHeader header; 169 | enet_uint16 startSequenceNumber; 170 | enet_uint16 dataLength; 171 | enet_uint32 fragmentCount; 172 | enet_uint32 fragmentNumber; 173 | enet_uint32 totalLength; 174 | enet_uint32 fragmentOffset; 175 | } ENET_PACKED ENetProtocolSendFragment; 176 | 177 | typedef union _ENetProtocol 178 | { 179 | ENetProtocolCommandHeader header; 180 | ENetProtocolAcknowledge acknowledge; 181 | ENetProtocolConnect connect; 182 | ENetProtocolVerifyConnect verifyConnect; 183 | ENetProtocolDisconnect disconnect; 184 | ENetProtocolPing ping; 185 | ENetProtocolSendReliable sendReliable; 186 | ENetProtocolSendUnreliable sendUnreliable; 187 | ENetProtocolSendUnsequenced sendUnsequenced; 188 | ENetProtocolSendFragment sendFragment; 189 | ENetProtocolBandwidthLimit bandwidthLimit; 190 | ENetProtocolThrottleConfigure throttleConfigure; 191 | } ENET_PACKED ENetProtocol; 192 | 193 | #ifdef _MSC_VER 194 | #pragma pack(pop) 195 | #endif 196 | 197 | #endif /* __ENET_PROTOCOL_H__ */ 198 | 199 | -------------------------------------------------------------------------------- /js/src/World.js: -------------------------------------------------------------------------------- 1 | const TankPacket = require("./Packet/TankPacket"); 2 | 3 | module.exports = class World { 4 | constructor(server, data = {}) { 5 | this.server = server; 6 | this.data = data; 7 | } 8 | 9 | static create(server, name) { 10 | if (!name) return 11 | 12 | return new World(server, { name }) 13 | } 14 | 15 | hasData() { 16 | const keys = Object.keys(this.data).filter(key => key !== "name"); 17 | 18 | return keys.length < 1 ? false : true; 19 | } 20 | 21 | async fetch(shouldGenerate = true) { 22 | if (!this.data.name) return; 23 | 24 | const worldStr = await this.server.cache.get(`world:${this.data.name}`); 25 | 26 | if (worldStr) { 27 | this.data = worldStr; 28 | this.fixData() 29 | } else { 30 | const world = await this.server.collections.worlds.findOne({ name: this.data.name }); 31 | const width = this.data.name === "TINY" ? 50 : 100; 32 | const height = this.data.name === "TALL" ? 100 : (this.data.name === "TINY" ? 50 : 60); 33 | 34 | if (!world && shouldGenerate) // generate 35 | await this.generate(width, height); 36 | else { 37 | if (world) { 38 | this.data = world; 39 | this.fixData() 40 | 41 | await this.server.cache.set(`world:${this.data.name}`, this.data); 42 | } 43 | } 44 | } 45 | } 46 | 47 | async generate(width = 100, height = 60) { 48 | if (typeof this.server.config.worldGenerator === 'function') { 49 | this.data = await this.server.config.worldGenerator(this, width, height) 50 | return 51 | } 52 | 53 | const tileCount = width * height; 54 | const tiles = [] 55 | const mainDoorPosition = Math.floor(Math.random() * width); 56 | 57 | const BEDROCK_START_LEVEL = height - 5; 58 | const DIRT_START_LEVEL = height / 3; 59 | 60 | let x = 0; 61 | let y = 0; 62 | 63 | for (let i = 0; i < tileCount; i++) { 64 | if (x >= width) { 65 | x = 0; 66 | y++; 67 | } 68 | 69 | const tile = { 70 | fg: 0, 71 | bg: 0, 72 | x, 73 | y, 74 | hitsTaken: 0 75 | } 76 | 77 | if (y >= BEDROCK_START_LEVEL || (y === DIRT_START_LEVEL && x === mainDoorPosition)) { 78 | tile.fg = 8; 79 | tile.bg = 14; 80 | } else if (y >= DIRT_START_LEVEL && y < BEDROCK_START_LEVEL) { 81 | tile.fg = 2; 82 | tile.bg = 14; 83 | } else if (y === DIRT_START_LEVEL - 1 && x === mainDoorPosition) { 84 | tile.fg = 6; 85 | 86 | // Main Door options 87 | tile.label = "EXIT"; 88 | tile.doorDestination = "EXIT"; 89 | tile.isDoor = true; 90 | } 91 | 92 | tiles.push(tile); 93 | 94 | x++; 95 | } 96 | 97 | this.data = { 98 | name: this.data.name, 99 | tiles, 100 | tileCount, 101 | width, 102 | height, 103 | playerCount: 0 104 | }; 105 | 106 | await this.server.collections.worlds.replaceOne({ name: this.data.name }, this.data, { upsert: true }); 107 | await this.server.cache.set(`world:${this.data.name}`, this.data); 108 | } 109 | 110 | async serialize() { 111 | if (!this.hasData()) 112 | await this.fetch(); 113 | 114 | let totalBufferSize = 0; 115 | 116 | // calculate total buffer size 117 | if (typeof this.server.config.worldTilesSize === 'function') 118 | totalBufferSize = this.server.config.worldTilesSize(this.data.tiles) 119 | else { 120 | for (const tile of this.data.tiles) { 121 | totalBufferSize += 8; 122 | 123 | switch (tile.fg) { 124 | case 6: { // main door 125 | totalBufferSize += 4 + (tile.label.length || 0); 126 | break; 127 | } 128 | } 129 | } 130 | } 131 | 132 | const WORLD_INFO_SIZE = 20 + this.data.name.length; 133 | const DROPPED_ITEMS_INFO_SIZE = 8; // dropped item count and last dropped item id 134 | const WORLD_WEATHER_SIZE = 15; // 6 bytes before weather, 8 bytes after. total: 15 bytes 135 | 136 | const WORLD_VERSION = 0x0f; 137 | const WORLD_NAME = this.data.name ?? ""; 138 | 139 | totalBufferSize += WORLD_INFO_SIZE + DROPPED_ITEMS_INFO_SIZE + WORLD_WEATHER_SIZE; 140 | 141 | const buffer = Buffer.alloc(totalBufferSize); 142 | 143 | buffer.writeUInt8(WORLD_VERSION); 144 | buffer.writeUInt16LE(WORLD_NAME.length, 6); 145 | buffer.write(WORLD_NAME, 8); 146 | buffer.writeUInt32LE(this.data.width, 8 + WORLD_NAME.length); 147 | buffer.writeUInt32LE(this.data.height, 12 + WORLD_NAME.length); 148 | buffer.writeUInt32LE(this.data.tileCount, 16 + WORLD_NAME.length); 149 | 150 | let pos = 20 + WORLD_NAME.length; 151 | 152 | if (typeof this.server.config.worldSerializationCall === 'function') 153 | this.server.config.worldSerializationCall(pos, buffer, this.data.tiles) 154 | else for (const tile of this.data.tiles) { 155 | buffer.writeUInt16LE(tile.fg, pos); 156 | buffer.writeUInt16LE(tile.bg, pos + 2); 157 | 158 | pos += 4; 159 | 160 | switch (tile.fg) { 161 | case 6: { // main door 162 | const DOOR_LABEL = tile.label || ""; 163 | 164 | buffer.writeUInt8(0x1, pos + 2); 165 | buffer.writeUInt8(0x1, pos + 4); 166 | buffer.writeUInt16LE(DOOR_LABEL.length, pos + 5); 167 | buffer.write(DOOR_LABEL, pos + 7); 168 | 169 | pos += 8 + DOOR_LABEL.length; 170 | break; 171 | } 172 | 173 | default: { 174 | pos += 4; 175 | break; 176 | } 177 | } 178 | } 179 | 180 | return TankPacket.from({ 181 | type: 0x4, 182 | extraData: () => buffer 183 | }); 184 | } 185 | 186 | async saveToCache() { 187 | if (!this.hasData()) return; 188 | 189 | await this.server.cache.set(`world:${this.data.name}`, this.data); 190 | } 191 | 192 | async saveToDb() { 193 | if (!this.hasData()) return; 194 | 195 | delete this.data["_id"]; 196 | await this.server.collections.worlds.replaceOne({ name: this.data.name }, this.data, { upsert: true }); 197 | } 198 | 199 | async uncache() { 200 | await this.server.cache.del(`world:${this.data.name}`); 201 | } 202 | 203 | fixData() { 204 | if (!this.hasData()) return 205 | 206 | const types = { 207 | name: 'string', 208 | width: 'number', 209 | height: 'number', 210 | tileCount: 'number', 211 | tiles: 'array', 212 | playerCount: 'number' 213 | } 214 | 215 | const keys = Object.entries(types) 216 | keys.forEach( 217 | entry => { 218 | const key = entry[0] 219 | const val = entry[1] 220 | 221 | switch (val) { 222 | case 'array': { 223 | if (!Array.isArray(this.data[key])) 224 | this.data[key] = [] 225 | break 226 | } 227 | 228 | case 'string': { 229 | if (typeof this.data[key] !== 'string') 230 | this.data[key] = '' 231 | break 232 | } 233 | 234 | case 'number': { 235 | if (typeof this.data[key] !== 'number') 236 | this.data[key] = 0 237 | break 238 | } 239 | 240 | default: { 241 | this.data[key] = {} 242 | break 243 | } 244 | } 245 | } 246 | ) 247 | } 248 | } -------------------------------------------------------------------------------- /js/src/Server.js: -------------------------------------------------------------------------------- 1 | const Native = require("./NativeWrapper"); 2 | const Peer = require("./Peer"); 3 | const { EventEmitter } = require("events"); 4 | const TankPacket = require("./Packet/TankPacket"); 5 | const mongo = require("mongodb"); 6 | const fs = require("fs"); 7 | const World = require("./World"); 8 | 9 | const SERVER_DAT_DEFAULT_PATH = `${__dirname}/Data/server.dat` 10 | 11 | module.exports = class Server extends EventEmitter { 12 | constructor(config) { 13 | super(); 14 | 15 | this.config = config 16 | if (!this.config.server?.serverDatPath) 17 | this.config.server.serverDatPath = SERVER_DAT_DEFAULT_PATH 18 | 19 | if (typeof this.config.server?.itemsDatFile !== 'string') 20 | throw new Error('Please supply a proper path to items.dat') 21 | 22 | // use/create cache connection 23 | let cache; 24 | if (!this.config.cache) 25 | cache = new (require('./Redis'))(this.config.redis) 26 | else cache = this.config.cache 27 | 28 | // apply the cache to the server prop 29 | this.cache = cache 30 | 31 | // create other event emitters for users to use 32 | this.events = new EventEmitter(); 33 | 34 | // this is for the items dat 35 | this.items = { meta: null }; 36 | 37 | // mongo collections 38 | this.collections = null; 39 | 40 | // last available user id 41 | this.availableUserID = null; 42 | 43 | // handle events that are emitted from the core 44 | this.on("connect", (connectID) => { 45 | const peer = new Peer(this, { connectID }); 46 | this.events.emit("connect", peer); 47 | }); 48 | 49 | this.on("receive", (connectID, packet) => { 50 | const peer = new Peer(this, { connectID }); 51 | this.events.emit("receive", peer, packet); 52 | }); 53 | 54 | this.on("disconnect", (connectID) => { 55 | const peer = new Peer(this, { connectID }); 56 | this.events.emit("disconnect", peer); 57 | }); 58 | 59 | // handle on exit 60 | process.on("SIGINT", async () => { 61 | if (!this.cache || !this.collections?.players || !this.collections?.worlds) return process.exit() 62 | 63 | const players = await this.cache.get("players"); 64 | let count = 0; 65 | 66 | for (const player of players) { 67 | if (!player) continue; 68 | 69 | delete player["_id"]; 70 | 71 | const peer = new Peer(this, player); 72 | peer.data.hasMovedInWorld = false; 73 | peer.data.currentWorld = 'EXIT' 74 | 75 | await peer.saveToDb(); 76 | 77 | count++; 78 | } 79 | 80 | await this.cache.del("player"); 81 | await this.log("Saved", count, `player${count === 1 ? "" : "s"}.`); 82 | 83 | const worldKeys = await this.cache.keys("world:*"); 84 | count = 0; 85 | 86 | for (const key of worldKeys) { 87 | const world = new World(this, { name: key.split(":")[1] }); // world:NAME_HERE 88 | await world.fetch(); 89 | 90 | world.data.playerCount = 0 91 | 92 | await world.saveToDb(); 93 | await world.uncache(); 94 | 95 | count++; 96 | } 97 | 98 | await this.log("Saved", count, `world${count === 1 ? "" : "s"}`); 99 | 100 | // handle server.dat 101 | const HEADER = "POGTOPIA"; 102 | const serverDat = Buffer.alloc(HEADER.length + 4); 103 | 104 | serverDat.write(HEADER); 105 | serverDat.writeUInt32LE(this.availableUserID, HEADER.length); 106 | 107 | fs.writeFileSync(this.config.server.serverDatPath, serverDat); // save the server.dat file 108 | 109 | process.exit(); 110 | }); 111 | } 112 | 113 | getCDN() { 114 | return this.config.server.cdn ?? { host: '', url: '' }; 115 | } 116 | 117 | clearServerDat() { 118 | this.availableUserID = 0; 119 | } 120 | 121 | hasCollections() { 122 | return this.collections ? ( 123 | this.collections.server && 124 | this.collections.players && 125 | this.collections.worlds 126 | ) : 127 | false 128 | } 129 | 130 | async start() { 131 | this.setItemsDat(this.config.server?.itemsDatFile) 132 | 133 | const itemKeys = Object.keys(this.items) 134 | .filter(key => key !== 'meta') 135 | 136 | if (itemKeys.length < 1) 137 | throw new Error("There are some stuff missing in-order to make the server online. Please check if you have set the handlers for each events, and the items.dat file"); 138 | 139 | Native.Init(this.config.server.port); // set our server port 140 | Native.setEmitter(this.emit.bind(this)); // set the emitter for events 141 | Native.createHost(); // create ENet Host 142 | 143 | const loop = () => new Promise((resolve) => setImmediate(() => resolve(Native.receive()))); 144 | const listen = async () => { 145 | while (true) 146 | await loop(); 147 | } 148 | 149 | listen(); // listen for events 150 | 151 | let mongoClient; 152 | try { 153 | const { user, pass, host, port } = this.config.db 154 | 155 | mongoClient = new mongo.MongoClient(`mongodb://${user}:${pass}@${host || 'localhost'}:${port || 27017}`, { useUnifiedTopology: true }); 156 | await mongoClient.connect(); // connect to mongodb 157 | } catch (err) { 158 | console.log('Failed connecting to MongoDB. Error:', err.message) 159 | return process.exit(); 160 | } 161 | 162 | const database = mongoClient.db("pogtopia"); 163 | 164 | // set mongo collections 165 | this.collections = { 166 | players: database.collection("players"), 167 | worlds: database.collection("worlds"), 168 | server: database.collection("server") 169 | } 170 | 171 | await this.log("Mongo Collections now available to use."); 172 | 173 | // check serverDat 174 | const serverDat = fs.existsSync(this.config.server.serverDatPath) ? fs.readFileSync(this.config.server.serverDatPath) : Buffer.alloc(0); 175 | const HEADER = "POGTOPIA"; 176 | const TOTAL_LEN = HEADER.length + 4; 177 | 178 | if (serverDat.length != TOTAL_LEN || (serverDat.length >= HEADER.length && !serverDat.toString().startsWith(HEADER))) { // reset the serverDat 179 | const file = Buffer.alloc(TOTAL_LEN); 180 | file.write(HEADER); 181 | 182 | this.availableUserID = 0; 183 | } else this.availableUserID = serverDat.readUInt32LE(HEADER.length); 184 | 185 | await this.log("Server.dat file processed."); 186 | 187 | await this.cache.set("players", []); // set an empty array for players cache 188 | } 189 | 190 | log(...args) { 191 | return new Promise((resolve) => setImmediate(() => resolve(console.log(`[${new Date().toLocaleDateString()}] |`, ...args)))); 192 | } 193 | 194 | setHandler(type, callback) { 195 | this.events.on(type, async (...args) => await callback(...args)); 196 | } 197 | 198 | setItemsDat(path) { 199 | if (!path) return 200 | let file; 201 | 202 | try { 203 | file = fs.readFileSync(path) 204 | } catch(err) { 205 | throw new Error('Failed finding items.dat at path:', path) 206 | } 207 | 208 | this.items.content = file 209 | this.items.packet = TankPacket.from({ 210 | type: 0x10, 211 | extraData: () => file 212 | }) 213 | this.items.hash = (() => { 214 | let h = 0x55555555; 215 | 216 | for (const byte of file) 217 | h = (h >>> 27) + (h << 5) + byte; 218 | 219 | return h; 220 | })() 221 | } 222 | 223 | stringPacketToMap(packet) { 224 | const data = new Map(); 225 | packet[packet.length - 1] = 0; 226 | 227 | const stringbytes = packet.slice(4).toString(); 228 | const stringPerLine = stringbytes.split("\n"); 229 | 230 | for (const line of stringPerLine) { 231 | const pair = line.split("|").filter(i => i); 232 | if (!pair[0] || !pair[1]) continue; 233 | 234 | if (pair[1][pair[1].length - 1] == "\x00") 235 | pair[1] = pair[1].slice(0, -1); 236 | 237 | data.set(pair[0], pair[1]); 238 | } 239 | 240 | return data; 241 | } 242 | 243 | async forEach(type, callback) { 244 | if (type === "player") { 245 | const players = await this.cache.get("players"); 246 | if (!Array.isArray(players)) return; 247 | 248 | for (const player of players) { 249 | if (!player) continue; 250 | callback(new Peer(this, player)); 251 | } 252 | } else if (type === "world") { 253 | const worldKeys = await this.cache.keys('world:*') 254 | .filter(key => key.toLowerCase().startsWith('world')) 255 | 256 | for (const key of worldKeys) { 257 | const world = await this.cache.get(key) 258 | 259 | callback(new World(this, world)) 260 | } 261 | } 262 | } 263 | 264 | async find(type, filter) { 265 | if (type === "player") return await this.collections.players.find(filter).toArray() 266 | else if (type === "world") return await this.collections.worlds.find(filter).toArray() 267 | } 268 | 269 | setItemMeta(meta) { 270 | this.items.meta = meta 271 | } 272 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | aggregate-error@^3.0.0: 6 | version "3.1.0" 7 | resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" 8 | integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== 9 | dependencies: 10 | clean-stack "^2.0.0" 11 | indent-string "^4.0.0" 12 | 13 | bl@^2.2.1: 14 | version "2.2.1" 15 | resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" 16 | integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g== 17 | dependencies: 18 | readable-stream "^2.3.5" 19 | safe-buffer "^5.1.1" 20 | 21 | bson@^1.1.4: 22 | version "1.1.5" 23 | resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.5.tgz#2aaae98fcdf6750c0848b0cba1ddec3c73060a34" 24 | integrity sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg== 25 | 26 | clean-stack@^2.0.0: 27 | version "2.2.0" 28 | resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" 29 | integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== 30 | 31 | cluster-key-slot@^1.1.0: 32 | version "1.1.0" 33 | resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" 34 | integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== 35 | 36 | core-util-is@~1.0.0: 37 | version "1.0.2" 38 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 39 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 40 | 41 | debug@^4.1.1: 42 | version "4.2.0" 43 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" 44 | integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== 45 | dependencies: 46 | ms "2.1.2" 47 | 48 | denque@^1.1.0, denque@^1.4.1: 49 | version "1.4.1" 50 | resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" 51 | integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== 52 | 53 | indent-string@^4.0.0: 54 | version "4.0.0" 55 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" 56 | integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== 57 | 58 | inherits@~2.0.3: 59 | version "2.0.4" 60 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 61 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 62 | 63 | ioredis@^4.17.3: 64 | version "4.19.0" 65 | resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.19.0.tgz#1ab3b36362cb3e805c4dbe90fbb7061182f94b63" 66 | integrity sha512-7NIkLtpDQ/6WkEircBljnYz/E+kDQcwCJspfR504/tzyXJJQcHaSofMP6G0nuuLfDpOVnpS0AEwDrNIbW38HGg== 67 | dependencies: 68 | cluster-key-slot "^1.1.0" 69 | debug "^4.1.1" 70 | denque "^1.1.0" 71 | lodash.defaults "^4.2.0" 72 | lodash.flatten "^4.4.0" 73 | p-map "^4.0.0" 74 | redis-commands "1.6.0" 75 | redis-errors "^1.2.0" 76 | redis-parser "^3.0.0" 77 | standard-as-callback "^2.0.1" 78 | 79 | isarray@~1.0.0: 80 | version "1.0.0" 81 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 82 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 83 | 84 | lodash.defaults@^4.2.0: 85 | version "4.2.0" 86 | resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" 87 | integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= 88 | 89 | lodash.flatten@^4.4.0: 90 | version "4.4.0" 91 | resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" 92 | integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= 93 | 94 | memory-pager@^1.0.2: 95 | version "1.5.0" 96 | resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" 97 | integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== 98 | 99 | mongodb@^3.6.2: 100 | version "3.6.2" 101 | resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.6.2.tgz#1154a4ac107bf1375112d83a29c5cf97704e96b6" 102 | integrity sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA== 103 | dependencies: 104 | bl "^2.2.1" 105 | bson "^1.1.4" 106 | denque "^1.4.1" 107 | require_optional "^1.0.1" 108 | safe-buffer "^5.1.2" 109 | optionalDependencies: 110 | saslprep "^1.0.0" 111 | 112 | ms@2.1.2: 113 | version "2.1.2" 114 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 115 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 116 | 117 | node-addon-api@^3.0.0: 118 | version "3.0.2" 119 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.2.tgz#04bc7b83fd845ba785bb6eae25bc857e1ef75681" 120 | integrity sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg== 121 | 122 | p-map@^4.0.0: 123 | version "4.0.0" 124 | resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" 125 | integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== 126 | dependencies: 127 | aggregate-error "^3.0.0" 128 | 129 | process-nextick-args@~2.0.0: 130 | version "2.0.1" 131 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 132 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 133 | 134 | readable-stream@^2.3.5: 135 | version "2.3.7" 136 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 137 | integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 138 | dependencies: 139 | core-util-is "~1.0.0" 140 | inherits "~2.0.3" 141 | isarray "~1.0.0" 142 | process-nextick-args "~2.0.0" 143 | safe-buffer "~5.1.1" 144 | string_decoder "~1.1.1" 145 | util-deprecate "~1.0.1" 146 | 147 | redis-commands@1.6.0: 148 | version "1.6.0" 149 | resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.6.0.tgz#36d4ca42ae9ed29815cdb30ad9f97982eba1ce23" 150 | integrity sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ== 151 | 152 | redis-errors@^1.0.0, redis-errors@^1.2.0: 153 | version "1.2.0" 154 | resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" 155 | integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= 156 | 157 | redis-parser@^3.0.0: 158 | version "3.0.0" 159 | resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" 160 | integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= 161 | dependencies: 162 | redis-errors "^1.0.0" 163 | 164 | require_optional@^1.0.1: 165 | version "1.0.1" 166 | resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" 167 | integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g== 168 | dependencies: 169 | resolve-from "^2.0.0" 170 | semver "^5.1.0" 171 | 172 | resolve-from@^2.0.0: 173 | version "2.0.0" 174 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" 175 | integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= 176 | 177 | safe-buffer@^5.1.1, safe-buffer@^5.1.2: 178 | version "5.2.1" 179 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 180 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 181 | 182 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 183 | version "5.1.2" 184 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 185 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 186 | 187 | saslprep@^1.0.0: 188 | version "1.0.3" 189 | resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" 190 | integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== 191 | dependencies: 192 | sparse-bitfield "^3.0.3" 193 | 194 | semver@^5.1.0: 195 | version "5.7.1" 196 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 197 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 198 | 199 | sparse-bitfield@^3.0.3: 200 | version "3.0.3" 201 | resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" 202 | integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= 203 | dependencies: 204 | memory-pager "^1.0.2" 205 | 206 | standard-as-callback@^2.0.1: 207 | version "2.0.1" 208 | resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.0.1.tgz#ed8bb25648e15831759b6023bdb87e6b60b38126" 209 | integrity sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg== 210 | 211 | string_decoder@~1.1.1: 212 | version "1.1.1" 213 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 214 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 215 | dependencies: 216 | safe-buffer "~5.1.0" 217 | 218 | util-deprecate@~1.0.1: 219 | version "1.0.2" 220 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 221 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 222 | -------------------------------------------------------------------------------- /docs/interfaces/onsupermainargs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OnSuperMainArgs | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface OnSuperMainArgs

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Hierarchy

69 |
    70 |
  • 71 | OnSuperMainArgs 72 |
  • 73 |
74 |
75 |
76 |

Index

77 |
78 |
79 |
80 |

Properties

81 | 85 |
86 |
87 |
88 |
89 |
90 |

Properties

91 |
92 | 93 |

arg3

94 |
arg3: string
95 | 100 |
101 |
102 | 103 |

arg4

104 |
arg4: string
105 | 110 |
111 |
112 |
113 | 234 |
235 |
236 |
237 |
238 |

Legend

239 |
240 |
    241 |
  • Constructor
  • 242 |
  • Property
  • 243 |
  • Method
  • 244 |
245 |
    246 |
  • Private property
  • 247 |
  • Private method
  • 248 |
249 |
    250 |
  • Property
  • 251 |
252 |
    253 |
  • Static method
  • 254 |
255 |
256 |
257 |
258 |
259 |

Generated using TypeDoc

260 |
261 |
262 | 263 | 264 | -------------------------------------------------------------------------------- /js/src/Peer.js: -------------------------------------------------------------------------------- 1 | const Native = require("./NativeWrapper"); 2 | const World = require("./World"); 3 | const Variant = require("./Packet/Variant"); 4 | const TextPacket = require("./Packet/TextPacket"); 5 | const TankPacket = require("./Packet/TankPacket"); 6 | const Constants = require('./Constants') 7 | 8 | module.exports = class Peer { 9 | constructor(server, data = {}) { 10 | if (!data || !data.connectID && isNaN(data.connectID)) 11 | throw new Error("Server crash due to invalid peer data."); 12 | 13 | this.data = data || null; 14 | this.server = server; 15 | } 16 | 17 | async create(data, saveToCache) { 18 | this.data = data; 19 | 20 | if (saveToCache) { 21 | const players = await this.server.cache.get("players"); 22 | if (!Array.isArray(players)) return; 23 | 24 | players[this.data.connectID] = this.data; 25 | await this.server.cache.set("players", players); 26 | } 27 | 28 | await this.server.collections.players.insertOne(data); 29 | } 30 | 31 | send(data) { 32 | Native.send(data, this.data.connectID); 33 | } 34 | 35 | send_multiple(...data) { 36 | Native.send_multiple(data, this.data.connectID) 37 | } 38 | 39 | async disconnect(type) { 40 | type = type?.toLowerCase(); 41 | 42 | await this.fetch('cache') 43 | 44 | Native.disconnect(type, this.data.connectID); 45 | if (this.hasPlayerData()) { 46 | this.data.hasMovedInWorld = false 47 | this.data.online = false 48 | 49 | await this.saveToDb() 50 | } 51 | 52 | const players = await this.server.cache.get("players"); 53 | 54 | if (!Array.isArray(players) || !players[this.data.connectID]) return; 55 | players[this.data.connectID] = null; 56 | 57 | await this.server.cache.set("players", players); 58 | } 59 | 60 | requestLoginInformation() { 61 | const buffer = Buffer.alloc(4); 62 | buffer.writeUInt8(0x1); 63 | 64 | this.send(buffer); 65 | } 66 | 67 | async saveToDb() { 68 | delete this.data["_id"]; // we need this gone if present 69 | const data = Object.assign( 70 | {}, 71 | this.data 72 | ) 73 | 74 | data.displayName = data.displayName.replace(/`.|`/g, '') 75 | 76 | await this.server.collections.players.replaceOne({ userID: data.userID }, data, { upsert: true }); 77 | } 78 | 79 | async saveToCache() { 80 | const players = await this.server.cache.get("players"); 81 | 82 | if (!Array.isArray(players)) return; 83 | players[this.data.connectID] = this.data; 84 | 85 | await this.server.cache.set("players", players); 86 | } 87 | 88 | hasPlayerData() { 89 | return Object.keys(this.data).length > 1 ? true : false; 90 | } 91 | 92 | async setOnline(cache, online = false) { 93 | if (!this.hasPlayerData()) return 94 | 95 | this.data.online = online 96 | 97 | if (cache) 98 | await this.saveToCache() 99 | 100 | await this.saveToDb() 101 | } 102 | 103 | async alreadyInCache(userID = null) { 104 | const players = await this.server.cache.get("players"); 105 | if (!Array.isArray(players)) return; 106 | 107 | const condition = userID ? 108 | players.find(p => p && p.userID === userID) : 109 | players[this.data.connectID] 110 | 111 | if (!condition) 112 | return null; 113 | else return condition; 114 | } 115 | 116 | async fetch(type, filter) { 117 | if (!filter) filter = this.data; 118 | 119 | switch (type) { 120 | case "cache": { 121 | const players = await this.server.cache.get("players"); 122 | if (!Array.isArray(players)) return; 123 | 124 | const data = players[this.data.connectID]; 125 | 126 | if (data) { 127 | data.connectID = this.data.connectID; 128 | this.data = data; 129 | } 130 | 131 | break; 132 | } 133 | 134 | case "db": { 135 | if (!this.server.collections) return 136 | 137 | let result = await this.server.collections.players.findOne(filter); 138 | if (!result) 139 | result = {}; 140 | else delete result["_id"]; 141 | 142 | result.connectID = this.data.connectID; 143 | 144 | this.data = result; 145 | break; 146 | } 147 | } 148 | } 149 | 150 | async join(name, isSuperMod, xPos, yPos) { 151 | if (!name) 152 | name = '' 153 | 154 | name = name.toUpperCase().trim(); 155 | 156 | if (!this.hasPlayerData() || name.match(/\W+|_/g) || name.length > 24 || name.length < 1) 157 | return this.send_multiple( 158 | Variant.from( 159 | "OnFailedToEnterWorld", 160 | 1 161 | ), 162 | Variant.from( 163 | "OnConsoleMessage", 164 | "Something went wrong. Please try again." 165 | ) 166 | ) 167 | 168 | if (name === 'EXIT') 169 | return this.send_multiple( 170 | Variant.from( 171 | "OnFailedToEnterWorld", 172 | 1 173 | ), 174 | Variant.from( 175 | "OnConsoleMessage", 176 | "`wEXIT`` from what?" 177 | ) 178 | ) 179 | 180 | const world = new World(this.server, { name }); 181 | const packet = await world.serialize(); 182 | 183 | this.send(packet); 184 | 185 | const mainDoor = world.data.tiles.find(tile => tile.fg === 6); 186 | 187 | world.data.playerCount++ 188 | 189 | const x = typeof xPos === 'number' ? 190 | xPos : 191 | mainDoor?.x || 0 192 | const y = typeof yPos === 'number' ? 193 | yPos : 194 | mainDoor?.y || 0 195 | 196 | this.data.x = x * 32; 197 | this.data.y = y * 32; 198 | this.data.currentWorld = name; 199 | 200 | await this.saveToCache(); 201 | await world.saveToCache() 202 | 203 | // send OnSpawn call 204 | this.send(Variant.from( 205 | "OnSpawn", 206 | `spawn|avatar 207 | netID|${this.data.connectID} 208 | userID|${this.data.userID} 209 | colrect|0|0|20|30 210 | posXY|${this.data.x}|${this.data.y} 211 | name|${this.data.displayName}\`\` 212 | country|${this.data.country} 213 | invis|0 214 | mstate|0 215 | smstate|${isSuperMod ? 1 : 0} 216 | type|local`)); 217 | 218 | // loop through each player 219 | this.server.forEach("player", (otherPeer) => { 220 | if (otherPeer.data.userID !== this.data.userID && otherPeer.data.currentWorld === this.data.currentWorld && otherPeer.data.currentWorld !== "EXIT") { 221 | // send ourselves to the other peers 222 | otherPeer.send(Variant.from( 223 | "OnSpawn", 224 | `spawn|avatar 225 | netID|${this.data.connectID} 226 | userID|${this.data.userID} 227 | colrect|0|0|20|30 228 | posXY|${this.data.x}|${this.data.y} 229 | name|${this.data.displayName}\`\` 230 | country|${this.data.country} 231 | invis|0 232 | mstate|0 233 | smstate|${isSuperMod ? 1 : 0}`)); 234 | 235 | // send the peer to ourselves 236 | this.send(Variant.from( 237 | "OnSpawn", 238 | `spawn|avatar 239 | netID|${otherPeer.data.connectID} 240 | userID|${otherPeer.data.userID} 241 | colrect|0|0|20|30 242 | posXY|${otherPeer.data.x}|${otherPeer.data.y} 243 | name|${otherPeer.data.displayName}\`\` 244 | country|${otherPeer.data.country} 245 | invis|0 246 | mstate|0 247 | smstate|0`)) 248 | } 249 | }); 250 | } 251 | 252 | audio(file, delay = 0) { 253 | this.send(TextPacket.from( 254 | 0x3, 255 | "action|play_sfx", 256 | `file|${file}.wav`, 257 | `delayMS|${delay}` 258 | )); 259 | } 260 | 261 | inventory() { 262 | const tank = TankPacket.from({ 263 | type: 0x9, 264 | extraData: () => { 265 | const buffer = Buffer.alloc(7 + (this.data.inventory.items.length * 4)); 266 | 267 | buffer.writeUInt8(0x1) 268 | buffer.writeUInt32LE(this.data.inventory.maxSize, 1) 269 | buffer.writeUInt16LE(this.data.inventory.items.length, 5) 270 | 271 | let pos = 7 272 | 273 | this.data.inventory.items.forEach( 274 | item => { 275 | buffer.writeUInt16LE(item.id, pos) 276 | buffer.writeUInt16LE(item.amount, pos + 2) 277 | 278 | pos += 4 279 | } 280 | ) 281 | 282 | return buffer; 283 | } 284 | }); 285 | 286 | const tankbuffer = tank.parse(); 287 | 288 | this.send(tankbuffer); 289 | } 290 | 291 | async world(name, fetchDataAfter) { 292 | if (typeof name === 'boolean') 293 | fetchDataAfter = name 294 | 295 | if (typeof name !== 'string') 296 | name = this.data.currentWorld 297 | 298 | const world = new World(this.server, { name }) 299 | if (fetchDataAfter) 300 | await world.fetch(false) 301 | 302 | return world 303 | } 304 | 305 | cloth_packet(silenced) { 306 | if (!this.hasPlayerData()) return 307 | 308 | return Variant.from( 309 | { 310 | netID: this.data.connectID 311 | }, 312 | 'OnSetClothing', 313 | [this.data.clothes.hair, this.data.clothes.shirt, this.data.clothes.pants], 314 | [this.data.clothes.shoes, this.data.clothes.face, this.data.clothes.hand], 315 | [this.data.clothes.back, this.data.clothes.mask, this.data.clothes.necklace], 316 | this.data.skinColor ?? Constants.DEFAULT_SKIN, 317 | [this.data.clothes.ances, silenced ? 0 : 1, 0] 318 | ) 319 | } 320 | 321 | async remove_item_from_inventory(id, amount = 1) { 322 | const item = this.data.inventory.items.find(item => item.id === id) 323 | if (!item || 324 | item.amount < 1) return 325 | 326 | item.amount -= amount 327 | 328 | if (item.amount < 1) 329 | this.data.inventory.items = this.data.inventory.items.filter(item => item.id !== id) 330 | 331 | await this.saveToCache() 332 | } 333 | 334 | async add_item_to_inventory(id, amount = 1) { 335 | if (typeof amount !== 'string' || 336 | amount > 200) 337 | amount = 1 338 | 339 | const item = this.data.inventory.items.find(item => item.id === id) 340 | if (!item && 341 | this.data.inventory.items.length < this.data.inventory.maxSize) { 342 | this.data.inventory.items.push( 343 | { 344 | id: id, 345 | amount 346 | } 347 | ) 348 | } else if (item) { 349 | if (item.amount + amount > 200) return 350 | item.amount += amount 351 | } 352 | 353 | await this.saveToCache() 354 | } 355 | 356 | async leave(sendToMenu) { 357 | if (this.data.currentWorld === 'EXIT' || !this.data.currentWorld) return 358 | 359 | const world = await this.world(this.data.currentWorld, true) 360 | world.data.playerCount-- 361 | 362 | this.data.hasMovedInWorld = false 363 | this.data.displayName = this.data.displayName.replace( 364 | /`./g, 365 | '' 366 | ) 367 | 368 | if (sendToMenu) 369 | this.send( 370 | Variant.from('OnRequestWorldSelectMenu') 371 | ) 372 | 373 | await this.server.forEach( 374 | 'player', 375 | eachPeer => { 376 | if (eachPeer.data.currentWorld === world.data.name && 377 | eachPeer.data.connectID !== this.data.connectID) 378 | eachPeer.send( 379 | Variant.from( 380 | 'OnRemove', 381 | `netID|${this.data.connectID}` 382 | ) 383 | ) 384 | } 385 | ) 386 | 387 | if (world.data.playerCount < 1) { // remove to cache if empty 388 | world.data.playerCount = 0 389 | 390 | await world.saveToDb() 391 | await world.uncache() 392 | } else await world.saveToCache() 393 | 394 | this.data.currentWorld = 'EXIT' 395 | 396 | await this.saveToCache() 397 | } 398 | 399 | isConnected() { 400 | return Native.isConnected(this.data.connectID) 401 | } 402 | } -------------------------------------------------------------------------------- /docs/interfaces/cdnoptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CDNOptions | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface CDNOptions

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Options for the CDN to use

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | CDNOptions 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 92 |
93 |
94 |
95 |
96 |
97 |

Properties

98 |
99 | 100 |

host

101 |
host: string
102 | 107 |
108 |
109 |

The host site of the CDN

110 |
111 |
112 |
113 |
114 | 115 |

url

116 |
url: string
117 | 122 |
123 |
124 |

The url of the CDN

125 |
126 |
127 |
128 |
129 |
130 | 251 |
252 |
253 |
254 |
255 |

Legend

256 |
257 |
    258 |
  • Constructor
  • 259 |
  • Property
  • 260 |
  • Method
  • 261 |
262 |
    263 |
  • Private property
  • 264 |
  • Private method
  • 265 |
266 |
    267 |
  • Property
  • 268 |
269 |
    270 |
  • Static method
  • 271 |
272 |
273 |
274 |
275 |
276 |

Generated using TypeDoc

277 |
278 |
279 | 280 | 281 | -------------------------------------------------------------------------------- /docs/interfaces/inventoryitem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | InventoryItem | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface InventoryItem

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

A representation of an item in an inventory

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | InventoryItem 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 92 |
93 |
94 |
95 |
96 |
97 |

Properties

98 |
99 | 100 |

amount

101 |
amount: number
102 | 107 |
108 |
109 |

The amount of that item in the inventory

110 |
111 |
112 |
113 |
114 | 115 |

id

116 |
id: number
117 | 122 |
123 |
124 |

The id of the item

125 |
126 |
127 |
128 |
129 |
130 | 251 |
252 |
253 |
254 |
255 |

Legend

256 |
257 |
    258 |
  • Constructor
  • 259 |
  • Property
  • 260 |
  • Method
  • 261 |
262 |
    263 |
  • Private property
  • 264 |
  • Private method
  • 265 |
266 |
    267 |
  • Property
  • 268 |
269 |
    270 |
  • Static method
  • 271 |
272 |
273 |
274 |
275 |
276 |

Generated using TypeDoc

277 |
278 |
279 | 280 | 281 | -------------------------------------------------------------------------------- /docs/interfaces/peerinventory.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PeerInventory | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface PeerInventory

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

The inventory data of a peer

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | PeerInventory 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 92 |
93 |
94 |
95 |
96 |
97 |

Properties

98 |
99 | 100 |

items

101 |
items: InventoryItem[]
102 | 107 |
108 |
109 |

The items in the inventory

110 |
111 |
112 |
113 |
114 | 115 |

maxSize

116 |
maxSize: number
117 | 122 |
123 |
124 |

The max slots of a peer's inventory

125 |
126 |
127 |
128 |
129 |
130 | 251 |
252 |
253 |
254 |
255 |

Legend

256 |
257 |
    258 |
  • Constructor
  • 259 |
  • Property
  • 260 |
  • Method
  • 261 |
262 |
    263 |
  • Private property
  • 264 |
  • Private method
  • 265 |
266 |
    267 |
  • Property
  • 268 |
269 |
    270 |
  • Static method
  • 271 |
272 |
273 |
274 |
275 |
276 |

Generated using TypeDoc

277 |
278 |
279 | 280 | 281 | -------------------------------------------------------------------------------- /docs/interfaces/variantoptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | VariantOptions | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface VariantOptions

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Options for the Variant Packet

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | VariantOptions 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 92 |
93 |
94 |
95 |
96 |
97 |

Properties

98 |
99 | 100 |

Optional delay

101 |
delay: number
102 | 107 |
108 |
109 |

The delay on when to execute the packet (in ms).

110 |
111 |
112 |
113 |
114 | 115 |

Optional netID

116 |
netID: number
117 | 122 |
123 |
124 |

The netID associated with the Packet.

125 |
126 |
127 |
128 |
129 |
130 | 251 |
252 |
253 |
254 |
255 |

Legend

256 |
257 |
    258 |
  • Constructor
  • 259 |
  • Property
  • 260 |
  • Method
  • 261 |
262 |
    263 |
  • Private property
  • 264 |
  • Private method
  • 265 |
266 |
    267 |
  • Property
  • 268 |
269 |
    270 |
  • Static method
  • 271 |
272 |
273 |
274 |
275 |
276 |

Generated using TypeDoc

277 |
278 |
279 | 280 | 281 | -------------------------------------------------------------------------------- /docs/interfaces/collections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Collections | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface Collections

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Mongodb collections

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | Collections 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 93 |
94 |
95 |
96 |
97 |
98 |

Properties

99 |
100 | 101 |

players

102 |
players: Mongo.Collection
103 | 108 |
109 |
110 | 111 |

server

112 |
server: Mongo.Collection
113 | 118 |
119 |
120 | 121 |

worlds

122 |
worlds: Mongo.Collection
123 | 128 |
129 |
130 |
131 | 255 |
256 |
257 |
258 |
259 |

Legend

260 |
261 |
    262 |
  • Constructor
  • 263 |
  • Property
  • 264 |
  • Method
  • 265 |
266 |
    267 |
  • Private property
  • 268 |
  • Private method
  • 269 |
270 |
    271 |
  • Property
  • 272 |
273 |
    274 |
  • Static method
  • 275 |
276 |
277 |
278 |
279 |
280 |

Generated using TypeDoc

281 |
282 |
283 | 284 | 285 | -------------------------------------------------------------------------------- /docs/interfaces/itemsdatmeta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ItemsDatMeta | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface ItemsDatMeta

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Items dat meta data and the items itself

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | ItemsDatMeta 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 93 |
94 |
95 |
96 |
97 |
98 |

Properties

99 |
100 | 101 |

itemCount

102 |
itemCount: number
103 | 108 |
109 |
110 | 111 |

items

112 | 113 | 118 |
119 |
120 | 121 |

version

122 |
version: number
123 | 128 |
129 |
130 |
131 | 255 |
256 |
257 |
258 |
259 |

Legend

260 |
261 |
    262 |
  • Constructor
  • 263 |
  • Property
  • 264 |
  • Method
  • 265 |
266 |
    267 |
  • Private property
  • 268 |
  • Private method
  • 269 |
270 |
    271 |
  • Property
  • 272 |
273 |
    274 |
  • Static method
  • 275 |
276 |
277 |
278 |
279 |
280 |

Generated using TypeDoc

281 |
282 |
283 | 284 | 285 | -------------------------------------------------------------------------------- /docs/interfaces/httpoptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HTTPOptions | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface HTTPOptions

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Hierarchy

69 |
    70 |
  • 71 | HTTPOptions 72 |
  • 73 |
74 |
75 |
76 |

Index

77 |
78 |
79 |
80 |

Properties

81 | 86 |
87 |
88 |
89 |
90 |
91 |

Properties

92 |
93 | 94 |

Optional httpsEnabled

95 |
httpsEnabled: boolean
96 | 101 |
102 |
103 | 104 |

Optional serverIP

105 |
serverIP: string
106 | 111 |
112 |
113 | 114 |

Optional serverPort

115 |
serverPort: string | number
116 | 121 |
122 |
123 |
124 | 248 |
249 |
250 |
251 |
252 |

Legend

253 |
254 |
    255 |
  • Constructor
  • 256 |
  • Property
  • 257 |
  • Method
  • 258 |
259 |
    260 |
  • Private property
  • 261 |
  • Private method
  • 262 |
263 |
    264 |
  • Property
  • 265 |
266 |
    267 |
  • Static method
  • 268 |
269 |
270 |
271 |
272 |
273 |

Generated using TypeDoc

274 |
275 |
276 | 277 | 278 | -------------------------------------------------------------------------------- /docs/enums/packetmessagetypes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PacketMessageTypes | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Enumeration PacketMessageTypes

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Message types for what Growtopia Sends, or what to send.

71 |
72 |
73 |
74 |
75 |

Index

76 |
77 |
78 |
79 |

Enumeration members

80 | 86 |
87 |
88 |
89 |
90 |
91 |

Enumeration members

92 |
93 | 94 |

ACTION

95 |
ACTION: = 3
96 | 101 |
102 |
103 | 104 |

REQUEST_LOGIN_INFO

105 |
REQUEST_LOGIN_INFO: = 1
106 | 111 |
112 |
113 | 114 |

STRING

115 |
STRING: = 2
116 | 121 |
122 |
123 | 124 |

TANK_PACKET

125 |
TANK_PACKET: = 4
126 | 131 |
132 |
133 |
134 | 261 |
262 |
263 |
264 |
265 |

Legend

266 |
267 |
    268 |
  • Constructor
  • 269 |
  • Property
  • 270 |
  • Method
  • 271 |
272 |
    273 |
  • Private property
  • 274 |
  • Private method
  • 275 |
276 |
    277 |
  • Property
  • 278 |
279 |
    280 |
  • Static method
  • 281 |
282 |
283 |
284 |
285 |
286 |

Generated using TypeDoc

287 |
288 |
289 | 290 | 291 | -------------------------------------------------------------------------------- /docs/interfaces/itemsdat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ItemsDat | pogtopia 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

Interface ItemsDat

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Items Dat buffers

71 |
72 |
73 |
74 |
75 |

Hierarchy

76 |
    77 |
  • 78 | ItemsDat 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Properties

88 | 94 |
95 |
96 |
97 |
98 |
99 |

Properties

100 |
101 | 102 |

content

103 |
content: Buffer
104 | 109 |
110 |
111 |

The contents of the items.dat file

112 |
113 |
114 |
115 |
116 | 117 |

hash

118 |
hash: number
119 | 124 |
125 |
126 |

The hash of the items.dat file

127 |
128 |
129 |
130 |
131 | 132 |

Optional meta

133 | 134 | 139 |
140 |
141 |

Metadata for the items.dat

142 |
143 |
144 |
145 |
146 | 147 |

packet

148 |
packet: TankPacket
149 | 154 |
155 |
156 |

The TankPacket to send when requesting refresh_item_data

157 |
158 |
159 |
160 |
161 |
162 | 289 |
290 |
291 |
292 |
293 |

Legend

294 |
295 |
    296 |
  • Constructor
  • 297 |
  • Property
  • 298 |
  • Method
  • 299 |
300 |
    301 |
  • Private property
  • 302 |
  • Private method
  • 303 |
304 |
    305 |
  • Property
  • 306 |
307 |
    308 |
  • Static method
  • 309 |
310 |
311 |
312 |
313 |
314 |

Generated using TypeDoc

315 |
316 |
317 | 318 | 319 | --------------------------------------------------------------------------------