From 59c2bc07670ac6453e0c785c1c70165fe283a988 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 26 Aug 2016 15:38:13 +0200 Subject: [PATCH 1/6] Initial commit --- .gitignore | 5 +- .help | 16 +- install-windows.bat | 2 + package.json | 13 +- run-windows.bat | 2 +- src/cycle.js | 21 +- src/db/create.js | 4 +- src/db/get.js | 37 +- src/db/index.js | 110 +-- src/db/tables/candy.table | 8 - src/db/tables/forts.table | 8 - src/db/tables/users.table | 40 +- src/dump.js | 75 ++ src/http.js | 39 ++ src/index.js | 205 ++---- src/master.js | 6 + src/models/GameMaster/index.js | 63 ++ src/models/Player/Avatar/index.js | 123 ++++ src/models/Player/Bag/CandyBag/index.js | 71 ++ src/models/Player/Bag/index.js | 72 ++ src/models/Player/Info/index.js | 91 +++ src/models/Player/Party/index.js | 65 ++ src/models/Player/index.js | 33 + .../{ => Player/packets}/GetInventory.js | 0 src/models/Player/packets/GetPlayer.js | 7 + src/models/Player/packets/GetPlayerProfile.js | 7 + src/models/Pokemon/WildPokemon.js | 28 + src/models/Pokemon/index.js | 127 ++++ src/models/World/MapObject/index.js | 17 + src/models/World/index.js | 124 ++++ .../{ => World/packets}/GetMapObject.js | 0 src/models/index.js | 14 - src/modes/default_player.json | 9 - src/modes/global_settings.json | 28 - src/packets/Data.DownloadUrlEntry.js | 18 - src/packets/Envelopes.AuthTicket.js | 19 - .../Envelopes.ResponseEnvelope.Auth.js | 22 - src/packets/Envelopes.ResponseEnvelope.js | 23 - src/packets/Responses.CatchPokemon.js | 52 -- src/packets/Responses.CheckAwardedBadges.js | 21 - src/packets/Responses.CheckChallenge.js | 17 - src/packets/Responses.ClaimCodeName.js | 25 - .../Responses.DownloadRemoteConfigVersion.js | 19 - src/packets/Responses.DownloadSettings.js | 44 -- src/packets/Responses.Encounter.js | 46 -- src/packets/Responses.EvolvePokemon.js | 22 - src/packets/Responses.FortDetails.js | 27 - src/packets/Responses.FortSearch.js | 42 -- src/packets/Responses.GetAssetDigest.js | 12 - src/packets/Responses.GetDownloadUrls.js | 42 -- src/packets/Responses.GetHatchedEggs.js | 21 - src/packets/Responses.GetInventory.js | 112 --- src/packets/Responses.GetMapObjects.js | 195 ------ src/packets/Responses.GetPlayer.js | 101 --- src/packets/Responses.GetPlayerProfile.js | 168 ----- src/packets/Responses.ItemTemplates.js | 13 - src/packets/Responses.LevelUpRewards.js | 21 - src/packets/Responses.MarkTutorialComplete.js | 25 - src/packets/Responses.NicknamePokemon.js | 21 - src/packets/Responses.SetAvatar.js | 23 - src/packets/Responses.SetContactSettings.js | 20 - src/packets/Responses.SetFavoritePokemon.js | 22 - src/packets/Responses.SfidaActionLog.js | 650 ------------------ src/packets/Responses.ShopData.js | 71 -- src/packets/Responses.UpgradePokemon.js | 20 - src/packets/index.js | 42 -- src/player.js | 556 --------------- src/process.js | 11 +- src/proto.js | 3 - src/request.js | 89 +-- src/response.js | 256 +------ src/setup.js | 14 +- src/utils.js | 31 +- update.js | 84 --- 74 files changed, 1100 insertions(+), 3390 deletions(-) create mode 100644 install-windows.bat delete mode 100644 src/db/tables/candy.table delete mode 100644 src/db/tables/forts.table create mode 100644 src/dump.js create mode 100644 src/http.js create mode 100644 src/master.js create mode 100644 src/models/GameMaster/index.js create mode 100644 src/models/Player/Avatar/index.js create mode 100644 src/models/Player/Bag/CandyBag/index.js create mode 100644 src/models/Player/Bag/index.js create mode 100644 src/models/Player/Info/index.js create mode 100644 src/models/Player/Party/index.js create mode 100644 src/models/Player/index.js rename src/models/{ => Player/packets}/GetInventory.js (100%) create mode 100644 src/models/Player/packets/GetPlayer.js create mode 100644 src/models/Player/packets/GetPlayerProfile.js create mode 100644 src/models/Pokemon/WildPokemon.js create mode 100644 src/models/Pokemon/index.js create mode 100644 src/models/World/MapObject/index.js create mode 100644 src/models/World/index.js rename src/models/{ => World/packets}/GetMapObject.js (100%) delete mode 100644 src/models/index.js delete mode 100644 src/modes/default_player.json delete mode 100644 src/modes/global_settings.json delete mode 100644 src/packets/Data.DownloadUrlEntry.js delete mode 100644 src/packets/Envelopes.AuthTicket.js delete mode 100644 src/packets/Envelopes.ResponseEnvelope.Auth.js delete mode 100644 src/packets/Envelopes.ResponseEnvelope.js delete mode 100644 src/packets/Responses.CatchPokemon.js delete mode 100644 src/packets/Responses.CheckAwardedBadges.js delete mode 100644 src/packets/Responses.CheckChallenge.js delete mode 100644 src/packets/Responses.ClaimCodeName.js delete mode 100644 src/packets/Responses.DownloadRemoteConfigVersion.js delete mode 100644 src/packets/Responses.DownloadSettings.js delete mode 100644 src/packets/Responses.Encounter.js delete mode 100644 src/packets/Responses.EvolvePokemon.js delete mode 100644 src/packets/Responses.FortDetails.js delete mode 100644 src/packets/Responses.FortSearch.js delete mode 100644 src/packets/Responses.GetAssetDigest.js delete mode 100644 src/packets/Responses.GetDownloadUrls.js delete mode 100644 src/packets/Responses.GetHatchedEggs.js delete mode 100644 src/packets/Responses.GetInventory.js delete mode 100644 src/packets/Responses.GetMapObjects.js delete mode 100644 src/packets/Responses.GetPlayer.js delete mode 100644 src/packets/Responses.GetPlayerProfile.js delete mode 100644 src/packets/Responses.ItemTemplates.js delete mode 100644 src/packets/Responses.LevelUpRewards.js delete mode 100644 src/packets/Responses.MarkTutorialComplete.js delete mode 100644 src/packets/Responses.NicknamePokemon.js delete mode 100644 src/packets/Responses.SetAvatar.js delete mode 100644 src/packets/Responses.SetContactSettings.js delete mode 100644 src/packets/Responses.SetFavoritePokemon.js delete mode 100644 src/packets/Responses.SfidaActionLog.js delete mode 100644 src/packets/Responses.ShopData.js delete mode 100644 src/packets/Responses.UpgradePokemon.js delete mode 100644 src/packets/index.js delete mode 100644 src/player.js delete mode 100644 src/proto.js delete mode 100644 update.js diff --git a/.gitignore b/.gitignore index 82e2f31..e014de4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ node_modules/ scripts/ -logs/ -data/ -cfg.js -npm-debug.* +data/ \ No newline at end of file diff --git a/.help b/.help index 89c535e..ed5ed37 100644 --- a/.help +++ b/.help @@ -1,8 +1,8 @@ -clients : How many players are connected -exit : Exit the server -update : Update the server -kick [PlayerName] : Kick player by username -kickall : Kick all players -clear : Clear the server console -save : Save all players into database -spawn [PlayerName] [PokemonName] [Amount] : Spawn pokemons at specific player +clients : How many players are connected +exit : Exit the server +update : Update the server +kick [Username] : Kick player by username +kickall : Kick all players +clear : Clear the server console +save : Save all players into database +spawn [Username] [Pkmn] [Amount] : Spawn pokemons at users position \ No newline at end of file diff --git a/install-windows.bat b/install-windows.bat new file mode 100644 index 0000000..b69e8ac --- /dev/null +++ b/install-windows.bat @@ -0,0 +1,2 @@ +set LIBPROTOBUF=%CD%\protobuf +npm install node-protobuf && npm install \ No newline at end of file diff --git a/package.json b/package.json index cc21082..d089a87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "POGOServer", - "version": "0.4.9", + "version": "0.5.0", "description": "", "repository": { "type": "git", @@ -9,7 +9,7 @@ "scripts": { "test": "echo \"Error: no test specified\"", "babel-node": "babel-node --presets=es2015", - "start": "nodemon --ignore tmp/ --ignore data/ --ignore logs/ --exec npm run babel-node -- ./src/index.js" + "start": "npm run babel-node -- ./src/index.js" }, "engines": { "node": ">= 6.x", @@ -21,17 +21,10 @@ "babel-cli": "^6.11.4", "babel-preset-es2015": "^6.13.1", "babel-preset-stage-0": "^6.5.0", - "nodemon": "^1.7.1", - "mongodb": "^2.2.5", + "fs-extra": "^0.30.0", "mysql": "^2.11.1", "jwt-decode": "^2.1.0", - "long": "^3.2.0", "pogo-asset-downloader": "^0.3.1", - "node-pogo-protos": "^1.3.0", - "path": "^0.12.7", - "directory-tree": "^1.1.0", - "nodegit": "^0.14.1", - "fs-extra": "^0.30.0", "pokemongo-protobuf": "^1.11.0", "pcrypt": "git+https://github.com/laverdet/pcrypt.git" }, diff --git a/run-windows.bat b/run-windows.bat index cb58fe4..0c6c316 100644 --- a/run-windows.bat +++ b/run-windows.bat @@ -1,2 +1,2 @@ -npm install && npm run start +npm run start pause \ No newline at end of file diff --git a/src/cycle.js b/src/cycle.js index 0fcf96e..abdc078 100644 --- a/src/cycle.js +++ b/src/cycle.js @@ -20,8 +20,6 @@ export function cycle() { if (this.passedTicks <= 0) return void 0; - this.updatePlayers(); - this.resetTimers(); return void 0; @@ -49,7 +47,7 @@ export function resetTimers() { this.saveTick++; // Save interval if (this.saveTick >= CFG.SAVE_INTERVAL) { - this.saveAllPlayers(); + //this.saveAllPlayers(); this.saveTick = 0; } return void 0; @@ -57,17 +55,20 @@ export function resetTimers() { export function playerTimeoutTick() { - let client = null; + let player = null; let maxTimeout = CFG.PLAYER_CONNECTION_TIMEOUT; - let ii = 0, length = this.clients.length; + let players = this.world.players; + + let ii = 0; + let length = players.length; for (; ii < length; ++ii) { - client = this.clients[ii]; - if (this.time - client.timeout >= maxTimeout) { - this.print(`${client.remoteAddress} timed out`, 34); - this.savePlayer(client); - this.removePlayer(client); + player = players[ii]; + if (this.time - player.timeout >= maxTimeout) { + this.print(`${player.remoteAddress} timed out`, 34); + this.savePlayer(player); + this.removePlayer(player); } }; diff --git a/src/db/create.js b/src/db/create.js index 545d0d8..dd2ae45 100644 --- a/src/db/create.js +++ b/src/db/create.js @@ -1,7 +1,7 @@ import fs from "fs"; import CFG from "../../cfg"; -export function createTableIfNoExists(name) { +export function createTableIfNotExists(name) { return new Promise((resolve) => { this.db.instance.query(`SHOW TABLES LIKE '${name}';`, (e, rows, fields) => { if (e) console.log(e); @@ -33,7 +33,7 @@ export function createTable(name) { this.print(`Creating table ${name}`, 36); let query = ` - CREATE TABLE ${name} ( + CREATE TABLE IF NOT EXISTS ${name} ( ${fs.readFileSync(__dirname + "/tables/" + name + ".table", "utf8")} ) ENGINE=InnoDB; `; diff --git a/src/db/get.js b/src/db/get.js index 2a4f920..3d9b9af 100644 --- a/src/db/get.js +++ b/src/db/get.js @@ -27,39 +27,4 @@ export function deleteQueryByColumnFromTable(column, value, table) { else resolve(void 0); }); }); -} - -/** - * @param {String} column - * @param {String} value - */ -export function getPkmnByColumn(column, value) { - return new Promise((resolve) => { - this.getQueryByColumnFromTable(column, value, CFG.MYSQL_OWNED_PKMN_TABLE).then((query) => { - resolve(query || []); - }); - }); -} - -/** - * @param {String} column - * @param {String} value - */ -export function getUserByColumn(column, value) { - return new Promise((resolve) => { - this.getQueryByColumnFromTable(column, value, CFG.MYSQL_USERS_TABLE).then((query) => { - resolve(query); - }); - }); -} - -/** - * @param {String} email - */ -export function getUserByEmail(email) { - return new Promise((resolve) => { - this.getQueryByColumnFromTable("email", email, CFG.MYSQL_USERS_TABLE).then((query) => { - resolve(query); - }); - }); -} +} \ No newline at end of file diff --git a/src/db/index.js b/src/db/index.js index 67d8516..2e6f41b 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -1,9 +1,8 @@ -import fs from "fs"; import mysql from "mysql"; import CFG from "../../cfg"; -export function setupConnection() { +export function setupDatabaseConnection() { let connection = mysql.createConnection({ host : CFG.MYSQL_HOST_IP, @@ -17,12 +16,12 @@ export function setupConnection() { connection.connect((error) => { if (error) { this.print("MySQL " + error, 31); - this.retry("Retrying again in ", () => this.setupConnection().then(resolve), 5); + this.retry("Retrying again in ", () => this.setupDatabaseConnection().then(resolve), 5); return void 0; } this.db.instance = connection; - this.createTableIfNoExists(CFG.MYSQL_USERS_TABLE).then(() => { - this.createTableIfNoExists(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => { + this.createTableIfNotExists(CFG.MYSQL_USERS_TABLE).then(() => { + this.createTableIfNotExists(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => { this.print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`); resolve(); }); @@ -30,7 +29,7 @@ export function setupConnection() { }); connection.on("error", (error) => { this.print("MySQL " + error, 31); - this.retry("Trying to reconnect in ", () => this.setupConnection().then(resolve), 5); + this.retry("Trying to reconnect in ", () => this.setupDatabaseConnection().then(resolve), 5); }); }); @@ -43,103 +42,4 @@ export function closeConnection(resolve) { this.db.instance.end(() => { resolve(); }); -} - -/** - * @param {Object} obj - */ -export function createUser(obj) { - - let query = this.getUserQuery("INSERT INTO", ""); - let data = this.getUserQueryData(obj); - - return new Promise((resolve) => { - this.db.instance.query(query, data, (e) => { - if (e) this.print(e, 31); - else resolve(); - }); - }); - -} - -/** - * @param {Object} obj - */ -export function createOwnedPokemon(obj) { - - let query = this.getOwnedPkmnQuery("INSERT INTO", ""); - let data = this.getOwnedPkmnQueryData(obj); - - return new Promise((resolve) => { - this.db.instance.query(query, data, resolve); - }); - -} - -/** - * @param {Object} obj - */ -export function deleteOwnedPokemon(id) { - - return new Promise((resolve) => { - this.deleteQueryByColumnFromTable("id", id, CFG.MYSQL_OWNED_PKMN_TABLE).then(() => { - resolve(); - }); - }); - -} - -/** - * @param {Player} player - */ -export function updateUser(player) { - - let query = this.getUserQuery("UPDATE", "WHERE email=? LIMIT 1"); - let data = this.getUserQueryData(player); - - return new Promise((resolve) => { - this.db.instance.query(query, data, resolve); - }); - -} - -/** - * @param {Player} player - */ -export function updateUserItems(player) { - - let query = this.getUserItemQuery("UPDATE", "WHERE email=? LIMIT 1"); - let data = this.getUserItemQueryData(player); - - return new Promise((resolve) => { - this.db.instance.query(query, data, resolve); - }); - -} - -/** - * @param {Player} player - */ -export function updateUserParty(player) { - - let pkmn = null; - let data = null; - let query = this.getOwnedPkmnQuery("UPDATE", "WHERE id=? AND owner_id=? LIMIT 1"); - - return new Promise((resolve) => { - let ii = 0; - let index = 0; - let length = player.party.length; - // dont go on if party empty - if (!length) return resolve(); - for (; ii < length; ++ii) { - pkmn = player.party[ii]; - data = this.getOwnedPkmnQueryData(pkmn); - data.push(pkmn.id, player.owner_id); - this.db.instance.query(query, data, () => { - if (++index >= length) resolve(); - }); - }; - }); - } \ No newline at end of file diff --git a/src/db/tables/candy.table b/src/db/tables/candy.table deleted file mode 100644 index ece6f75..0000000 --- a/src/db/tables/candy.table +++ /dev/null @@ -1,8 +0,0 @@ -id int(15) NOT NULL AUTO_INCREMENT, -fort_id varchar(42) NOT NULL, -latitude double NOT NULL, -longitude double NOT NULL, -enabled tinyint(1) NOT NULL, -items_awarded varchar(42) NOT NULL, -experience_awarded int(10) NOT NULL -PRIMARY KEY (id) \ No newline at end of file diff --git a/src/db/tables/forts.table b/src/db/tables/forts.table deleted file mode 100644 index ece6f75..0000000 --- a/src/db/tables/forts.table +++ /dev/null @@ -1,8 +0,0 @@ -id int(15) NOT NULL AUTO_INCREMENT, -fort_id varchar(42) NOT NULL, -latitude double NOT NULL, -longitude double NOT NULL, -enabled tinyint(1) NOT NULL, -items_awarded varchar(42) NOT NULL, -experience_awarded int(10) NOT NULL -PRIMARY KEY (id) \ No newline at end of file diff --git a/src/db/tables/users.table b/src/db/tables/users.table index 1e6a398..b777823 100644 --- a/src/db/tables/users.table +++ b/src/db/tables/users.table @@ -1,25 +1,25 @@ id int(11) NOT NULL AUTO_INCREMENT, -username longtext NOT NULL, -email longtext NOT NULL, -exp int(255) NOT NULL, -level int(11) NOT NULL, -stardust int(255) NOT NULL, -pokecoins int(255) NOT NULL, -team int(11) NOT NULL, +username varchar(16) NOT NULL, +email varchar(32) NOT NULL, +exp int(11) NOT NULL, +level smallint(11) NOT NULL, +stardust int(11) NOT NULL, +pokecoins int(11) NOT NULL, +team tinyint(11) NOT NULL, latitude double NOT NULL, longitude double NOT NULL, -altitude int(255) NOT NULL, +altitude double NOT NULL, send_marketing_emails tinyint(1) NOT NULL, send_push_notifications tinyint(1) NOT NULL, -skin int(11) NOT NULL, -hair int(11) NOT NULL, -shirt int(11) NOT NULL, -pants int(11) NOT NULL, -hat int(11) NOT NULL, -shoes int(11) NOT NULL, -eyes int(11) NOT NULL, -gender int(11) NOT NULL, -backpack int(11) NOT NULL, +avatar_skin tinyint(1) NOT NULL, +avatar_hair tinyint(1) NOT NULL, +avatar_shirt tinyint(1) NOT NULL, +avatar_pants tinyint(1) NOT NULL, +avatar_hat tinyint(1) NOT NULL, +avatar_shoes tinyint(1) NOT NULL, +avatar_eyes tinyint(1) NOT NULL, +avatar_gender tinyint(1) NOT NULL, +avatar_backpack tinyint(1) NOT NULL, item_poke_ball int(11) NOT NULL, item_great_ball int(11) NOT NULL, item_ultra_ball int(11) NOT NULL, @@ -45,4 +45,10 @@ item_incubator_basic int(11) NOT NULL, item_incubator_basic_unlimited int(11) NOT NULL, item_pokemon_storage_upgrade int(11) NOT NULL, item_storage_upgrade int(11) NOT NULL, +tutorial_legal_screen tinyint(1) NOT NULL, +tutorial_avatar_selection tinyint(1) NOT NULL, +tutorial_pokemon_capture tinyint(1) NOT NULL, +tutorial_name_selection tinyint(1) NOT NULL, +tutorial_first_time_exp tinyint(1) NOT NULL, +candies text(11) NOT NULL, PRIMARY KEY (id) \ No newline at end of file diff --git a/src/dump.js b/src/dump.js new file mode 100644 index 0000000..9f85978 --- /dev/null +++ b/src/dump.js @@ -0,0 +1,75 @@ +import fse from "fs-extra"; +import POGOProtos from "pokemongo-protobuf"; + +import CFG from "../cfg"; + +/** + * @param {Request} req + * @param {Array} res + * @return {Object} + */ +export function decode(req, res) { + + // clone + req = JSON.parse(JSON.stringify(req)); + res = JSON.parse(JSON.stringify(res)); + + // dont decode unknown6, since it bloats the file size + delete req.unknown6; + + // decode requests + for (let request of req.requests) { + let key = _toCC(request.request_type); + let msg = request.request_message; + if (msg) { + let proto = `POGOProtos.Networking.Requests.Messages.${key}Message`; + request.request_message = this.parseProtobuf(new Buffer(msg.data), proto); + } + }; + + // decode responses + let index = 0; + for (let resp of res) { + let key = _toCC(req.requests[index].request_type); + let msg = new Buffer(resp); + let proto = `POGOProtos.Networking.Responses.${key}Response`; + res[index] = this.parseProtobuf(msg, proto); + index++; + }; + + // clone again to build response out of it + let req2 = JSON.parse(JSON.stringify(req)); + + // build res base out of req + delete req2.requests; + req2.returns = res; + req2.status_code = 1; + + return ({ + req: req, + res: res + }); + +} + +/** + * @param {Request} req + * @param {Response} res + */ +export function dumpTraffic(req, res) { + + let decoded = this.decode(req, res); + + let out = { + Request: decoded.req, + Response: decoded.res + }; + + try { + let decoded = JSON.stringify(out, null, 2); + fse.outputFileSync(CFG.DEBUG_DUMP_PATH + Date.now(), decoded); + } catch (e) { + this.print("Dump traffic: " + e, 31); + } + +} \ No newline at end of file diff --git a/src/http.js b/src/http.js new file mode 100644 index 0000000..4eede73 --- /dev/null +++ b/src/http.js @@ -0,0 +1,39 @@ +import http from "http"; + +import CFG from "../cfg"; + +/** + * @return {HTTP} + */ +export function createHTTPServer() { + let server = http.createServer((req, res) => { + if (this.world.isFull()) { + this.print(`Server is full! Refused ${req.headers.host}`, 31); + return void 0; + } + let chunks = []; + req.on("data", (chunk) => { + chunks.push(chunk); + }); + req.on("end", () => { + let buffer = Buffer.concat(chunks); + req.body = buffer; + this.world.getPlayerByRequest(req, res).then((player) => { + this.routeRequest(player, req, res); + }); + }); + }); + server.listen(CFG.PORT); + return (server); +} + +export function shutdown() { + this.socket.close(() => { + this.print("Closed http server!", 33); + this.closeConnection(() => { + this.print("Closed database connection!", 33); + this.print("Server shutdown!", 31); + setTimeout(() => process.exit(1), 2e3); + }); + }); +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 72c478c..4226ca2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,7 @@ import fs from "fs"; import os from "os"; -import fse from "fs-extra"; -import http from "http"; -import proto from "./proto"; import EventEmitter from "events"; +import POGOProtos from "pokemongo-protobuf"; import { inherit, @@ -12,10 +10,13 @@ import { import CFG from "../cfg"; +import World from "./models/World"; + import * as _api from "./api"; +import * as _dump from "./dump"; +import * as _http from "./http"; import * as _setup from "./setup"; import * as _cycle from "./cycle"; -import * as _player from "./player"; import * as _request from "./request"; import * as _response from "./response"; import * as _process from "./process"; @@ -60,14 +61,13 @@ export default class GameServer extends EventEmitter { this.timeoutTick = 0; this.passedTicks = 0; - this.clients = []; - this.wild_pokemons = []; - this.initAPI(); if (CFG.GREET) this.greet(); - this.print(`Booting Server v${require("../package.json").version}...`, 33); + this.print(`Booting Server v${require("../package.json").version}-dev`, 33); + + this.world = new World(this); this.setup(); @@ -85,84 +85,6 @@ export default class GameServer extends EventEmitter { } } - clientAlreadyConnected(client) { - - let remoteAddress = client.headers.host; - - let ii = 0, length = this.clients.length; - - for (; ii < length; ++ii) { - if (this.clients[ii].remoteAddress === remoteAddress) { - return (true); - } - }; - - return (false); - - } - - /** - * @return {HTTP} - */ - createHTTPServer() { - let server = http.createServer((req, res) => { - if (this.clients.length >= CFG.MAX_CONNECTIONS) { - this.print(`Server is full! Refused ${req.headers.host}`, 31); - return void 0; - } - - let player = null; - - if (this.clientAlreadyConnected(req)) player = this.getPlayerByRequest(req); - else player = this.addPlayer(req, res); - - let chunks = []; - req.on("data", (chunk) => { - chunks.push(chunk); - }); - req.on("end", () => { - let buffer = Buffer.concat(chunks); - req.body = buffer; - player.updateResponse(res); - this.routeRequest(req, res); - }); - }); - server.listen(CFG.PORT); - return (server); - } - - setupDatabaseConnection() { - - return new Promise((resolve) => { - - let name = String(CFG.DATABASE_TYPE).toUpperCase(); - - switch (name) { - case "MYSQL": - inherit(GameServer, _mysql); - this.setupConnection().then(resolve); - break; - default: - this.print("Invalid database connection type!", 31); - return void 0; - break; - }; - - }); - - } - - shutdown() { - this.socket.close(() => { - this.print("Closed http server!", 33); - this.closeConnection(() => { - this.print("Closed database connection!", 33); - this.print("Server shutdown!", 31); - setTimeout(() => process.exit(1), 2e3); - }); - }); - } - /** * @param {String} msg * @param {Number} color @@ -174,87 +96,23 @@ export default class GameServer extends EventEmitter { /** * @param {String} msg - * @param {Function} func + * @param {Function} fn * @param {Number} timer */ - retry(msg, func, timer) { + retry(msg, fn, timer) { process.stdout.clearLine(); process.stdout.cursorTo(0); this.print(`${msg}${timer}s`, 33, true); - if (timer >= 1) setTimeout(() => this.retry(msg, func, --timer), 1e3); + if (timer >= 1) setTimeout(() => this.retry(msg, fn, --timer), 1e3); else { process.stdout.write("\n"); - func(); + fn(); } } /** - * @param {Request} req - * @param {Array} res - * @return {Object} + * @return {String} */ - decode(req, res) { - - // clone - req = JSON.parse(JSON.stringify(req)); - res = JSON.parse(JSON.stringify(res)); - - // dont decode unknown6, since it bloats the file size - delete req.unknown6; - - // decode requests - for (let request of req.requests) { - let key = _toCC(request.request_type); - let msg = request.request_message; - if (msg) { - let proto = `POGOProtos.Networking.Requests.Messages.${key}Message`; - request.request_message = this.parseProtobuf(new Buffer(msg.data), proto); - } - }; - - // decode responses - let index = 0; - for (let resp of res) { - let key = _toCC(req.requests[index].request_type); - let msg = new Buffer(resp); - let proto = `POGOProtos.Networking.Responses.${key}Response`; - res[index] = this.parseProtobuf(msg, proto); - index++; - }; - - // clone again to build response out of it - let req2 = JSON.parse(JSON.stringify(req)); - - // build res base out of req - delete req2.requests; - req2.returns = res; - req2.status_code = 1; - - return ({ - req: req, - res: res - }); - - } - - dumpTraffic(req, res) { - - let decoded = this.decode(req, res); - - let out = { - Request: decoded.req, - Response: decoded.res - }; - - try { - let decoded = JSON.stringify(out, null, 2); - fse.outputFileSync(CFG.DEBUG_DUMP_PATH + Date.now(), decoded); - } catch (e) { - this.print("Dump traffic: " + e, 31); - } - - } - getLocalIPv4() { let address = null; let interfaces = os.networkInterfaces(); @@ -264,15 +122,32 @@ export default class GameServer extends EventEmitter { return (address); } - directoryExists(directory) { + /** + * @param {Buffer} buffer + * @param {String} schema + */ + parseProtobuf(buffer, schema) { try { - fs.statSync(directory); - return true; - } catch(e) { - return false; + return POGOProtos.parseWithUnknown(buffer, schema); + } catch (e) { + this.print(e, 31); } } + /** + * @param {Request} req + */ + parseSignature(req) { + let key = pcrypt.decrypt(req.unknown6.unknown2.encrypted_signature); + return ( + POGOProtos.parseWithUnknown(key, "POGOProtos.Networking.Envelopes.Signature") + ); + } + + /** + * @param {String} path + * @return {Boolean} + */ fileExists(path) { try { fs.statSync(path); @@ -288,9 +163,10 @@ export default class GameServer extends EventEmitter { } +inherit(GameServer, _dump); +inherit(GameServer, _http); inherit(GameServer, _setup); inherit(GameServer, _cycle); -inherit(GameServer, _player); inherit(GameServer, _request); inherit(GameServer, _response); inherit(GameServer, _process); @@ -299,10 +175,9 @@ inherit(GameServer, _mysql_get); inherit(GameServer, _mysql_query); inherit(GameServer, _mysql_create); +(() => { -((Server) => { - - const server = new Server(); + const server = new GameServer(); process.openStdin().addListener("data", (data) => { server.stdinInput(data); @@ -312,4 +187,4 @@ inherit(GameServer, _mysql_create); server.uncaughtException(data); }); -})(GameServer); \ No newline at end of file +})(); \ No newline at end of file diff --git a/src/master.js b/src/master.js new file mode 100644 index 0000000..f80580f --- /dev/null +++ b/src/master.js @@ -0,0 +1,6 @@ +/** + * Global shared parsed + * game master file + * @type {GameMaster} + */ +export let GAME_MASTER = null; \ No newline at end of file diff --git a/src/models/GameMaster/index.js b/src/models/GameMaster/index.js new file mode 100644 index 0000000..2229041 --- /dev/null +++ b/src/models/GameMaster/index.js @@ -0,0 +1,63 @@ +/** + * @class GameMaster + */ +export default class GameMaster { + + /** + * @param {Buffer} buffer + * @constructor + */ + constructor(buffer) { + + this.settings = this.buildSettings(); + + this.buffer = buffer; + + this.parse(); + + } + + parse() { + + let ii = 0; + let length = 0; + + let item = null; + let items = this.buffer.item_templates; + + length = items.length; + + for (; ii < length; ++ii) { + item = items[ii]; + this.parseKey(item, item.template_id); + }; + + } + + /** + * @param {Object} item + * @param {String} key + */ + parseKey(item, key) { + if (key in this.settings) { + this.settings[key] = item; + } + } + + /** + * @return {Object} + */ + buildSettings() { + let settings = { + "PLAYER_LEVEL_SETTINGS": null + }; + return (settings); + } + + getPlayerSettings() { + return ( + this.settings["PLAYER_LEVEL_SETTINGS"].player_level + ); + } + +} \ No newline at end of file diff --git a/src/models/Player/Avatar/index.js b/src/models/Player/Avatar/index.js new file mode 100644 index 0000000..034b501 --- /dev/null +++ b/src/models/Player/Avatar/index.js @@ -0,0 +1,123 @@ +/** + * @class Avatar + */ +export default class Avatar { + + /** @constructor */ + constructor() { + + this._skin = 0; + this._hair = 0; + this._shirt = 0; + this._pants = 0; + this._hat = 0; + this._shoes = 0; + this._eyes = 0; + this._gender = 0; + this._backpack = 0; + + } + + /** + * @param {Number} value + * @param {Number} a + * @param {Number} b + * @return {Boolean} + */ + between(value, a, b) { + return ( + value >= a && value <= b + ); + } + + // skin + get skin() { + return (this._skin); + } + set skin(value) { + if (this.between(value, 0, 3)) { + this.skin = value; + } + } + + // hair + get hair() { + return (this._hair); + } + set hair(value) { + if (this.between(value, 0, 5)) { + this._hair = value; + } + } + + // shirt + get shirt() { + return (this._shirt); + } + set shirt(value) { + if (this.between(value, 0, 3)) { + this._shirt = value; + } + } + + // pants + get pants() { + return (this._pants); + } + set pants(value) { + if (this.between(value, 0, 2)) { + this._pants = value; + } + } + + // hat + get hat() { + return (this._hat); + } + set hat(value) { + if (this.between(value, 0, 4)) { + this._hat = value; + } + } + + // shoes + get shoes() { + return (this._shoes); + } + set shoes(value) { + if (this.between(value, 0, 6)) { + this._shoes = value; + } + } + + // eyes + get eyes() { + return (this._eyes); + } + set eyes(value) { + if (this.between(value, 0, 4)) { + this._eyes = value; + } + } + + // gender + get gender() { + return (this._gender); + } + set gender(value) { + if (this.between(value, 0, 1)) { + this._gender = value; + } + } + + // backpack + get backpack() { + return (this._backpack); + } + set backpack(value) { + if (this.between(value, 0, 5)) { + this._backpack = value; + } + } + +} \ No newline at end of file diff --git a/src/models/Player/Bag/CandyBag/index.js b/src/models/Player/Bag/CandyBag/index.js new file mode 100644 index 0000000..9f095e4 --- /dev/null +++ b/src/models/Player/Bag/CandyBag/index.js @@ -0,0 +1,71 @@ +/** + * @class CandyBag + */ +export default class CandyBag { + + /** @constructor */ + constructor() { + this.candies = []; + } + + getCandyByDexNumber(dex) { + + let candies = this.candies; + + let ii = 0; + let length = candies.length; + + for (; ii < length; ++ii) { + if (candies[ii].dex === dex) { + return (candies[ii]); + } + }; + + return (null); + + } + + /** + * @param {String} str + */ + parseCandies(str) { + + let result = null; + + let seperator = ":"; + let split = str.split(","); + + let ii = 0; + let length = split.length; + + for (; ii < length; ++ii) { + result = split[ii].split(seperator); + this.candies.push({ + dex: result[0] << 0, + amount: result[1] << 0 + }); + }; + + } + + /** + * @return {String} + */ + serializeCandies() { + + let str = ""; + let candies = this.candies; + + let ii = 0; + let length = candies.length; + + for (; ii < length; ++ii) { + str += candies[ii].dex + ":" + candies[ii].dex; + if (ii + 1 < length) str += ","; + }; + + return (str); + + } + +} \ No newline at end of file diff --git a/src/models/Player/Bag/index.js b/src/models/Player/Bag/index.js new file mode 100644 index 0000000..128ead3 --- /dev/null +++ b/src/models/Player/Bag/index.js @@ -0,0 +1,72 @@ +import Candy from "./Candy"; + +/** + * @class Bag + */ +export default class Bag { + + /** @constructor */ + constructor() { + + this.candies = new CandyBag(); + + this.stardust = 0; + this.pokecoins = 0; + + this.pokeBall = 0; + this.greatBall = 0; + this.ultraBall = 0; + this.masterBall = 0; + + this.potion = 0; + this.superPotion = 0; + this.hyperPotion = 0; + this.maxPotion = 0; + + this.revive = 0; + this.maxRevive = 0; + + this.razzBerry = 0; + this.blukBerry = 0; + this.nanabBerry = 0; + this.weparBerry = 0; + this.pinapBerry = 0; + + // premium shit + this.luckyEgg = 0; + this.troyDisk = 0; + + this.incenseOrdinary = 0; + this.incenseSpicy = 0; + this.incenseCool = 0; + this.incenseFloral = 0; + + this.incubatorBasic = 0; + this.incubatorBasicUnlimited = 0; + + this.storageUpgrade = 0; + + this.pkmnStorageUpgrade = 0; + + } + + /** + * @param {Number} dex + * @return {Object} + */ + getCandy(dex) { + return ( + this.candies.getCandyByDexNumber(dex) + ); + } + + /** + * @param {Number} dex + * @param {Number} value + */ + updateCandyAmount(dex, value) { + let candy = this.getCandy(dex); + candy.amount += (value << 0); + } + +} \ No newline at end of file diff --git a/src/models/Player/Info/index.js b/src/models/Player/Info/index.js new file mode 100644 index 0000000..88ed579 --- /dev/null +++ b/src/models/Player/Info/index.js @@ -0,0 +1,91 @@ +/** + * @class Info + */ +class Info { + + /** @constructor */ + constructor() { + + this.lvl = 0; + + this.exp = 0; + this.prevLvlExp = 0; + this.nextLvlExp = 0; + + this.kmWalked = 0; + this.pkmnEncountered = 0; + this.uniquePokedexEntries = 0; + this.pkmnCaptured = 0; + this.pokeStopVisits = 0; + this.pokeballsThrown = 0; + this.eggsHatched = 0; + this.bigMagikarpCaught = 0; + this.pkmnDeployed = 0; + + } + + upgradeLevel() { + + } + + getLevelSettings() { + return ( + this.owner.gameMaster("PLAYER_LEVEL_SETTINGS") + ); + } + + getMaximumLevel() { + return ( + Object.keys(this.getLevelSettings().player_level).length + ); + } + + getCurrentLevel() { + let levels = this.getLevelSettings().required_experience; + for (let key in levels) { + if (levels[key] << 0 === this.nextLvlExp) { + return (key << 0); + } + }; + return (1); + } + + /** + * @param {Number} exp + */ + upgradeExp(exp) { + let levels = this.getLevelSettings().required_experience; + let maxLevel = this.getMaximumLevel(); + let currentLevel = this.getCurrentLevel(); + if (currentLevel + 1 <= maxLevel) { + this.lvl += 1; + this.exp += exp; + this.prevLvlExp = levels[this.lvl - 1]; + this.nextLvlExp = levels[this.lvl + 1]; + } + else { + this.exp += exp; + } + } + + getPlayerInfo() { + return ({ + player_stats: { + level: this.lvl, + experience: this.exp, + prev_level_xp: this.prevLvlExp, + next_level_xp: this.nextLvlExp, + km_walked: this.kmWalked, + pokemons_encountered: this.pkmnEncountered, + unique_pokedex_entries: this.uniquePokedexEntries, + pokemons_captured: this.pkmnCaptured, + poke_stop_visits: this.pokeStopVisits, + pokeballs_thrown: this.pokeballsThrown, + eggs_hatched: this.eggsHatched, + big_magikarp_caught: this.bigMagikarpCaught, + pokemon_deployed: this.pkmnDeployed + } + }); + } + +} \ No newline at end of file diff --git a/src/models/Player/Party/index.js b/src/models/Player/Party/index.js new file mode 100644 index 0000000..d2b3276 --- /dev/null +++ b/src/models/Player/Party/index.js @@ -0,0 +1,65 @@ +/** + * @class Party + */ +class Party { + + /** @constructor */ + constructor() { + + this.party = []; + + } + + addPkmn(obj) { + console.log(obj); + } + + /** + * @param {Number} id + * @return {Number} + */ + getPkmnIndexById(id) { + for (let ii = 0; ii < this.party.length; ++ii) { + if (this.party[ii].id === id) return (ii); + }; + return (-1); + } + + /** + * @param {Number} id + * @return {Pokemon} + */ + getPkmnById(id) { + let index = this.getPkmnIndexById(id); + return (this.party[index]); + } + + /** + * @param {Number} id + */ + deletePkmn(id) { + let index = this.getPkmnIndexById(id); + let pkmn = this.party[index]; + if (pkmn) this.party.splice(index, 1); + } + + /** + * @return {Number} + */ + getUniquePkmnCount() { + let ii = 0; + let dex = 0; + let amount = 0; + let length = this.party.length; + let array = []; + for (; ii < length; ++ii) { + dex = this.party[ii].dexNumber; + if (array.indexOf(dex) === -1) { + array.push(dex); + amount++; + } + }; + return (amount); + } + +} \ No newline at end of file diff --git a/src/models/Player/index.js b/src/models/Player/index.js new file mode 100644 index 0000000..7a98157 --- /dev/null +++ b/src/models/Player/index.js @@ -0,0 +1,33 @@ +import Avatar from "./Avatar"; +import MapObject from "../World/MapObject"; + +import { GAME_MASTER } from "../../master"; + +/** + * @class Player + */ +export default class Player extends MapObject { + + /** + * @param {Object} obj + * @constructor + */ + constructor(obj) { + + super(null); + + this.request = obj.request; + this.response = obj.response; + + this.authenticated = 0; + + this.bag = new Bag(this); + this.info = new Info(this); + this.pary = new Party(this); + this.avatar = new Avatar(this); + this.pokedex = new Pokedex(this); + this.tutorial = new Tutorial(this); + + } + +} \ No newline at end of file diff --git a/src/models/GetInventory.js b/src/models/Player/packets/GetInventory.js similarity index 100% rename from src/models/GetInventory.js rename to src/models/Player/packets/GetInventory.js diff --git a/src/models/Player/packets/GetPlayer.js b/src/models/Player/packets/GetPlayer.js new file mode 100644 index 0000000..b81b18c --- /dev/null +++ b/src/models/Player/packets/GetPlayer.js @@ -0,0 +1,7 @@ +import POGOProtos from "pokemongo-protobuf"; + +export function GetPlayer() { + + return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerResponse")); + +} \ No newline at end of file diff --git a/src/models/Player/packets/GetPlayerProfile.js b/src/models/Player/packets/GetPlayerProfile.js new file mode 100644 index 0000000..a785433 --- /dev/null +++ b/src/models/Player/packets/GetPlayerProfile.js @@ -0,0 +1,7 @@ +import POGOProtos from "pokemongo-protobuf"; + +export function GetPlayerProfile() { + + return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse")); + +} \ No newline at end of file diff --git a/src/models/Pokemon/WildPokemon.js b/src/models/Pokemon/WildPokemon.js new file mode 100644 index 0000000..9ac34c4 --- /dev/null +++ b/src/models/Pokemon/WildPokemon.js @@ -0,0 +1,28 @@ +import Pokemon from "index"; +import CaptureProbability from "CaptureProbability"; + +/** + * @class WildPokemon + */ +class WildPokemon extends Pokemon { + + /** + * @param {Object} obj + * @constructor + */ + constructor(obj) { + + super(obj); + + this.encounterId = 0; + + this.latitude = 0; + this.longitude = 0; + + this.pokeball = 0; + + this.spawnPoint = 0; + + } + +} \ No newline at end of file diff --git a/src/models/Pokemon/index.js b/src/models/Pokemon/index.js new file mode 100644 index 0000000..9013c2e --- /dev/null +++ b/src/models/Pokemon/index.js @@ -0,0 +1,127 @@ +import pokename from "pokename"; + +import MapObject from "../World/MapObject"; + +import { + validName +} from "../../utils"; + +/** + * @class Pokemon + */ +export default class Pokemon extends MapObject { + + /** + * @param {Object} obj + * @constructor + */ + constructor(obj) { + + this.id = 0; + this.dexNumber = 0; + + this.owner = null; + + this.level = 0; + + this.cp = 0; + this.cpMultiplier = 0; + this.addCpMultiplier = 0; + + this.stamina = 0; + this.staminaMax = 0; + + this.move1 = 0; + this.move2 = 0; + + this.attack = 0; + this.defence = 0; + + this.height = 0; + this.weight = 0; + + this.ivAttack = 0; + this.ivDefense = 0; + this.ivStamina = 0; + + this.nickname = null; + + this.favorite = 0; + + this.init(obj); + + } + + /** + * @param {Object} obj + */ + init(obj) { + obj = obj || {}; + for (let key in obj) { + if (this.hasOwnProperty(key)) { + this[key] = obj[key]; + } + }; + } + + /** + * @param {Boolean} truth + */ + setFavorite(truth) { + this.favorite = !!truth; + } + + /** + * @param {String} name + */ + setNickname(name) { + if (!validName(name)) return void 0; + this.nickname = name; + } + + /** + * @param {Number} dex + * @return {Object} + */ + getPkmnTemplate(dex) { + let name = pokename.getPokemonNameById(dex); + return (this.owner.gameMaster.pokemon[name]); + } + + upgradePkmn() { + let pkmnTmpl = this.getPkmnTemplate(this.dexNumber); + let ownerCandies = this.owner.bag.getCandyByDexNumber(this.dexNumber); + if (ownerCandies >= pkmnTmpl.CandyToEvolve) { + this.owner.bag.setCandyByDex(ownerCandies - pkmnTmpl.CandyToEvolve); + this.evolveTo(pkmnTmpl.Evolution); + } + } + + /** + * @param {Number} dex + */ + evolveTo(dex) { + console.log("EVOLVE TO: ", dex); + } + + /** + * @return {Object} + */ + getPokemonData() { + return ({ + pokemon_id: this.dexNumber, + cp: this.cp, + stamina: this.stamina, + stamina_max: this.stamina_max, + move_1: this.move_1, + move_2: this.move_2, + height_m: this.height, + weight_kg: this.weight, + individual_attack: this.ivAttack, + individual_defense: this.ivDefense, + individual_stamina: this.ivStamina, + cp_multiplier: this.cpMultiplier + }); + } + +} \ No newline at end of file diff --git a/src/models/World/MapObject/index.js b/src/models/World/MapObject/index.js new file mode 100644 index 0000000..b616555 --- /dev/null +++ b/src/models/World/MapObject/index.js @@ -0,0 +1,17 @@ +/** + * @class MapObject + */ +export default class MapObject { + + /** @constructor */ + constructor() { + + this.uid = 0; + + this.altitude = 0; + this.latitude = 0; + this.longitude = 0; + + } + +} \ No newline at end of file diff --git a/src/models/World/index.js b/src/models/World/index.js new file mode 100644 index 0000000..017a75a --- /dev/null +++ b/src/models/World/index.js @@ -0,0 +1,124 @@ +import Player from "../Player"; + +import CFG from "../../../cfg"; + +/** + * @class World + */ +export default class World { + + /** @constructor */ + constructor() { + + this.players = []; + + } + + /** + * @return {Boolean} + */ + isFull() { + return ( + this.players.length >= CFG.MAX_CONNECTIONS + ); + } + + /** + * @param {Request} req + * @param {Response} res + */ + getPlayerByRequest(req, res) { + + let player = null; + + return new Promise((resolve) => { + if (this.playerAlreadyConnected(req)) { + player = this.getPlayerByIP(req.headers.host); + resolve(player); + } + else { + this.addPlayer(req, res); + resolve(player); + } + }); + + } + + /** + * @param {Object} client + * @return {Boolean} + */ + playerAlreadyConnected(client) { + + let players = this.players; + + let ii = 0; + let length = players.length; + + let remoteAddress = client.headers.host; + + for (; ii < length; ++ii) { + if (players[ii].remoteAddress === remoteAddress) { + return (true); + } + }; + + return (false); + + } + + /** + * @param {String} ip + */ + getPlayerByIP(ip) { + + let players = this.players; + + let ii = 0; + let length = players.length; + + for (; ii < length; ++ii) { + if (players[ii].remoteAddress === ip) { + return (players[ii]); + } + }; + + return (null); + + } + + /** + * @param {Request} req + * @param {Response} res + */ + addPlayer(req, res) { + + let player = new Player({ + request: req, + response: res + }); + + this.players.push(player); + + } + + /** + * @param {Player} player + */ + removePlayer(player) { + console.log(player); + } + + spawnFort() { + + } + + spawnGym() { + + } + + spawnEncounter() { + + } + +} \ No newline at end of file diff --git a/src/models/GetMapObject.js b/src/models/World/packets/GetMapObject.js similarity index 100% rename from src/models/GetMapObject.js rename to src/models/World/packets/GetMapObject.js diff --git a/src/models/index.js b/src/models/index.js deleted file mode 100644 index 5cce868..0000000 --- a/src/models/index.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @class GameServer - */ -class PacketModel { - - /** - * @param {Player} player - * @constructor - */ - constructor(player) { - this.player = player; - } - -} \ No newline at end of file diff --git a/src/modes/default_player.json b/src/modes/default_player.json deleted file mode 100644 index 1f8ab38..0000000 --- a/src/modes/default_player.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "exp": 0, - "level": 5, - "stardust": 1000, - "pokecoins": 0, - "team": 0, - "items": [], - "pokemons": [] -} \ No newline at end of file diff --git a/src/modes/global_settings.json b/src/modes/global_settings.json deleted file mode 100644 index 993e9dc..0000000 --- a/src/modes/global_settings.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "fort_settings": { - "interaction_range_meters": 40, - "max_total_deployed_pokemon": 10, - "max_player_deployed_pokemon": 1, - "deploy_stamina_multiplier": 8, - "deploy_attack_multiplier": 0, - "far_interaction_range_meters": 1000 - }, - "map_settings": { - "pokemon_visible_range": 70, - "poke_nav_range_meters": 751, - "encounter_range_meters": 50, - "get_map_objects_min_refresh_seconds": 10, - "get_map_objects_max_refresh_seconds": 30, - "get_map_objects_min_distance_meters": 10 - }, - "inventory_settings": { - "max_pokemon": 1000, - "max_bag_items": 1000, - "base_pokemon": 250, - "base_bag_items": 350, - "base_eggs": 9 - }, - "pokemon_settings": { - - } -} \ No newline at end of file diff --git a/src/packets/Data.DownloadUrlEntry.js b/src/packets/Data.DownloadUrlEntry.js deleted file mode 100644 index bb602bd..0000000 --- a/src/packets/Data.DownloadUrlEntry.js +++ /dev/null @@ -1,18 +0,0 @@ -import proto from "../proto"; - -/** - * @param {Object} - * @return {Object} - */ -export default function DownloadUrlEntry(obj) { - - return ( - new proto.Data.DownloadUrlEntry({ - url: obj.url, - asset_id: obj.id, - size: obj.size, - checksum: obj.checksum - }) - ); - -} \ No newline at end of file diff --git a/src/packets/Envelopes.AuthTicket.js b/src/packets/Envelopes.AuthTicket.js deleted file mode 100644 index 27b6732..0000000 --- a/src/packets/Envelopes.AuthTicket.js +++ /dev/null @@ -1,19 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import CFG from "../../cfg"; - -/** - * @return {Object} - */ -export default function AuthTicket() { - - let buffer = ({ - start: new Buffer(""), - "expire_timestamp_ms": new Date().getTime() + CFG.PLAYER_CONNECTION_TIMEOUT, - end: new Buffer("") - }); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.AuthTicket")); - -} \ No newline at end of file diff --git a/src/packets/Envelopes.ResponseEnvelope.Auth.js b/src/packets/Envelopes.ResponseEnvelope.Auth.js deleted file mode 100644 index 61d1a82..0000000 --- a/src/packets/Envelopes.ResponseEnvelope.Auth.js +++ /dev/null @@ -1,22 +0,0 @@ -import proto from "../proto"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function ResponseEnvelope(obj) { - - return ( - new proto.Networking.Envelopes.ResponseEnvelope({ - status_code: 53, - request_id: obj.id, - api_url: "pgorelease.nianticlabs.com/custom", - auth_ticket: new proto.Networking.Envelopes.AuthTicket({ - start: new Buffer(""), - expire_timestamp_ms: 9999999999999, - end: new Buffer("") - }) - }).encode().toBuffer() - ); - -} \ No newline at end of file diff --git a/src/packets/Envelopes.ResponseEnvelope.js b/src/packets/Envelopes.ResponseEnvelope.js deleted file mode 100644 index b39c98d..0000000 --- a/src/packets/Envelopes.ResponseEnvelope.js +++ /dev/null @@ -1,23 +0,0 @@ -import proto from "../proto"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function ResponseEnvelope(obj) { - - return ( - new proto.Networking.Envelopes.ResponseEnvelope({ - status_code: obj.status, - unknown6: new proto.Networking.Envelopes.Unknown6Response({ - response_type: 6, - unknown2: new proto.Networking.Envelopes.Unknown6Response.Unknown2({ - unknown1: 1 - }) - }), - request_id: obj.id, - returns: obj.response - }).encode().toBuffer() - ); - -} \ No newline at end of file diff --git a/src/packets/Responses.CatchPokemon.js b/src/packets/Responses.CatchPokemon.js deleted file mode 100644 index 5c5caab..0000000 --- a/src/packets/Responses.CatchPokemon.js +++ /dev/null @@ -1,52 +0,0 @@ -import CFG from "../../cfg"; - -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function CatchPokemon(encounter, player, req) { - - let obj = { - "owner_id": -1, - "encounter_id": encounter.encounter_id, - "spawn_point_id": "87bdcd88cb5", - "captured_cell_id": "9781199952939057152", - pokemon_id: encounter.pokemon_id, - cp: encounter.cp, - "move_1": "BUG_BITE_FAST", - "move_2": "STRUGGLE", - "stamina": 10, - "stamina_max": 10, - height_m: 0.30962005257606506, - weight_kg: 3.3212273120880127, - "pokeball": "ITEM_POKE_BALL", - "creation_time_ms": new Date().getTime(), - "cp_multiplier": 0.16639786958694458, - individual_attack: 7, - individual_defense: 13, - individual_stamina: 3 - }; - - if (!req.hit_pokemon) { - return ({ - status: 'CATCH_MISSED' - }); - } else { - return ({ - pkmn: obj, - buffer: { - status: 'CATCH_SUCCESS', - captured_pokemon_id: encounter.encounter_id, - capture_award: { - activity_type: ['ACTIVITY_CATCH_POKEMON'], - xp: [100], - candy: [3], - stardust: [100] - } - } - }); - } - -} \ No newline at end of file diff --git a/src/packets/Responses.CheckAwardedBadges.js b/src/packets/Responses.CheckAwardedBadges.js deleted file mode 100644 index d5079b9..0000000 --- a/src/packets/Responses.CheckAwardedBadges.js +++ /dev/null @@ -1,21 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function CheckAwardedBadges(obj) { - - let buffer = { - "success": true, - "awarded_badges": [], - "awarded_badge_levels": [], - "$unknownFields": [] - } - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CheckAwardedBadgesResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.CheckChallenge.js b/src/packets/Responses.CheckChallenge.js deleted file mode 100644 index 7aeecb0..0000000 --- a/src/packets/Responses.CheckChallenge.js +++ /dev/null @@ -1,17 +0,0 @@ -import CFG from "../../cfg"; - -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function CheckChallenge(req) { - - let buffer = ({ - challenge_url: " " - }); - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CheckChallengeResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.ClaimCodeName.js b/src/packets/Responses.ClaimCodeName.js deleted file mode 100644 index f23326f..0000000 --- a/src/packets/Responses.ClaimCodeName.js +++ /dev/null @@ -1,25 +0,0 @@ -import proto from "../proto"; - -import { GetPlayer } from "./"; - -/** - * @param {Request} req - * @param {Player} player - * @return {Object} - */ -export default function ClaimCodeName(req, player) { - - let data = proto.Networking.Requests.Messages.ClaimCodenameMessage.decode(req.request_message.toBuffer()); - - player.username = data.codename; - - return ( - new proto.Networking.Responses.ClaimCodenameResponse({ - codename: data.codename, - user_message: data.codename, - is_assignable: true, - status: 1 - }).encode() - ); - -} \ No newline at end of file diff --git a/src/packets/Responses.DownloadRemoteConfigVersion.js b/src/packets/Responses.DownloadRemoteConfigVersion.js deleted file mode 100644 index 0f2423f..0000000 --- a/src/packets/Responses.DownloadRemoteConfigVersion.js +++ /dev/null @@ -1,19 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function DownloadRemoteConfigVersion(obj) { - - let buffer = { - "result": "SUCCESS", - "item_templates_timestamp_ms": "1471650700946", - "asset_digest_timestamp_ms": "1467338276561000", - "$unknownFields": [] - } - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.DownloadRemoteConfigVersionResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.DownloadSettings.js b/src/packets/Responses.DownloadSettings.js deleted file mode 100644 index 4f18c65..0000000 --- a/src/packets/Responses.DownloadSettings.js +++ /dev/null @@ -1,44 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import CFG from "../../cfg"; - -/** - * @return {Buffer} - */ -export default function DownloadSettings() { - - let buffer = ({ - hash: "2788184af4004004d6ab0740f7632983332106f6", - settings: { - fort_settings: { - "interaction_range_meters": 40.25098039215686, - "max_total_deployed_pokemon": 10, - "max_player_deployed_pokemon": 1, - "deploy_stamina_multiplier": 8.062745098039215, - "deploy_attack_multiplier": 0, - "far_interaction_range_meters": 1000.0156862745098 - }, - map_settings: { - "pokemon_visible_range": 70.00196078431372, - "poke_nav_range_meters": 751.0156862745098, - "encounter_range_meters": 50.25098039215686, - "get_map_objects_min_refresh_seconds": 10.007843017578125, - "get_map_objects_max_refresh_seconds": 11.01568603515625, - "get_map_objects_min_distance_meters": 10.007843017578125, - "google_maps_api_key": CFG.GMAPS_KEY - }, - inventory_settings: { - "max_pokemon": 1000, - "max_bag_items": 1000, - "base_pokemon": 250, - "base_bag_items": 350, - "base_eggs": 9 - }, - minimum_client_version: CFG.MINIMUM_CLIENT_VERSION - } - }) - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.DownloadSettingsResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.Encounter.js b/src/packets/Responses.Encounter.js deleted file mode 100644 index 156199f..0000000 --- a/src/packets/Responses.Encounter.js +++ /dev/null @@ -1,46 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import { decodeLong } from "../utils"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function Encounter(encounter, req) { - - let buffer = ({ - wild_pokemon: { - encounter_id: encounter.encounter_id, - last_modified_timestamp_ms: '1471852141657', - latitude: encounter.latitude, - longitude: encounter.longitude, - spawn_point_id: '87bdcd8f2e9', - pokemon_data: { - pokemon_id: encounter.pokemon_id, - cp: encounter.cp, - stamina: 10, - stamina_max: 10, - move_1: "BUG_BITE_FAST", - move_2: "STRUGGLE", - height_m: 0.30962005257606506, - weight_kg: 3.3212273120880127, - individual_attack: 7, - individual_defense: 13, - individual_stamina: 3, - cp_multiplier: 0.16639786958694458 - }, - time_till_hidden_ms: 860075 - }, - status: 'ENCOUNTER_SUCCESS', - capture_probability: { - pokeball_type: ['ITEM_POKE_BALL', 'ITEM_GREAT_BALL', 'ITEM_ULTRA_BALL'], - capture_probability: [1, 1, 1] - } - }); - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.EncounterResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.EvolvePokemon.js b/src/packets/Responses.EvolvePokemon.js deleted file mode 100644 index e157f2c..0000000 --- a/src/packets/Responses.EvolvePokemon.js +++ /dev/null @@ -1,22 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function EvolvePokemon(req) { - - let data = proto.Networking.Requests.Messages.EvolvePokemonMessage.decode(req.request_message.toBuffer()); - - return ( - new proto.Networking.Responses.EvolvePokemonResponse({ - result: proto.Networking.Responses.EvolvePokemonResponse.Result.FAILED_POKEMON_CANNOT_EVOLVE, - evolved_pokemon_data: null, - experience_awarded: 0, - candy_awarded: 0 - }).encode() - ); - -} \ No newline at end of file diff --git a/src/packets/Responses.FortDetails.js b/src/packets/Responses.FortDetails.js deleted file mode 100644 index 0435f98..0000000 --- a/src/packets/Responses.FortDetails.js +++ /dev/null @@ -1,27 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function FortDetails(req) { - - let buffer = ({ - "fort_id": req.fort_id, - "name": `POGOserver v${CFG.VERSION}`, - "image_urls": [ - "http://thecatapi.com/api/images/get?format=src&type=png" - ], - "type": "CHECKPOINT", - "latitude": req.latitude, - "longitude": req.longitude, - "modifiers": [], - "$unknownFields": [] - }); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortDetailsResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.FortSearch.js b/src/packets/Responses.FortSearch.js deleted file mode 100644 index 6b6a7ca..0000000 --- a/src/packets/Responses.FortSearch.js +++ /dev/null @@ -1,42 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Player} player - * @return {Buffer} - */ -export default function FortSearch(player) { - - let ii = 0; - let items = []; - - let name = "ultra_ball"; - let amount = 5; - let exp = 50; - - while (++ii < amount) { - items.push({ - "item_id": "ITEM_" + name.toUpperCase(), - "item_count": 1 - }); - }; - - player.items[name] += amount; - player.exp += exp; - - console.log(player.items[name]); - - let buffer = ({ - "result": "SUCCESS", - "items_awarded": items, - "experience_awarded": exp, - "cooldown_complete_timestamp_ms": "1471780158665", - "chain_hack_sequence_number": 2, - "$unknownFields": [] - }); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortSearchResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.GetAssetDigest.js b/src/packets/Responses.GetAssetDigest.js deleted file mode 100644 index 2485000..0000000 --- a/src/packets/Responses.GetAssetDigest.js +++ /dev/null @@ -1,12 +0,0 @@ -import fs from "fs"; -import proto from "../proto"; - -import CFG from "../../cfg"; - -/** - * @param {Player} player - * @return {Object} - */ -export default function GetAssetDigest(player) { - return (player.asset_digest.buffer); -} \ No newline at end of file diff --git a/src/packets/Responses.GetDownloadUrls.js b/src/packets/Responses.GetDownloadUrls.js deleted file mode 100644 index 91ad0d4..0000000 --- a/src/packets/Responses.GetDownloadUrls.js +++ /dev/null @@ -1,42 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import CFG from "../../cfg"; - -import { - getDownloadUrlByAssetId -} from "../utils.js"; - -/** - * @return {Object} - */ -export default function GetDownloadUrls(asset, ip, req) { - - let key = req.asset_id[0]; - - let download_urls = []; - - let node = null; - - for (node of asset.digest) { - if (node.asset_id === key) { - break; - } - }; - - return new Promise((resolve) => { - download_urls.push( - { - asset_id: key, - url: `http://${ip}:${CFG.PORT}/model/${node.bundle_name}`, - size: node.size, - checksum: node.checksum - } - ); - let buffer = ({ - download_urls: download_urls - }); - resolve(POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetDownloadUrlsResponse")); - }); - -} \ No newline at end of file diff --git a/src/packets/Responses.GetHatchedEggs.js b/src/packets/Responses.GetHatchedEggs.js deleted file mode 100644 index a5dd156..0000000 --- a/src/packets/Responses.GetHatchedEggs.js +++ /dev/null @@ -1,21 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function GetHatchedEggs(obj) { - - let buffer = { - "success": true, - "pokemon_id": [], - "experience_awarded": [], - "candy_awarded": [], - "stardust_awarded": [], - "$unknownFields": [] - }; - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetHatchedEggsResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.GetInventory.js b/src/packets/Responses.GetInventory.js deleted file mode 100644 index 8ac6ad9..0000000 --- a/src/packets/Responses.GetInventory.js +++ /dev/null @@ -1,112 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -let isFirst = false; - -/** - * @param {Object} data - * @return {Object} - */ -export default function GetInventoryData(player) { - - let entries = []; - - entries.push({ - "modified_timestamp_ms": new Date().getTime() - 1e3, - "inventory_item_data": { - "player_stats": { - "level": player.level, - "experience": player.exp, - "prev_level_xp": "21000", - "next_level_xp": "36000", - "km_walked": 3.921541213989258, - "pokemons_encountered": 75, - "unique_pokedex_entries": 25, - "pokemons_captured": 71, - "poke_stop_visits": 123, - "pokeballs_thrown": 74, - "eggs_hatched": 1, - "big_magikarp_caught": 1, - "pokemon_deployed": 1, - "pokemon_caught_by_type": { - "type": "Buffer", - "data": [ - 0, - 24, - 0, - 26, - 20, - 1, - 0, - 14, - 0, - 0, - 1, - 14, - 3, - 0, - 1, - 0, - 0, - 0, - 3 - ] - } - } - } - }); - - for (let key in player.items) { - let amount = parseInt(player.items[key]); - if (amount > 0) { - entries.push({ - "modified_timestamp_ms": new Date().getTime() - 1e3, - "inventory_item_data": { - "item": { - "item_id": "ITEM_" + key.toUpperCase(), - "count": amount - } - } - }); - } - }; - - let party = player.party; - - for (let pkmn of party) { - entries.push({ - "inventory_item_data": { - "pokemon_data": { - "id": pkmn.id, - "pokemon_id": pkmn.pokemon_id, - "cp": pkmn.cp, - "stamina": pkmn.stamina, - "stamina_max": pkmn.stamina_max, - "move_1": pkmn.move_1, - "move_2": pkmn.move_2, - "height_m": pkmn.height_m, - "weight_kg": pkmn.weight_kg, - "individual_attack": pkmn.individual_attack, - "individual_defense": pkmn.individual_defense, - "individual_stamina": pkmn.individual_stamina, - "cp_multiplier": pkmn.cp_multiplier, - "pokeball": pkmn.pokeball, - "captured_cell_id": pkmn.captured_cell_id, - "creation_time_ms": pkmn.creation_time_ms, - "favorite": pkmn.favorite - } - } - }); - }; - - let buffer = ({ - "success": true, - "inventory_delta": { - "new_timestamp_ms": new Date().getTime() - 1e3, - "inventory_items": entries - } - }); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetInventoryResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.GetMapObjects.js b/src/packets/Responses.GetMapObjects.js deleted file mode 100644 index 9d7e60a..0000000 --- a/src/packets/Responses.GetMapObjects.js +++ /dev/null @@ -1,195 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Player} player - * @param {Request} request - * @return {Object} - */ -export default function GetMapObjects(player, wild_pkmns, request) { - - let cells = request.cell_id; - let cellsRes = []; - - let pokemon_id = 121; - - cells.forEach((cell) => { - cellsRes.push({ - s2_cell_id: cell, - current_timestamp_ms: new Date().getTime() * 1e3, - forts: [], - spawn_points: [], - deleted_objects: [], - is_truncated_list: false, - fort_summaries: [], - decimated_spawn_points: [], - wild_pokemons: [], - catchable_pokemons: [], - nearby_pokemons: [] - }); - }); - - let latitude = player.latitude; - let longitude = player.longitude; - - let cell = cellsRes[0]; - - cell.forts = [ - { - "id": "108dc9c703a94b619a53a3c29b5c676f.11", - "last_modified_timestamp_ms": "1471621873766", - "latitude": latitude + 0.0002, - "longitude": longitude - 0.0001, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "1391315b489f421abd52ce10b6da7dd3.16", - "last_modified_timestamp_ms": "1471776325164", - "latitude": 39.188351, - "longitude": -96.582561, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "33ad17c71fff4797b7f25dcfcdca48df.16", - "last_modified_timestamp_ms": "1470755536809", - "latitude": 39.189386, - "longitude": -96.581113, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "40f86138ca6f49cc9e98e18b77d6067a.16", - "last_modified_timestamp_ms": "1471748353301", - "latitude": 39.188351, - "longitude": -96.582209, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "560ef7ed6cb5469db29b543cce7677ae.16", - "last_modified_timestamp_ms": "1469923654520", - "latitude": 39.186848, - "longitude": -96.581267, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "60d2ffcfe9cf4003b4d94bcc9ee0c318.16", - "last_modified_timestamp_ms": "1471811998365", - "latitude": 39.188008, - "longitude": -96.582409, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "637941f1f2494276884ede4fc538c2b2.16", - "last_modified_timestamp_ms": "1471816829142", - "latitude": 39.186868, - "longitude": -96.583295, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "7c0154d02e4f4d91b9b74214eda75abc.16", - "last_modified_timestamp_ms": "1471483788730", - "latitude": 39.187991, - "longitude": -96.581251, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "8a94bf6cd17b43be8b5b42655d23e2f6.16", - "last_modified_timestamp_ms": "1471751252546", - "latitude": 39.187684, - "longitude": -96.583537, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "9b469dbd550f43b5a8e94f941d66bc43.16", - "last_modified_timestamp_ms": "1471243641146", - "latitude": 39.189051, - "longitude": -96.581804, - "enabled": true, - "type": "CHECKPOINT" - }, - { - "id": "bb0e515e3f554b398f1fa9c58435dc78.16", - "last_modified_timestamp_ms": "1471748285036", - "latitude": 39.188823, - "longitude": -96.582742, - "enabled": true, - "type": "CHECKPOINT" - } - ]; - - cell.spawn_points = [ - { - "latitude": 39.188870250798956, - "longitude": -96.58196728549451 - }, - { - "latitude": 39.1889464335866, - "longitude": -96.58205070456887 - }, - { - "latitude": 39.18887496474473, - "longitude": -96.58188386652927 - }, - { - "latitude": 39.18879406783625, - "longitude": -96.58188386652927 - }, - { - "latitude": 39.189284161630674, - "longitude": -96.58180044767312 - } - ]; - - for (let pkmn of wild_pkmns) { - cell.wild_pokemons.push({ - encounter_id: pkmn.encounter_id, - last_modified_timestamp_ms: new Date().getTime(), - latitude: pkmn.latitude, - longitude: pkmn.longitude, - spawn_point_id: "87bdd289c69", - pokemon_data: { - pokemon_id: pkmn.pokemon_id, - cp: pkmn.cp, - stamina: 41, - stamina_max: 41, - move_1: 221, - move_2: 26, - height_m: 0.22802678267819977, - weight_kg: 1.3452539511871338, - individual_attack: 9, - individual_defense: 13, - individual_stamina: 14, - cp_multiplier: 0.5663545199394226 - }, - time_till_hidden_ms: 730176 - }); - cell.catchable_pokemons.push({ - spawn_point_id: "87bdd289c69", - encounter_id: pkmn.encounter_id, - pokemon_id: pkmn.pokemon_id, - latitude: latitude, - longitude: longitude, - expiration_timestamp_ms: (new Date().getTime() + 1e6) * 1e3 - }); - cell.nearby_pokemons.push({ - distance_in_meters: 200.0, - pokemon_id: pkmn.pokemon_id - }); - }; - - let buffer = ({ - status: "SUCCESS", - map_cells: cellsRes - }); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.GetPlayer.js b/src/packets/Responses.GetPlayer.js deleted file mode 100644 index 455fb01..0000000 --- a/src/packets/Responses.GetPlayer.js +++ /dev/null @@ -1,101 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Object} obj - * @return {Object} - */ -function getPlayerDataPacket(obj) { - - return ( - { - creation_timestamp_ms: 1467936859925, - username: obj.username, - team: proto.Enums.TeamColor.YELLOW, - tutorial_state: obj.tutorial_state, - avatar: obj.avatar, - max_pokemon_storage: 250, - max_item_storage: 350, - daily_bonus: { - next_defender_bonus_collect_timestamp_ms: 1470174535972 - }, - contact_settings: obj.contact_settings, - currencies: obj.currencies - } - ); - -} - -/** - * @param {Object} obj - * @return {Object} - */ -function buildPlayerData(obj) { - - let team = obj.team; - let username = obj.username; - - let pokecoins = obj.pokecoins; - let stardust = obj.stardust; - - let avatar = { - skin: obj.skin, - hair: obj.hair, - shirt: obj.shirt, - pants: obj.pants, - hat: obj.hat, - shoes: obj.shoes, - eyes: obj.eyes, - gender: obj.gender, - backpack: obj.backpack - }; - - let contact_settings = { - send_marketing_emails: obj.send_marketing_emails, - send_push_notifications: obj.send_push_notifications - }; - - let tutorial_state = [ - proto.Enums.TutorialState.LEGAL_SCREEN, - proto.Enums.TutorialState.AVATAR_SELECTION, - proto.Enums.TutorialState.POKEMON_CAPTURE, - proto.Enums.TutorialState.NAME_SELECTION, - proto.Enums.TutorialState.FIRST_TIME_EXPERIENCE_COMPLETE - ]; - - let currencies = [ - { - name: "POKECOIN", - amount: pokecoins - }, - { - name: "STARDUST", - amount: stardust - } - ]; - - return ({ - username: username, - team: team, - tutorial_state: tutorial_state, - contact_settings: contact_settings, - avatar: avatar, - currencies: currencies - }); - -} - -export default function GetPlayer(obj) { - - let data = buildPlayerData(obj); - - let packet = getPlayerDataPacket(data); - - let buffer = { - success: true, - player_data: packet - }; - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.GetPlayerProfile.js b/src/packets/Responses.GetPlayerProfile.js deleted file mode 100644 index 5a373ee..0000000 --- a/src/packets/Responses.GetPlayerProfile.js +++ /dev/null @@ -1,168 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function GetPlayerProfile(obj) { - - let buffer = ({ - "result": "SUCCESS", - "start_time": "1471096437979", - "badges": [ - { - "badge_type": "BADGE_TRAVEL_KM", - "end_value": 10, - "current_value": 3.921541213989258 - }, - { - "badge_type": "BADGE_POKEDEX_ENTRIES", - "rank": 1, - "start_value": 5, - "end_value": 50, - "current_value": 25 - }, - { - "badge_type": "BADGE_CAPTURE_TOTAL", - "rank": 1, - "start_value": 30, - "end_value": 500, - "current_value": 71 - }, - { - "badge_type": "BADGE_EVOLVED_TOTAL", - "end_value": 3 - }, - { - "badge_type": "BADGE_HATCHED_TOTAL", - "end_value": 10, - "current_value": 1 - }, - { - "badge_type": "BADGE_POKESTOPS_VISITED", - "rank": 1, - "start_value": 100, - "end_value": 1000, - "current_value": 123 - }, - { - "badge_type": "BADGE_BIG_MAGIKARP", - "end_value": 3, - "current_value": 1 - }, - { - "badge_type": "BADGE_BATTLE_ATTACK_WON", - "end_value": 10 - }, - { - "badge_type": "BADGE_BATTLE_TRAINING_WON", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_NORMAL", - "rank": 1, - "start_value": 10, - "end_value": 50, - "current_value": 24 - }, - { - "badge_type": "BADGE_TYPE_FIGHTING", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_FLYING", - "rank": 1, - "start_value": 10, - "end_value": 50, - "current_value": 26 - }, - { - "badge_type": "BADGE_TYPE_POISON", - "rank": 1, - "start_value": 10, - "end_value": 50, - "current_value": 20 - }, - { - "badge_type": "BADGE_TYPE_GROUND", - "end_value": 10, - "current_value": 1 - }, - { - "badge_type": "BADGE_TYPE_ROCK", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_BUG", - "rank": 1, - "start_value": 10, - "end_value": 50, - "current_value": 14 - }, - { - "badge_type": "BADGE_TYPE_GHOST", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_STEEL", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_FIRE", - "end_value": 10, - "current_value": 1 - }, - { - "badge_type": "BADGE_TYPE_WATER", - "rank": 1, - "start_value": 10, - "end_value": 50, - "current_value": 14 - }, - { - "badge_type": "BADGE_TYPE_GRASS", - "end_value": 10, - "current_value": 3 - }, - { - "badge_type": "BADGE_TYPE_ELECTRIC", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_PSYCHIC", - "end_value": 10, - "current_value": 1 - }, - { - "badge_type": "BADGE_TYPE_ICE", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_DRAGON", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_DARK", - "end_value": 10 - }, - { - "badge_type": "BADGE_TYPE_FAIRY", - "end_value": 10, - "current_value": 3 - }, - { - "badge_type": "BADGE_SMALL_RATTATA", - "end_value": 3 - }, - { - "badge_type": "BADGE_PIKACHU", - "end_value": 3 - } - ], - "$unknownFields": [] - }); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse")); - -} diff --git a/src/packets/Responses.ItemTemplates.js b/src/packets/Responses.ItemTemplates.js deleted file mode 100644 index 691b18d..0000000 --- a/src/packets/Responses.ItemTemplates.js +++ /dev/null @@ -1,13 +0,0 @@ -import CFG from "../../cfg"; - -import fs from "fs"; -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Buffer} master - * @return {Object} - */ -export default function ItemTemplates(master) { - return (master); -} \ No newline at end of file diff --git a/src/packets/Responses.LevelUpRewards.js b/src/packets/Responses.LevelUpRewards.js deleted file mode 100644 index 5768804..0000000 --- a/src/packets/Responses.LevelUpRewards.js +++ /dev/null @@ -1,21 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import { GetPlayer } from "./"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function LevelUpRewards(obj) { - - let buffer = { - "result": "AWARDED_ALREADY", - "items_awarded": [], - "items_unlocked": [], - "$unknownFields": [] - } - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.LevelUpRewardsResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.MarkTutorialComplete.js b/src/packets/Responses.MarkTutorialComplete.js deleted file mode 100644 index 0c12249..0000000 --- a/src/packets/Responses.MarkTutorialComplete.js +++ /dev/null @@ -1,25 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import { GetPlayer } from "./"; - -/** - * @param {Player} player - * @param {Object} req - * @return {Object} - */ -export default function MarkTutorialComplete(player, req) { - - if (req.tutorials_completed[0] === "LEGAL_SCREEN") { - player.tutorial_state.push("LEGAL_SCREEN"); - player.send_marketing_emails = !!req.send_marketing_emails; - } - - let buffer = ({ - success: true, - player_data: GetPlayer(player).player_data - }); - - return POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetHatchedEggsResponse"); - -} \ No newline at end of file diff --git a/src/packets/Responses.NicknamePokemon.js b/src/packets/Responses.NicknamePokemon.js deleted file mode 100644 index e6959a7..0000000 --- a/src/packets/Responses.NicknamePokemon.js +++ /dev/null @@ -1,21 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function NicknamePokemon(req) { - - let data = proto.Networking.Requests.Messages.NicknamePokemonMessage.decode(req.request_message.toBuffer()); - - // TODO: Save nickname into db - - return ( - new proto.Networking.Responses.NicknamePokemonResponse({ - result: proto.Networking.Responses.NicknamePokemonResponse.Result.ERROR_INVALID_NICKNAME - }).encode() - ); - -} \ No newline at end of file diff --git a/src/packets/Responses.SetAvatar.js b/src/packets/Responses.SetAvatar.js deleted file mode 100644 index 19cd9a7..0000000 --- a/src/packets/Responses.SetAvatar.js +++ /dev/null @@ -1,23 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import { GetPlayer } from "./"; - -/** - * @param {Player} player - * @return {Object} - */ -export default function SetAvatar(player) { - - let buffer = ({ - status: proto.Networking.Responses.SetAvatarResponse.Status.SUCCESS, - player_data: GetPlayer(player) - }); - - if (player.tutorial_state.indexOf("AVATAR_SELECTION") === -1) { - player.tutorial_state.push("AVATAR_SELECTION"); - } - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.SetAvatarResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.SetContactSettings.js b/src/packets/Responses.SetContactSettings.js deleted file mode 100644 index e11041d..0000000 --- a/src/packets/Responses.SetContactSettings.js +++ /dev/null @@ -1,20 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; - -import { GetPlayer } from "./"; - -/** - * @param {Player} player - * @return {Object} - */ -export default function SetContactSettings(player) { - - return ( - new proto.Networking.Responses.SetContactSettingsResponse({ - status: proto.Networking.Responses.SetContactSettingsResponse.Status.SUCCESS, - player_data: GetPlayer(player).player_data - }).encode() - ); - -} \ No newline at end of file diff --git a/src/packets/Responses.SetFavoritePokemon.js b/src/packets/Responses.SetFavoritePokemon.js deleted file mode 100644 index 100ff99..0000000 --- a/src/packets/Responses.SetFavoritePokemon.js +++ /dev/null @@ -1,22 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function SetFavoritePokemon(player, req) { - - let buffer = { - "result": "SUCCESS", - }; - - player.setFavoritePkmn(req.pokemon_id << 0, req.is_favorite); - - return ( - POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.SetFavoritePokemonResponse") - ); - -} \ No newline at end of file diff --git a/src/packets/Responses.SfidaActionLog.js b/src/packets/Responses.SfidaActionLog.js deleted file mode 100644 index 5c107c9..0000000 --- a/src/packets/Responses.SfidaActionLog.js +++ /dev/null @@ -1,650 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import CFG from "../../cfg"; - -/** - * @param {Object} obj - * @return {Object} - */ -export default function SfidaActionLog(obj) { - - let buffer = ({ - "result": "SUCCESS", - "log_entries": [ - { - "timestamp_ms": "1471694560585", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "HORSEA", - "combat_points": 110 - } - }, - { - "timestamp_ms": "1471694566830", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "HORSEA", - "combat_points": 73 - } - }, - { - "timestamp_ms": "1471694573468", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471694597662", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "HORSEA", - "combat_points": 102 - } - }, - { - "timestamp_ms": "1471694603880", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "CATERPIE", - "combat_points": 36 - } - }, - { - "timestamp_ms": "1471694610259", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "WEEDLE", - "combat_points": 15 - } - }, - { - "timestamp_ms": "1471694616586", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "PIDGEY", - "combat_points": 26 - } - }, - { - "timestamp_ms": "1471694632969", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 1 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471694739319", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471694741772", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 4 - } - ] - } - }, - { - "timestamp_ms": "1471694904167", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "KRABBY", - "combat_points": 100 - } - }, - { - "timestamp_ms": "1471694925722", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 2 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 4 - } - ] - } - }, - { - "timestamp_ms": "1471695030964", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471695071016", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695089887", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "SPEAROW", - "combat_points": 80 - } - }, - { - "timestamp_ms": "1471695096341", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471695139926", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "EKANS", - "combat_points": 72 - } - }, - { - "timestamp_ms": "1471695148440", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695195481", - "catch_pokemon": { - "result": "UNKNOWN_ENUM_VALUE_Result_3", - "pokemon_id": "MAGIKARP", - "combat_points": 26 - } - }, - { - "timestamp_ms": "1471695197676", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "ZUBAT", - "combat_points": 41 - } - }, - { - "timestamp_ms": "1471695203917", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "MAGIKARP", - "combat_points": 46 - } - }, - { - "timestamp_ms": "1471695211919", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471695316895", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "ZUBAT", - "combat_points": 43 - } - }, - { - "timestamp_ms": "1471695323589", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471695429795", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 1 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695487237", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471695522740", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471695577816", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 1 - }, - { - "item_id": "ITEM_POTION", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471695633092", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 1 - }, - { - "item_id": "ITEM_POTION", - "count": 2 - } - ], - "eggs": 1 - } - }, - { - "timestamp_ms": "1471695738726", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471695776746", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_REVIVE", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695790415", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_REVIVE", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 4 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695829422", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "MAGIKARP", - "combat_points": 40 - } - }, - { - "timestamp_ms": "1471695836445", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695838748", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695860629", - "catch_pokemon": { - "result": "POKEMON_FLED", - "pokemon_id": "VENONAT", - "combat_points": 174 - } - }, - { - "timestamp_ms": "1471695866223", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471695884419", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "STARYU", - "combat_points": 11 - } - }, - { - "timestamp_ms": "1471695889856", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471695906137", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "PINSIR", - "combat_points": 28 - } - }, - { - "timestamp_ms": "1471695914356", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471696008778", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471696010713", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "CATERPIE", - "combat_points": 74 - } - }, - { - "timestamp_ms": "1471696050313", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471696052566", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - }, - { - "timestamp_ms": "1471696072147", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 1 - }, - { - "item_id": "ITEM_POTION", - "count": 2 - } - ] - } - }, - { - "timestamp_ms": "1471696074058", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "ZUBAT", - "combat_points": 40 - } - }, - { - "timestamp_ms": "1471696079308", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_RAZZ_BERRY", - "count": 1 - }, - { - "item_id": "ITEM_POKE_BALL", - "count": 2 - }, - { - "item_id": "ITEM_POTION", - "count": 1 - } - ] - } - }, - { - "timestamp_ms": "1471696081215", - "catch_pokemon": { - "result": "POKEMON_CAPTURED", - "pokemon_id": "ZUBAT", - "combat_points": 73 - } - }, - { - "timestamp_ms": "1471696235352", - "fort_search": { - "result": "SUCCESS", - "items": [ - { - "item_id": "ITEM_POKE_BALL", - "count": 3 - } - ] - } - } - ], - "$unknownFields": [] -}); - - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.SfidaActionLogResponse")); - -} \ No newline at end of file diff --git a/src/packets/Responses.ShopData.js b/src/packets/Responses.ShopData.js deleted file mode 100644 index 2d3bea7..0000000 --- a/src/packets/Responses.ShopData.js +++ /dev/null @@ -1,71 +0,0 @@ -import proto from "../proto"; -import POGOProtos from "pokemongo-protobuf"; - -import CFG from "../../cfg"; - -/** - * @return {Object} - */ -export default function ShopData() { - - let buffer = ({ - "response_type": 5, - "unknown2": { - "unknown1": "1", - "items": [ - { - "item_id": "pgorelease.pokeball.100", - "currency_to_buy": { - "name": "POKECOIN", - "amount": 460 - }, - "yields_item": { - "count": 100 - }, - "tags": [ - { - "key": "CATEGORY", - "value": "ITEMS" - }, - { - "key": "SORT", - "value": "2" - } - ] - }, - { - "item_id": "pgorelease.pokecoin.100", - "is_iap": true, - "yields_currency": { - "name": "POKECOIN", - "amount": 100 - }, - "tags": [ - { - "key": "CATEGORY", - "value": "POKECOINS" - }, - { - "key": "SORT", - "value": "1" - } - ], - "unknown7": 1 - } - ], - "player_currencies": [ - { - "name": "POKECOIN" - }, - { - "name": "STARDUST", - "amount": 7681 - } - ], - "unknown4": "ZCgldzCNR+0pbImyvzykby/6+iA=" - } - }); - - return (buffer); - -} \ No newline at end of file diff --git a/src/packets/Responses.UpgradePokemon.js b/src/packets/Responses.UpgradePokemon.js deleted file mode 100644 index bd78790..0000000 --- a/src/packets/Responses.UpgradePokemon.js +++ /dev/null @@ -1,20 +0,0 @@ -import CFG from "../../cfg"; - -import proto from "../proto"; - -/** - * @param {Request} req - * @return {Object} - */ -export default function UpgradePokemon(req) { - - let data = proto.Networking.Requests.Messages.UpgradePokemonMessage.decode(req.request_message.toBuffer()); - - return ( - new proto.Networking.Responses.UpgradePokemonResponse({ - result: proto.Networking.Responses.UpgradePokemonResponse.Result.ERROR_UPGRADE_NOT_AVAILABLE, - upgraded_pokemon: null - }).encode() - ); - -} \ No newline at end of file diff --git a/src/packets/index.js b/src/packets/index.js deleted file mode 100644 index f4903aa..0000000 --- a/src/packets/index.js +++ /dev/null @@ -1,42 +0,0 @@ -export DownloadRemoteConfigVersion from "./Responses.DownloadRemoteConfigVersion"; -export DownloadSettings from "./Responses.DownloadSettings"; - -export GetAssetDigest from "./Responses.GetAssetDigest"; -export GetHatchedEggs from "./Responses.GetHatchedEggs"; -export GetInventory from "./Responses.GetInventory"; -export GetPlayer from "./Responses.GetPlayer"; -export GetPlayerProfile from "./Responses.GetPlayerProfile"; - -export FortDetails from "./Responses.FortDetails"; -export FortSearch from "./Responses.FortSearch"; -export GetMapObjects from "./Responses.GetMapObjects"; - -export SetContactSettings from "./Responses.SetContactSettings"; - -export ItemTemplates from "./Responses.ItemTemplates"; -export CheckAwardedBadges from "./Responses.CheckAwardedBadges"; -export GetDownloadUrls from "./Responses.GetDownloadUrls"; - -export MarkTutorialComplete from "./Responses.MarkTutorialComplete"; -export ClaimCodeName from "./Responses.ClaimCodeName"; - -export LevelUpRewards from "./Responses.LevelUpRewards"; - -export SetAvatar from "./Responses.SetAvatar"; - -export SfidaActionLog from "./Responses.SfidaActionLog"; - -export CheckChallenge from "./Responses.CheckChallenge"; - -export Encounter from "./Responses.Encounter"; -export CatchPokemon from "./Responses.CatchPokemon"; -export NicknamePokemon from "./Responses.NicknamePokemon"; -export UpgradePokemon from "./Responses.UpgradePokemon"; -export EvolvePokemon from "./Responses.EvolvePokemon"; -export SetFavoritePokemon from "./Responses.SetFavoritePokemon"; - -export ShopData from "./Responses.ShopData"; - -export AuthTicket from "./Envelopes.AuthTicket"; -export ResponseEnvelope from "./Envelopes.ResponseEnvelope"; -export ResponseEnvelopeAuth from "./Envelopes.ResponseEnvelope.Auth"; \ No newline at end of file diff --git a/src/player.js b/src/player.js deleted file mode 100644 index a10ecce..0000000 --- a/src/player.js +++ /dev/null @@ -1,556 +0,0 @@ -import proto from "./proto"; - -import CFG from "../cfg"; - -import { - getHashCodeFrom, - decodeRequestEnvelope -} from "./utils"; - -import { - ResponseEnvelope, - ResponseEnvelopeAuth -} from "./packets"; - -import jwtDecode from "jwt-decode"; - -import { GetPlayer } from "./packets"; - -/** - * @class Player - */ -class Player { - - /** @constructor */ - constructor(obj) { - - this.uid = -1; - this.owner_id = -1; - - this._email = null; - this.username = "unknown"; - - this.latitude = 0; - this.longitude = 0; - this.altitude = 0; - - this.send_marketing_emails = false; - this.send_push_notifications = false; - - this.exp = 0; - this.level = 1; - - this.stardust = 0; - this.pokecoins = 0; - - this.team = 0; - - this.skin = 0; - this.hair = 0; - this.shirt = 0; - this.pants = 0; - this.hat = 0; - this.shoes = 0; - this.eyes = 0; - this.gender = 0; - this.backpack = 0; - - this.items = { - poke_ball: 0, - great_ball: 0, - ultra_ball: 0, - master_ball: 0, - potion: 0, - super_potion: 0, - hyper_potion: 0, - max_potion: 0, - revive: 0, - max_revive: 0, - lucky_egg: 0, - incense_ordinary: 0, - incense_spicy: 0, - incense_cool: 0, - incense_floral: 0, - troy_disk: 0, - razz_berry: 0, - bluk_berry: 0, - nanab_berry: 0, - wepar_berry: 0, - pinap_berry: 0, - incubator_basic: 0, - incubator_basic_unlimited: 0, - pokemon_storage_upgrade: 0, - storage_upgrade: 0 - }; - - this.party = []; - - this.tutorial_state = []; - - this.badges = null; - this.pokedex = null; - this.inventory = null; - - this.asset_digest = null; - - this.hasSignature = false; - - this.isIOS = false; - this.isAndroid = false; - - this.isPTCAccount = false; - this.isGoogleAccount = false; - - this.request = obj.request; - this.response = null; - this.connection = obj.connection; - - this.timeout = obj.timeout; - - this.remotePort = obj.remotePort; - this.remoteAddress = obj.remoteAddress; - - this.currentEncounter = null; - - this.loggedIn = obj.loggedIn || false; - this.authenticated = false; - - this.authentications = 0; - - } - - get email() { - return (this._email); - } - set email(value) { - this._email = value.replace("@gmail.com", ""); - } - - /** - * @param {String} email - */ - generateUid(email) { - this.uid = getHashCodeFrom(String(email)); - } - - /** - * @param {Buffer} buffer - */ - sendResponse(buffer) { - this.response.end(buffer); - } - - /** - * @param {Response} res - */ - updateResponse(res) { - this.response = res; - } - - updateByObject(obj) { - for (let key in obj) { - if (this.hasOwnProperty(key)) { - if (key === "send_marketing_emails" || key === "send_push_notifications") { - this[key] = !!obj[key]; - } - else { - this[key] = obj[key]; - } - } - else if (key.substring(0, 5) === "item_") { - let name = key.substring(5, key.length); - this.items[name] = obj[key]; - } - else if (key === "id") { - this.owner_id = obj[key]; - } - }; - } - - /** - * @param {Object} obj - */ - updatePosition(obj) { - this.latitude = obj.latitude; - this.longitude = obj.longitude; - } - - updateAvatar(req) { - - let node = null; - - for (let key in req.player_avatar) { - node = req.player_avatar[key]; - if (key === "gender") { - this.gender = node === "FEMALE" ? 1 : 0; - } - else { - if (this.hasOwnProperty(key)) { - this[key] = node; - } - } - }; - - } - - updateContactSettings(req) { - - let data = proto.Networking.Requests.Messages.SetContactSettingsMessage.decode(req.request_message.toBuffer()).contact_settings; - - if (!data) return void 0; - - this.send_marketing_emails = data.send_marketing_emails; - this.send_push_notifications = data.send_push_notifications; - - } - - getPkmnIndexFromPartyById(id) { - for (let ii = 0; ii < this.party.length; ++ii) { - if (this.party[ii].id === id) return (ii); - }; - return (-1); - } - - getPkmnFromPartyById(id) { - let index = this.getPkmnIndexFromPartyById(id); - return (this.party[index]); - } - - deletePkmnFromParty(id) { - let index = this.getPkmnIndexFromPartyById(id); - let pkmn = this.party[index]; - if (pkmn) this.party.splice(index, 1); - } - - setFavoritePkmn(id, favorite) { - let pkmn = this.getPkmnFromPartyById(id); - if (pkmn) pkmn.favorite = favorite; - } - -} - -/** - * @param {String} name - * @return {Boolean} - */ -export function validPlayerName(name) { - return !( - name === null || - name === void 0 || - typeof name === "string" && - name.length <= 1 - ); -} - -/** - * @param {Player} player - */ -export function getPlayerIndex(player) { - - let ip = player.remoteAddress; - let index = -1; - - let ii = 0, length = this.clients.length; - - for (; ii < length; ++ii) { - if (this.clients[ii].remoteAddress === ip) { - index = ii; - break; - } - }; - - return (index); - -} - -/** - * @param {Request} req - */ -export function getPlayerByRequest(req) { - return ( - this.getPlayerByIP(req.headers.host) - ); -} - -/** - * @param {String} ip - */ -export function getPlayerByIP(ip) { - - let ii = 0, length = this.clients.length; - - for (; ii < length; ++ii) { - if (this.clients[ii].remoteAddress === ip) { - return (this.clients[ii]); - } - }; - - return (null); - -} - -/** - * @param {String} name - */ -export function getPlayerByName(name) { - - let ii = 0, length = this.clients.length; - - for (; ii < length; ++ii) { - if (this.clients[ii].username === name) { - return (this.clients[ii]); - } - }; - - return (null); - -} - -/** - * @param {Request} req - * @return {Player} - */ -export function addPlayer(req) { - - let connection = req.connection; - - let player = new Player({ - timeout: this.time, - connection: connection, - request: req, - remotePort: connection.remotePort, - remoteAddress: req.headers.host - }); - - this.clients.push(player); - - return (player); - -} - -/** - * @param {Player} player - */ -export function removePlayer(player) { - - let index = this.getPlayerIndex(player); - - if (index >= 0) { - this.clients.splice(index, 1); - this.print(`${player.remoteAddress} disconnected!`, 36); - this.emit("killPlayer", player); - } - else { - this.print("Failed at removing player", 33); - } - -} - -/** - * @param {String} name - * @param {String} pkmn - */ -export function spawnPkmnAtPlayer(name, pkmn, amount) { - - if (!this.validPlayerName(name)) { - this.print(`Invalid player name!`, 31); - return void 0; - } - - let spawn = null; - let player = this.getPlayerByName(name); - - if (player !== null) { - let index = 0; - while (++index <= amount) { - spawn = { - pokemon_id: pkmn.toUpperCase(), - cp: Math.random() * 1e3 << 0, - encounter_id: Math.random() * 1e5 << 0, - latitude: player.latitude + parseFloat((Math.random() / 1e3).toFixed(5)), - longitude: player.longitude + parseFloat((Math.random() / 1e3).toFixed(5)) - }; - this.wild_pokemons.push(spawn); - }; - this.print(`Spawned ${amount}x ${pkmn}'s to ${name} at ${spawn.latitude.toFixed(7)}:${spawn.longitude.toFixed(7)}!`); - } - else this.print(`Failed to spawn ${pkmn} to player ${name}!`, 31); - -} - -/** - * @param {String} name - */ -export function kickPlayer(name) { - - if (!this.validPlayerName(name)) { - this.print(`Invalid player name!`, 31); - return void 0; - } - - let player = this.getPlayerByName(name); - - if (player !== null) { - this.removePlayer(player); - this.print(`Kicked ${name} from the server!`); - } - else this.print(`Failed to kick ${name} from the server!`, 31); - -} - -export function updatePlayers() { - //this.print("Updating players"); -} - -export function saveAllPlayers() { - for (let client of this.clients) { - this.savePlayer(client); - }; -} - -export function removeAllPlayers() { - for (let client of this.clients) { - this.removePlayer(client); - }; -} - -/** - * @param {Player} player - */ -export function savePlayer(player) { - return new Promise((resolve) => { - if (player.authenticated) { - this.updateUser(player).then(() => { - this.updateUserItems(player).then(() => { - this.updateUserParty(player).then(() => { - // refresh player data by database - this.loginPlayer(player).then(resolve); - }); - }); - }); - } - }); -} - -/** - * @param {Player} player - */ -export function forwardPlayer(player) { - - return new Promise((resolve) => { - this.getUserByEmail(player.email).then((doc) => { - if (player.email.length) { - let provider = player.isGoogleAccount ? "Google" : "PTC"; - this.print(`${player.email} authenticated via ${provider}!`, 36); - } - if (doc) { - this.loginPlayer(player).then((res) => { - this.emit("loginPlayer", player); - resolve(res); - }); - } - else { - this.registerPlayer(player).then((res) => { - this.emit("registerPlayer", player); - resolve(res); - }); - } - }); - }); - -} - -/** - * @param {Player} player - */ -export function loginPlayer(player) { - - return new Promise((resolve) => { - this.getUserByEmail(player.email).then((user) => { - user = user[0]; - this.getPkmnByColumn("owner_id", user.id).then((party) => { - player.updateByObject(user); - player.party = party; - let buffer = GetPlayer(player); - resolve(buffer); - }); - }); - }); - -} - -/** - * @param {Player} player - */ -export function registerPlayer(player) { - - return new Promise((resolve) => { - this.createUser(player).then(() => { - this.print(`${player.email} registered!`, 36); - this.loginPlayer(player).then((res) => { - resolve(res); - }); - }); - }); - -} - -/** - * @param {Player} player - * @return {Buffer} - */ -export function authenticatePlayer(player) { - - let request = decodeRequestEnvelope(player.request.body); - - let msg = ResponseEnvelopeAuth({ - id: request.request_id - }); - - let token = request.auth_info; - - // TODO: Support PTC server authentification - - if (!token || !token.provider) { - this.print("Invalid authentication token! Kicking..", 31); - this.removePlayer(player); - return void 0; - } - - if (token.provider === "google") { - if (token.token !== null) { - let decoded = jwtDecode(token.token.contents); - player.generateUid(decoded.email); - player.email = decoded.email; - player.email_verified = decoded.email_verified; - player.isGoogleAccount = true; - this.print(`${player.email} connected!`, 36); - } - else { - this.print("Invalid authentication token! Kicking..", 31); - this.removePlayer(player); - return void 0; - } - } - else if (token.provider === "ptc") { - let decoded = token.token.contents; - player.isPTCAccount = true; - player.email = decoded.split("-")[1]; - player.email_verified = true; - player.isPTCAccount = true; - //console.log(decoded); - this.print("PTC auth isnt supported yet! Your progress wont get saved!", 33); - //this.removePlayer(player); - //return void 0; - } - else { - this.print("Invalid provider! Kicking..", 31); - this.removePlayer(player); - return void 0; - } - - player.authenticated = true; - - return (msg); - -} diff --git a/src/process.js b/src/process.js index 51edbb4..e437c21 100644 --- a/src/process.js +++ b/src/process.js @@ -5,10 +5,11 @@ import CFG from "../cfg"; const helpMessage = fs.readFileSync(".help", "utf8"); export function processCommand(cmd, data) { + let players = this.world.players; switch (cmd) { // How many active connections there are - case "/clients": - var length = this.clients.length; + case "/players": + var length = players.length; this.print(`${length}:${CFG.MAX_CONNECTIONS} connected players!`, 33); break; // Exit the server @@ -19,9 +20,9 @@ export function processCommand(cmd, data) { this.kickPlayer(data[1]); break; case "/killall": - var length = this.clients.length; + var length = players.length; this.removeAllPlayers(); - var result = length - this.clients.length; + var result = length - players.length; this.print(`Removed ${result} player${result === 1 ? "": "s"}!`); break; case "/clear": @@ -35,7 +36,7 @@ export function processCommand(cmd, data) { break; case "/save": this.saveAllPlayers(); - var length = this.clients.length; + var length = players.length; this.print(`Saved ${length} player${length === 1 ? "": "s"} into database!`); break; case "/spawn": diff --git a/src/proto.js b/src/proto.js deleted file mode 100644 index e92dc2b..0000000 --- a/src/proto.js +++ /dev/null @@ -1,3 +0,0 @@ -import pogo from "node-pogo-protos"; - -export default pogo; \ No newline at end of file diff --git a/src/request.js b/src/request.js index 1ce7a3b..9702deb 100644 --- a/src/request.js +++ b/src/request.js @@ -1,24 +1,16 @@ import fs from "fs"; import url from "url"; -import proto from "./proto"; import pcrypt from "pcrypt"; import POGOProtos from "pokemongo-protobuf"; import CFG from "../cfg"; -import { - ResponseEnvelope, - AuthTicket, - ShopData -} from "./packets"; - /** + * @param {Player} player * @param {Request} req * @param {Response} res */ -export function routeRequest(req, res) { - - let player = this.getPlayerByRequest(req); +export function routeRequest(player, req, res) { let parsed = url.parse(req.url).pathname; let route = parsed.split("/"); @@ -27,11 +19,11 @@ export function routeRequest(req, res) { switch (route[1]) { case "plfe": case "custom": - if (route[2] === "rpc") this.onRequest(req); + if (route[2] === "rpc") this.onRequest(player, req); break; case "model": // make sure no random dudes can access download - if (!player.authenticated || !player.email_verified) return void 0; + if (!player.authenticated) return void 0; let name = route[2]; if (name && name.length > 1) { let folder = player.isAndroid ? "android/" : "ios/"; @@ -45,14 +37,6 @@ export function routeRequest(req, res) { }); } break; - case "om": - if ( - route[2] === "glm" && - route[3] === "mmap" - ) { - this.print(`Received gmaps request!`, 33); - } - break; default: console.log(`Unknown request url: https://${req.headers.host}${req.url}`); break; @@ -60,43 +44,14 @@ export function routeRequest(req, res) { } -export function parseProtobuf(buffer, path) { - try { - return POGOProtos.parseWithUnknown(buffer, path); - } catch (e) { - this.print(e, 31); - } -} - /** + * @param {Player} player * @param {Request} req */ -export function parseSignature(req) { - let key = pcrypt.decrypt(req.unknown6.unknown2.encrypted_signature); - return ( - POGOProtos.parseWithUnknown(key, "POGOProtos.Networking.Envelopes.Signature") - ); -} - -/** - * @param {Request} req - */ -export function onRequest(req) { - - let player = this.getPlayerByRequest(req); - - // Validate email verification - if (player.authenticated) { - if (!player.email_verified) { - this.print(`${player.email}'s email isnt verified, kicking..`, 31); - this.removePlayer(player); - return void 0; - } - } +export function onRequest(player, req) { let request = this.parseProtobuf(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope"); - - request.requests = request.requests || []; + request.requests = request.requests || []; if (CFG.DEBUG_LOG_REQUESTS) { console.log("#####"); @@ -111,23 +66,15 @@ export function onRequest(req) { } if (!request.requests.length) { - // send shop data - if (request.unknown6 && request.unknown6.request_type === 6) { - let msg = this.envelopResponse(1, [], request, !!request.auth_ticket, true); - player.sendResponse(msg); - } - // otherwise invalid - else { - this.print("Received invalid request!", 31); - return void 0; - } + this.print("Received invalid request!", 31); + return void 0; } this.processRequests(player, request.requests).then((returns) => { if (CFG.DEBUG_DUMP_TRAFFIC) { this.dumpTraffic(request, returns); } - let msg = this.envelopResponse(1, returns, request, player, !!request.auth_ticket, false); + let msg = this.envelopResponse(1, returns, request, player, !!request.auth_ticket); player.sendResponse(msg); }); @@ -139,10 +86,9 @@ export function onRequest(req) { * @param {Request} req * @param {Player} player * @param {Boolean} auth - * @param {Boolean} shop * @return {Buffer} */ -export function envelopResponse(status, returns, req, player, auth, shop) { +export function envelopResponse(status, returns, req, player, auth) { let buffer = req; @@ -163,9 +109,8 @@ export function envelopResponse(status, returns, req, player, auth, shop) { } if (auth) buffer.auth_ticket = AuthTicket(); - if (shop) buffer.unknown6 = [ShopData()]; - if (buffer.unknown6 && !shop) { + if (buffer.unknown6) { buffer.unknown6 = [{ "response_type": 6, "unknown2": { @@ -197,7 +142,7 @@ export function processRequests(player, requests) { let length = requests.length; let body = []; - let loop = (index) => { + const loop = (index) => { this.processResponse(player, requests[index]).then((request) => { body.push(request); if (++index >= length) resolve(body); @@ -209,12 +154,4 @@ export function processRequests(player, requests) { }); -} - -/** - * @param {Request} req - * @return {Boolean} - */ -export function validRequest(req) { - return (true); } \ No newline at end of file diff --git a/src/response.js b/src/response.js index f2a51fc..2a66b78 100644 --- a/src/response.js +++ b/src/response.js @@ -1,40 +1,27 @@ -import proto from "./proto"; import POGOProtos from "pokemongo-protobuf"; import CFG from "../cfg"; import { - GetInventory, - GetHatchedEggs, - CheckAwardedBadges, - DownloadSettings, - DownloadRemoteConfigVersion, - GetPlayerProfile, - ItemTemplates, - GetPlayer, - GetAssetDigest, - GetDownloadUrls, - GetMapObjects, - SfidaActionLog, - FortDetails, - FortSearch, - SetContactSettings, - SetAvatar, - MarkTutorialComplete, - LevelUpRewards, - Encounter, - CatchPokemon, - NicknamePokemon, - UpgradePokemon, - EvolvePokemon, - SetFavoritePokemon, - ClaimCodeName, - CheckChallenge -} from "./packets"; + _toCC, + validUsername +} from "./utils"; -import { _toCC } from "./utils"; - -const REQUEST = proto.Networking.Requests.RequestType; +/** + * @param {Request} req + * @return {Buffer} + */ +export function parseMessage(req) { + let proto = `POGOProtos.Networking.Requests.Messages.${cc}Message`; + if (req.request_message) { + try { + return (this.parseProtobuf(req.request_message, proto)); + } catch (e) { + this.print(`Failed to parse ${cc}: ${e}`, 31); + } + } + return void 0; +} /** * @param {Player} player @@ -46,196 +33,12 @@ export function processResponse(player, req) { let buffer = null; let cc = _toCC(req.request_type); - let msg = null; - let proto = `POGOProtos.Networking.Requests.Messages.${cc}Message`; - - if (req.request_message) { - try { - msg = this.parseProtobuf(req.request_message, proto); - } catch (e) { - this.print(`Failed to parse ${cc}: ${e}`, 31); - } - } + let msg = this.parseMessage(req); return new Promise((resolve) => { try { switch (req.request_type) { - case "GET_PLAYER": - player.authentications++; - if (player.authentications >= 2) { - buffer = GetPlayer(player); - resolve(buffer); - return void 0; - } - this.forwardPlayer(player).then((res) => resolve(res)); - return void 0; - break; - case "CHECK_CHALLENGE": - buffer = CheckChallenge(); - break; - case "GET_HATCHED_EGGS": - buffer = GetHatchedEggs(); - break; - case "GET_INVENTORY": - buffer = GetInventory(player); - break; - case "CHECK_AWARDED_BADGES": - buffer = CheckAwardedBadges(); - break; - case "DOWNLOAD_SETTINGS": - buffer = DownloadSettings(); - break; - case "DOWNLOAD_ITEM_TEMPLATES": - buffer = ItemTemplates(this.master); - break; - case "DOWNLOAD_REMOTE_CONFIG_VERSION": - buffer = DownloadRemoteConfigVersion(msg); - break; - case "GET_ASSET_DIGEST": - buffer = GetAssetDigest(player); - break; - case "GET_PLAYER_PROFILE": - buffer = GetPlayerProfile(); - break; - case "GET_MAP_OBJECTS": - player.updatePosition(msg); - buffer = GetMapObjects(player, this.wild_pokemons, msg); - this.savePlayer(player).then(() => { - resolve(buffer); - }); - return void 0; - break; - case "GET_DOWNLOAD_URLS": - GetDownloadUrls(player.asset_digest.decode, CFG.LOCAL_IP || this.getLocalIPv4(), msg).then((res) => { - resolve(res); - }); - return void 0; - break; - case "SET_AVATAR": - player.updateAvatar(msg); - this.emit("updatePlayerAvatar", player); - buffer = SetAvatar(player); - this.savePlayer(player).then(() => { - resolve(buffer); - }); - return void 0; - break; - case "SFIDA_ACTION_LOG": - buffer = SfidaActionLog(); - break; - case "MARK_TUTORIAL_COMPLETE": - buffer = MarkTutorialComplete(player, msg); - this.savePlayer(player).then(() => { - resolve(buffer); - }); - return void 0; - break; - case "ENCOUNTER_TUTORIAL_COMPLETE": - let starters = ["BULBASAUR", "CHARMANDER", "SQUIRTLE"]; - // Make sure the catched pokemon is valid - if (starters.indexOf(msg.pokemon_id) === -1) { - if (player.tutorial_state.indexOf("POKEMON_CAPTURE") === -1) { - player.tutorial_state.push("POKEMON_CAPTURE"); - } - let pkmn = { - pokemon_id: msg.pokemon_id, - cp: 15, - stamina: 10, - stamina_max: 10, - move_1: "TACKLE", - move_2: "TACKLE", - height_m: 0.20962005257606506, - weight_kg: 0.3212273120880127, - individual_attack: 7, - individual_defense: 7, - individual_stamina: 3, - cp_multiplier: 0.16639786958694458 - }; - player.party.push(pkmn); - buffer = POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.EncounterTutorialCompleteResponse"); - } - break; - case "CLAIM_CODENAME": - buffer = ClaimCodeName(msg, player); - this.savePlayer(player).then(() => { - resolve(buffer); - }); - return void 0; - break; - case "LEVEL_UP_REWARDS": - buffer = LevelUpRewards(); - break; - case "FORT_DETAILS": - buffer = FortDetails(msg); - break; - case "FORT_SEARCH": - buffer = FortSearch(player); - this.updateUserItems(player).then(() => { - resolve(buffer); - }); - return void 0; - break; - case "SET_CONTACT_SETTINGS": - player.updateContactSettings(msg); - buffer = SetContactSettings(player); - this.savePlayer(player).then(() => { - resolve(buffer); - }); - return void 0; - break; - case "ENCOUNTER": - buffer = Encounter(this.getEncounterPkmn(msg).pkmn, msg); - break; - case "CATCH_POKEMON": - let encounter = this.getEncounterPkmn(msg); - let result = CatchPokemon(encounter.pkmn, player, msg); - // save pkmn into player party - if (result.status !== "CATCH_MISSED") { - result.pkmn.owner_id = player.owner_id; - player.exp += 100; - player.stardust += 100; - this.wild_pokemons.splice(encounter.index, 1); - this.createOwnedPokemon(result.pkmn).then(() => { - // make sure it got saved, also get db id - this.getQueryByColumnFromTable("creation_time_ms", result.pkmn.creation_time_ms, CFG.MYSQL_OWNED_PKMN_TABLE).then((query) => { - player.party.push(query[0]); - result.buffer.captured_pokemon_id = query[0].id; - result = POGOProtos.serialize(result.buffer, "POGOProtos.Networking.Responses.CatchPokemonResponse"); - resolve(result); - }); - }); - } - else { - resolve(POGOProtos.serialize(result, "POGOProtos.Networking.Responses.CatchPokemonResponse")); - } - return void 0; - break; - case "NICKNAME_POKEMON": - buffer = NicknamePokemon(msg); - break; - case "UPGRADE_POKEMON": - buffer = UpgradePokemon(msg); - break; - case "EVOLVE_POKEMON": - buffer = EvolvePokemon(msg); - break; - case "SET_FAVORITE_POKEMON": - buffer = SetFavoritePokemon(player, msg); - break; - case "RELEASE_POKEMON": - buffer = { - result: "SUCCESS", - candy_awarded: 5 - }; - let id = msg.pokemon_id << 0; - this.deleteOwnedPokemon(id).then(() => { - player.deletePkmnFromParty(id); - buffer = POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.ReleasePokemonResponse"); - resolve(buffer); - }); - return void 0; - break; default: this.print(`Unknown request: ${req.request_type}`, 31); break; @@ -248,25 +51,4 @@ export function processResponse(player, req) { }); -} - -export function getEncounterPkmn(req) { - - let ii = 0; - let length = this.wild_pokemons.length; - - let pkmn = null; - - for (; ii < length; ++ii) { - pkmn = this.wild_pokemons[ii]; - if (pkmn.encounter_id === parseInt(req.encounter_id)) { - break; - } - }; - - return ({ - pkmn: pkmn, - index: ii - }); - } \ No newline at end of file diff --git a/src/setup.js b/src/setup.js index 07faae1..498978a 100644 --- a/src/setup.js +++ b/src/setup.js @@ -1,11 +1,14 @@ import fs from "fs"; import fse from "fs-extra"; import pogo from "pogo-asset-downloader"; -import proto from "./proto"; import POGOProtos from "pokemongo-protobuf"; import CFG from "../cfg"; +import * as master from "./master"; + +import GameMaster from "./models/GameMaster"; + import { capitalize, idToPkmnBundleName @@ -13,7 +16,7 @@ import { export function setup() { - let isFirstRun = !this.directoryExists(CFG.DUMP_ASSET_PATH); + let isFirstRun = !this.fileExists(CFG.DUMP_ASSET_PATH); if (isFirstRun) { this.print("Required assets are missing! Preparing dump session..", 31); @@ -30,7 +33,9 @@ export function setup() { this.print(`Downloaded assets are valid! Proceeding..`); - this.master = POGOProtos.serialize(this.parseGameMaster(), "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse"); + master.GAME_MASTER = this.parseGameMaster(); + + this.master = POGOProtos.serialize(fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master"), "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse"); this.setupDatabaseConnection().then(() => { if (CFG.PORT < 1) { @@ -40,7 +45,7 @@ export function setup() { this.socket = this.createHTTPServer(); setTimeout(this::this.cycle, 1); let localIPv4 = this.getLocalIPv4(); - this.print(`Server running at ${localIPv4}:${CFG.PORT}`); + this.print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33); this.emit("ready", void 0); }); @@ -124,6 +129,7 @@ export function parseGameMaster() { try { let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master"); master = this.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse"); + let Master = new GameMaster(master); } catch (e) { this.print(e, 31); } diff --git a/src/utils.js b/src/utils.js index b8585cc..0bfe004 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,3 @@ -import Long from "long"; -import proto from "./proto"; - import CFG from "../cfg"; /** @@ -20,15 +17,6 @@ export function inherit(cls, prot) { } -/** - * @param {Buffer} body - */ -export function decodeRequestEnvelope(body) { - return ( - proto.Networking.Envelopes.RequestEnvelope.decode(body) - ); -} - /** * http://stackoverflow.com/a/7616484/3367904 * @param {String} str @@ -45,18 +33,6 @@ export function getHashCodeFrom(str) { return hash; }; -/** - * @param {Long} long - * @return {Number} - */ -export function decodeLong(long) { - - let value = Long.fromBits(long.high, long.low, !!long.unsigned); - - return (parseInt(value.toString())); - -} - /** * @return {Number} */ @@ -96,4 +72,11 @@ export function capitalize(str) { return ( str[0].toUpperCase() + str.slice(1) ); +} + +let rx_username = /[^a-z\d]/i; +export function validUsername(str) { + return ( + !!(rx_username.test(str)) + ); } \ No newline at end of file diff --git a/update.js b/update.js deleted file mode 100644 index 0fada23..0000000 --- a/update.js +++ /dev/null @@ -1,84 +0,0 @@ -"use strict"; - -var fs = require("fs"); -var fse = require("fs-extra"); -var git = require("nodegit"); -var dirTree = require("directory-tree"); - -let tmpDir = "./tmp"; -let cloneDir = JSON.parse(fs.readFileSync("./package.json")).repository.url; - -let ignore = [".git", "cfg.js", "updater.js"]; - -function skip() { - return new Promise((resolve) => { - fse.removeSync(tmpDir); - console.log(`Skipped update to version ${newVersion}`); - resolve(); - }); -} - -function updateProject() { - return new Promise((resolve) => { - let newFiles = dirTree("./tmp"); - recurse(newFiles.children); - resolve(); - }); -} - -function recurse(parent) { - for (let key in parent) { - let name = parent[key].name; - if (ignore.indexOf(name) > -1) continue; - let isDir = parent[key].hasOwnProperty("children"); - try { - if (!isDir) { - fse.outputFileSync(parent[key].path.substring(4), fs.readFileSync(parent[key].path)); - } - else { - recurse(parent[key].children); - } - } catch (e) { console.log(e); } - }; -} - -(function() { - return new Promise((resolve) => { - console.log("Preparing to update.."); - setTimeout(() => { - fse.removeSync(tmpDir); - let currentVersion = JSON.parse(fs.readFileSync("./package.json")).version; - console.log(`Your current version is ${currentVersion}!`); - setTimeout(() => { - console.log("Fetching latest version.."); - git.Clone(cloneDir, tmpDir).then((res, rofl) => { - let newVersion = null; - try { - newVersion = JSON.parse(fs.readFileSync(`${tmpDir}/package.json`)).version; - } catch (e) { - console.log("Version check failed!"); - return resolve(); - } - if (currentVersion === newVersion) { - console.log(`You are already running the latest version ${currentVersion}!`); - skip().then(resolve); - return void 0; - } - else if (currentVersion > newVersion) { - console.log(`Your version ${currentVersion} is newer than ${newVersion}!`); - skip().then(resolve); - return void 0; - } - console.log(`Latest version is ${newVersion}`); - console.log(`Updating to version ${newVersion}`); - updateProject().then(() => { - fse.removeSync(tmpDir); - console.log("Update successfully completed, please restart!"); - resolve(); - }); - }); - }, 2e3); - - }, 1e3); - }); -})(); \ No newline at end of file From c66efe50b479c715f523968b8f0ec9f80b76dc2e Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 27 Aug 2016 13:20:28 +0200 Subject: [PATCH 2/6] Updates - Added api support - Improved first run setup detection - Better db instance access - Cleaner routing --- .save | 1 + cfg.js.example | 4 +- package.json | 2 + src/api.js | 156 +++++++++++++++++++++++++++++-------- src/db/create.js | 4 +- src/db/get.js | 4 +- src/db/index.js | 4 +- src/http.js | 4 +- src/index.js | 43 +++++----- src/models/Player/index.js | 4 +- src/models/World/index.js | 24 +++--- src/request.js | 62 +++++++++++---- src/setup.js | 10 +-- 13 files changed, 221 insertions(+), 101 deletions(-) create mode 100644 .save diff --git a/.save b/.save new file mode 100644 index 0000000..d36709e --- /dev/null +++ b/.save @@ -0,0 +1 @@ +{"isFirstRun":false,"allowedApiHosts":["localhost"],"loginDetails":{"username":"root","password":"13377"}} \ No newline at end of file diff --git a/cfg.js.example b/cfg.js.example index d90ab73..15ddac0 100644 --- a/cfg.js.example +++ b/cfg.js.example @@ -6,8 +6,8 @@ export default { // show greeting GREET: true, - // emit api calls - ENABLE_API: false, + // allow api calls + ENABLE_API: true, // Server settings MAX_CONNECTIONS: 64, diff --git a/package.json b/package.json index d089a87..15cf40b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "babel-cli": "^6.11.4", "babel-preset-es2015": "^6.13.1", "babel-preset-stage-0": "^6.5.0", + "url": "^0.11.0", + "prompt": "^1.0.0", "fs-extra": "^0.30.0", "mysql": "^2.11.1", "jwt-decode": "^2.1.0", diff --git a/src/api.js b/src/api.js index 121f8a4..47640a9 100644 --- a/src/api.js +++ b/src/api.js @@ -1,44 +1,138 @@ +import fs from "fs"; +import url from "url"; +import prompt from "prompt"; + import CFG from "../cfg"; -export function ready(e) { - if (e) console.log(e); - else console.log("Ready!"); +prompt.start({ + message: " ", + delimiter: " " +}); + +export function processApiCall(req, res, route) { + + let save = JSON.parse(fs.readFileSync(".save", "utf8")); + let allowedHosts = save.allowedApiHosts; + + let hoster = url.parse(req.headers.referer).host; + + if (!(allowedHosts.indexOf(hoster) > -1)) { + this.grantApiAccess(req, res, route); + return void 0; + } + + let raw = req.body.toString(); + let json = null; + + try { + json = JSON.parse(raw); + } catch (e) { + this.print(e, 31); + this.answerApiCall(res, ""); + return void 0; + } + + if (this.isApiCall(json)) { + json.host = hoster; + if (json.action === "login") { + let result = this["api_login"](json); + this.answerApiCall(res, JSON.stringify(result)); + } + else { + if (this.apiClients[hoster]) { + let result = this["api_" + json.action](json); + this.answerApiCall(res, JSON.stringify(result)); + } + else { + this.print(`${hoster} isnt logged in! Kicking..`, 31); + } + } + } + else { + if (json.action === "init") { + this.answerApiCall(res, JSON.stringify({ success: true })); + } + } + } -/** - * @param {String} msg - * @param {Number} color - * @param {Boolean} nl - */ -export function print(msg, color, newline) { - color = Number.isInteger(color) ? color : CFG.DEFAULT_CONSOLE_COLOR; - process.stdout.write(`[Console] \x1b[${color};1m${msg}\x1b[0m${newline === void 0 ? "\n" : ""}`); +export function grantApiAccess(req, res, route) { + + let save = JSON.parse(fs.readFileSync(".save", "utf8")); + + let hoster = url.parse(req.headers.referer).host; + + let msg = `[Console] \x1b[33mGrant API access to ${hoster}?\x1b[0m`; + + prompt.get([{ name: "grant", required: true, description: msg }], (e, result) => { + if (result.grant === "y" || result.grant === "yes") { + save.allowedApiHosts.push(hoster); + fs.writeFileSync(".save", JSON.stringify(save), "utf8"); + this.print(`Successfully added ${hoster} to allowed API hosts!`); + this.processApiCall(req, res, route); + } + else { + this.print(`Denied API access for ${hoster}`, 31); + this.answerApiCall(res, ""); + } + }); + } -/** - * @param {Player} player - */ -export function registerPlayer(player) { - console.log(player.username + " registered!"); +export function answerApiCall(res, data) { + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); + res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type"); + res.setHeader("Access-Control-Allow-Credentials", true); + res.end(data); } -/** - * @param {Player} player - */ -export function loginPlayer(player) { - console.log(player.username + " logged in!"); +export function api_login(data) { + + if (typeof data !== "object") return void 0; + + let save = JSON.parse(fs.readFileSync(".save", "utf8")); + + let success = false; + + let username = save.loginDetails.username; + let password = save.loginDetails.password; + + if ( + username === data.username && + password === data.password + ) { + success = true; + if (!this.apiClients[data.host]) { + this.print(`API access for ${data.host} granted!`); + } + this.print(`${data.host} logged in!`, 36); + this.apiClients[data.host] = { + timestamp: +new Date() + }; + } + return ({ + success: success + }); } -/** - * @param {Player} player - */ -export function killPlayer(player) { - console.log(player.username + " killed!"); +export function api_getConnectedPlayers() { + return ({ + connected_players: this.world.players.length + }); } -/** - * @param {Player} player - */ -export function updatePlayerAvatar(player) { - console.log("Updated avatar of " + player.username + "!"); +export function api_getServerVersion() { + return ({ + version: CFG.VERSION + }); +} + +export function api_spawnPkmnToPlayer(data) { + let name = String(data.player); + let pkmn = String(data.pkmn).toUpperCase(); + this.print(`Spawned 1x ${pkmn}'s to ${name}!`); + return ({ + success: true + }); } \ No newline at end of file diff --git a/src/db/create.js b/src/db/create.js index dd2ae45..a3fa4c9 100644 --- a/src/db/create.js +++ b/src/db/create.js @@ -3,7 +3,7 @@ import CFG from "../../cfg"; export function createTableIfNotExists(name) { return new Promise((resolve) => { - this.db.instance.query(`SHOW TABLES LIKE '${name}';`, (e, rows, fields) => { + this.db.query(`SHOW TABLES LIKE '${name}';`, (e, rows, fields) => { if (e) console.log(e); else { // exists @@ -39,7 +39,7 @@ export function createTable(name) { `; return new Promise((resolve) => { - this.db.instance.query(query, (e, rows) => { + this.db.query(query, (e, rows) => { if (e) console.log(e); else resolve(); }); diff --git a/src/db/get.js b/src/db/get.js index 3d9b9af..93fbfd4 100644 --- a/src/db/get.js +++ b/src/db/get.js @@ -7,7 +7,7 @@ import CFG from "../../cfg"; */ export function getQueryByColumnFromTable(column, value, table) { return new Promise((resolve) => { - this.db.instance.query(`SELECT * FROM ${table} WHERE ${column}=?`, [value], (e, rows) => { + this.db.query(`SELECT * FROM ${table} WHERE ${column}=?`, [value], (e, rows) => { if (e) console.log(e); if (rows && rows.length) resolve(rows); else resolve(void 0); @@ -22,7 +22,7 @@ export function getQueryByColumnFromTable(column, value, table) { */ export function deleteQueryByColumnFromTable(column, value, table) { return new Promise((resolve) => { - this.db.instance.query(`DELETE FROM ${table} WHERE ${column}=?`, [value], (e, rows) => { + this.db.query(`DELETE FROM ${table} WHERE ${column}=?`, [value], (e, rows) => { if (e) console.log(e); else resolve(void 0); }); diff --git a/src/db/index.js b/src/db/index.js index 2e6f41b..adcc0a5 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -19,7 +19,7 @@ export function setupDatabaseConnection() { this.retry("Retrying again in ", () => this.setupDatabaseConnection().then(resolve), 5); return void 0; } - this.db.instance = connection; + this.db = connection; this.createTableIfNotExists(CFG.MYSQL_USERS_TABLE).then(() => { this.createTableIfNotExists(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => { this.print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`); @@ -39,7 +39,7 @@ export function setupDatabaseConnection() { * @param {Function} resolve */ export function closeConnection(resolve) { - this.db.instance.end(() => { + this.db.end(() => { resolve(); }); } \ No newline at end of file diff --git a/src/http.js b/src/http.js index 4eede73..ffcc1ac 100644 --- a/src/http.js +++ b/src/http.js @@ -18,9 +18,7 @@ export function createHTTPServer() { req.on("end", () => { let buffer = Buffer.concat(chunks); req.body = buffer; - this.world.getPlayerByRequest(req, res).then((player) => { - this.routeRequest(player, req, res); - }); + this.routeRequest(req, res); }); }); server.listen(CFG.PORT); diff --git a/src/index.js b/src/index.js index 4226ca2..af63d5a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ import fs from "fs"; import os from "os"; -import EventEmitter from "events"; import POGOProtos from "pokemongo-protobuf"; import { @@ -30,23 +29,20 @@ const greetMessage = fs.readFileSync(".greet", "utf8"); /** * @class GameServer */ -export default class GameServer extends EventEmitter { +export default class GameServer { /** @constructor */ constructor() { - super(null); - this.STATES = { PAUSE: false, DEBUG: false, CRASH: false }; - this.db = { - instance: null, - collections: {} - }; + this.db = null; + + this.apiClients = {}; this.assets = {}; this.master = null; @@ -61,8 +57,6 @@ export default class GameServer extends EventEmitter { this.timeoutTick = 0; this.passedTicks = 0; - this.initAPI(); - if (CFG.GREET) this.greet(); this.print(`Booting Server v${require("../package.json").version}-dev`, 33); @@ -73,25 +67,25 @@ export default class GameServer extends EventEmitter { } - initAPI() { - if (CFG.ENABLE_API) { - for (let key in _api) { - this.on(key, _api[key]); - }; - } - // make sure we still have our print fn - else { - this.on("print", _api["print"]); - } - } - /** * @param {String} msg * @param {Number} color * @param {Boolean} nl */ - print(msg, color, nl) { - this.emit("print", msg, color, nl); + print(msg, color, newline) { + color = Number.isInteger(color) ? color : CFG.DEFAULT_CONSOLE_COLOR; + process.stdout.write(`[Console] \x1b[${color};1m${msg}\x1b[0m${newline === void 0 ? "\n" : ""}`); + } + + /** + * @param {Object} obj + * @return {Boolean} + */ + isApiCall(call) { + let action = String(call.action); + return ( + "api_" + action in _api + ); } /** @@ -163,6 +157,7 @@ export default class GameServer extends EventEmitter { } +inherit(GameServer, _api); inherit(GameServer, _dump); inherit(GameServer, _http); inherit(GameServer, _setup); diff --git a/src/models/Player/index.js b/src/models/Player/index.js index 7a98157..cd6fe7a 100644 --- a/src/models/Player/index.js +++ b/src/models/Player/index.js @@ -20,14 +20,14 @@ export default class Player extends MapObject { this.response = obj.response; this.authenticated = 0; - +/* this.bag = new Bag(this); this.info = new Info(this); this.pary = new Party(this); this.avatar = new Avatar(this); this.pokedex = new Pokedex(this); this.tutorial = new Tutorial(this); - +*/ } } \ No newline at end of file diff --git a/src/models/World/index.js b/src/models/World/index.js index 017a75a..b1ef05b 100644 --- a/src/models/World/index.js +++ b/src/models/World/index.js @@ -8,7 +8,11 @@ import CFG from "../../../cfg"; export default class World { /** @constructor */ - constructor() { + constructor(instance) { + + this.instance = instance; + + this.db = this.instance.db; this.players = []; @@ -31,16 +35,14 @@ export default class World { let player = null; - return new Promise((resolve) => { - if (this.playerAlreadyConnected(req)) { - player = this.getPlayerByIP(req.headers.host); - resolve(player); - } - else { - this.addPlayer(req, res); - resolve(player); - } - }); + if (this.playerAlreadyConnected(req)) { + player = this.getPlayerByIP(req.headers.host); + return (player); + } + else { + this.addPlayer(req, res); + return (player); + } } diff --git a/src/request.js b/src/request.js index 9702deb..60b70b0 100644 --- a/src/request.js +++ b/src/request.js @@ -6,11 +6,10 @@ import POGOProtos from "pokemongo-protobuf"; import CFG from "../cfg"; /** - * @param {Player} player * @param {Request} req * @param {Response} res */ -export function routeRequest(player, req, res) { +export function routeRequest(req, res) { let parsed = url.parse(req.url).pathname; let route = parsed.split("/"); @@ -19,31 +18,60 @@ export function routeRequest(player, req, res) { switch (route[1]) { case "plfe": case "custom": - if (route[2] === "rpc") this.onRequest(player, req); + this.processRpcRequest(req, res, route); break; case "model": - // make sure no random dudes can access download - if (!player.authenticated) return void 0; - let name = route[2]; - if (name && name.length > 1) { - let folder = player.isAndroid ? "android/" : "ios/"; - fs.readFile("data/" + folder + name, (error, data) => { - if (error) { - this.print(`Error file resolving model ${name}:` + error, 31); - return void 0; - } - this.print(`Sent ${name} to ${player.email}`, 36); - res.end(data); - }); + this.processModelRequest(req, res, route); + break; + case "api": + if (!CFG.ENABLE_API) return void 0; + if (req.method === "POST") { + this.processApiCall(req, res, route); } break; default: - console.log(`Unknown request url: https://${req.headers.host}${req.url}`); + console.log(`Unknown request url: https://${host}${req.url}`); break; }; } +/** + * @param {Request} req + * @param {Response} res + * @param {Array} route + */ +export function processRpcRequest(req, res, route) { + let player = this.world.getPlayerByRequest(req, res); + if (route[2] === "rpc") this.onRequest(player, req); +} + +/** + * @param {Request} req + * @param {Response} res + * @param {Array} route + */ +export function processModelRequest(req, res, route) { + + let player = this.world.getPlayerByRequest(req, res); + + // make sure no random dudes can access download + if (!player.authenticated) return void 0; + let name = route[2]; + if (name && name.length > 1) { + let folder = player.isAndroid ? "android/" : "ios/"; + fs.readFile("data/" + folder + name, (error, data) => { + if (error) { + this.print(`Error file resolving model ${name}:` + error, 31); + return void 0; + } + this.print(`Sent ${name} to ${player.email}`, 36); + res.end(data); + }); + } + +} + /** * @param {Player} player * @param {Request} req diff --git a/src/setup.js b/src/setup.js index 498978a..e14c153 100644 --- a/src/setup.js +++ b/src/setup.js @@ -16,12 +16,14 @@ import { export function setup() { - let isFirstRun = !this.fileExists(CFG.DUMP_ASSET_PATH); + let save = JSON.parse(fs.readFileSync(".save", "utf8")); - if (isFirstRun) { - this.print("Required assets are missing! Preparing dump session..", 31); + if (save.isFirstRun) { + this.print("Required assets are missing! Preparing dump session..", 33); setTimeout(() => { this.onFirstRun(() => { + save.isFirstRun = false; + fs.writeFileSync(".save", JSON.stringify(save), "utf8"); this.setup(); }); }, 1e3); @@ -46,13 +48,11 @@ export function setup() { setTimeout(this::this.cycle, 1); let localIPv4 = this.getLocalIPv4(); this.print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33); - this.emit("ready", void 0); }); }).catch((e) => { //fse.removeSync(CFG.DUMP_ASSET_PATH); this.print("Error: " + e + " was not found!", 31); - this.emit("ready", e); }); } From dbde45fe73d7602cdb65f447c615a7c1a89053eb Mon Sep 17 00:00:00 2001 From: Felix Date: Sat, 27 Aug 2016 21:07:40 +0200 Subject: [PATCH 3/6] Update - Global shared print method - API hearbeat and reconnecting feature - Serialize avatar - Basic player authentication - New packet structure for classes - Safer url routing --- src/api.js | 23 ++-- src/cycle.js | 5 +- src/db/create.js | 4 +- src/db/index.js | 7 +- src/dump.js | 3 +- src/http.js | 9 +- src/index.js | 17 +-- src/models/Player/Avatar/index.js | 14 ++ src/models/Player/Bag/CandyBag/index.js | 2 +- src/models/Player/Tutorial/index.js | 47 +++++++ src/models/Player/index.js | 127 +++++++++++++++++- src/models/Player/packets/GetAuthTicket.js | 20 +++ src/models/Player/packets/GetInventory.js | 2 +- src/models/Player/packets/GetPlayer.js | 22 ++- src/models/Player/packets/GetPlayerProfile.js | 2 +- src/models/Player/packets/index.js | 4 + src/models/World/index.js | 44 ++++-- src/print.js | 11 ++ src/process.js | 15 ++- src/request.js | 34 ++--- src/response.js | 28 +++- src/setup.js | 27 ++-- 22 files changed, 373 insertions(+), 94 deletions(-) create mode 100644 src/models/Player/Tutorial/index.js create mode 100644 src/models/Player/packets/GetAuthTicket.js create mode 100644 src/models/Player/packets/index.js create mode 100644 src/print.js diff --git a/src/api.js b/src/api.js index 47640a9..7bf6a1a 100644 --- a/src/api.js +++ b/src/api.js @@ -2,6 +2,7 @@ import fs from "fs"; import url from "url"; import prompt from "prompt"; +import print from "./print"; import CFG from "../cfg"; prompt.start({ @@ -27,7 +28,7 @@ export function processApiCall(req, res, route) { try { json = JSON.parse(raw); } catch (e) { - this.print(e, 31); + print(e, 31); this.answerApiCall(res, ""); return void 0; } @@ -44,7 +45,7 @@ export function processApiCall(req, res, route) { this.answerApiCall(res, JSON.stringify(result)); } else { - this.print(`${hoster} isnt logged in! Kicking..`, 31); + print(`${hoster} isnt logged in! Kicking..`, 31); } } } @@ -68,11 +69,11 @@ export function grantApiAccess(req, res, route) { if (result.grant === "y" || result.grant === "yes") { save.allowedApiHosts.push(hoster); fs.writeFileSync(".save", JSON.stringify(save), "utf8"); - this.print(`Successfully added ${hoster} to allowed API hosts!`); + print(`Successfully added ${hoster} to allowed API hosts!`); this.processApiCall(req, res, route); } else { - this.print(`Denied API access for ${hoster}`, 31); + print(`Denied API access for ${hoster}`, 31); this.answerApiCall(res, ""); } }); @@ -104,9 +105,9 @@ export function api_login(data) { ) { success = true; if (!this.apiClients[data.host]) { - this.print(`API access for ${data.host} granted!`); + print(`API access for ${data.host} granted!`); } - this.print(`${data.host} logged in!`, 36); + print(`${data.host} logged in!`, 36); this.apiClients[data.host] = { timestamp: +new Date() }; @@ -116,9 +117,15 @@ export function api_login(data) { }); } +export function api_heartBeat() { + return ({ + timestamp: +new Date() + }); +} + export function api_getConnectedPlayers() { return ({ - connected_players: this.world.players.length + connected_players: this.world.connectedPlayers }); } @@ -131,7 +138,7 @@ export function api_getServerVersion() { export function api_spawnPkmnToPlayer(data) { let name = String(data.player); let pkmn = String(data.pkmn).toUpperCase(); - this.print(`Spawned 1x ${pkmn}'s to ${name}!`); + print(`Spawned 1x ${pkmn}'s to ${name}!`); return ({ success: true }); diff --git a/src/cycle.js b/src/cycle.js index abdc078..b10b86c 100644 --- a/src/cycle.js +++ b/src/cycle.js @@ -1,3 +1,4 @@ +import print from "./print"; import CFG from "../cfg"; export function startCycle() { @@ -61,12 +62,12 @@ export function playerTimeoutTick() { let players = this.world.players; let ii = 0; - let length = players.length; + let length = this.world.connectedPlayers; for (; ii < length; ++ii) { player = players[ii]; if (this.time - player.timeout >= maxTimeout) { - this.print(`${player.remoteAddress} timed out`, 34); + print(`${player.remoteAddress} timed out`, 34); this.savePlayer(player); this.removePlayer(player); } diff --git a/src/db/create.js b/src/db/create.js index a3fa4c9..7251a7a 100644 --- a/src/db/create.js +++ b/src/db/create.js @@ -1,4 +1,6 @@ import fs from "fs"; + +import print from "../print"; import CFG from "../../cfg"; export function createTableIfNotExists(name) { @@ -30,7 +32,7 @@ export function createTables() { export function createTable(name) { - this.print(`Creating table ${name}`, 36); + print(`Creating table ${name}`, 36); let query = ` CREATE TABLE IF NOT EXISTS ${name} ( diff --git a/src/db/index.js b/src/db/index.js index adcc0a5..772414b 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -1,5 +1,6 @@ import mysql from "mysql"; +import print from "../print"; import CFG from "../../cfg"; export function setupDatabaseConnection() { @@ -15,20 +16,20 @@ export function setupDatabaseConnection() { return new Promise((resolve) => { connection.connect((error) => { if (error) { - this.print("MySQL " + error, 31); + print("MySQL " + error, 31); this.retry("Retrying again in ", () => this.setupDatabaseConnection().then(resolve), 5); return void 0; } this.db = connection; this.createTableIfNotExists(CFG.MYSQL_USERS_TABLE).then(() => { this.createTableIfNotExists(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => { - this.print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`); + print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`); resolve(); }); }); }); connection.on("error", (error) => { - this.print("MySQL " + error, 31); + print("MySQL " + error, 31); this.retry("Trying to reconnect in ", () => this.setupDatabaseConnection().then(resolve), 5); }); }); diff --git a/src/dump.js b/src/dump.js index 9f85978..e40811c 100644 --- a/src/dump.js +++ b/src/dump.js @@ -1,6 +1,7 @@ import fse from "fs-extra"; import POGOProtos from "pokemongo-protobuf"; +import print from "./print"; import CFG from "../cfg"; /** @@ -69,7 +70,7 @@ export function dumpTraffic(req, res) { let decoded = JSON.stringify(out, null, 2); fse.outputFileSync(CFG.DEBUG_DUMP_PATH + Date.now(), decoded); } catch (e) { - this.print("Dump traffic: " + e, 31); + print("Dump traffic: " + e, 31); } } \ No newline at end of file diff --git a/src/http.js b/src/http.js index ffcc1ac..83e77ae 100644 --- a/src/http.js +++ b/src/http.js @@ -1,5 +1,6 @@ import http from "http"; +import print from "./print"; import CFG from "../cfg"; /** @@ -8,7 +9,7 @@ import CFG from "../cfg"; export function createHTTPServer() { let server = http.createServer((req, res) => { if (this.world.isFull()) { - this.print(`Server is full! Refused ${req.headers.host}`, 31); + print(`Server is full! Refused ${req.headers.host}`, 31); return void 0; } let chunks = []; @@ -27,10 +28,10 @@ export function createHTTPServer() { export function shutdown() { this.socket.close(() => { - this.print("Closed http server!", 33); + print("Closed http server!", 33); this.closeConnection(() => { - this.print("Closed database connection!", 33); - this.print("Server shutdown!", 31); + print("Closed database connection!", 33); + print("Server shutdown!", 31); setTimeout(() => process.exit(1), 2e3); }); }); diff --git a/src/index.js b/src/index.js index af63d5a..bf81bcd 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ import { _toCC } from "./utils"; +import print from "./print"; import CFG from "../cfg"; import World from "./models/World"; @@ -59,7 +60,7 @@ export default class GameServer { if (CFG.GREET) this.greet(); - this.print(`Booting Server v${require("../package.json").version}-dev`, 33); + print(`Booting Server v${require("../package.json").version}-dev`, 33); this.world = new World(this); @@ -67,16 +68,6 @@ export default class GameServer { } - /** - * @param {String} msg - * @param {Number} color - * @param {Boolean} nl - */ - print(msg, color, newline) { - color = Number.isInteger(color) ? color : CFG.DEFAULT_CONSOLE_COLOR; - process.stdout.write(`[Console] \x1b[${color};1m${msg}\x1b[0m${newline === void 0 ? "\n" : ""}`); - } - /** * @param {Object} obj * @return {Boolean} @@ -96,7 +87,7 @@ export default class GameServer { retry(msg, fn, timer) { process.stdout.clearLine(); process.stdout.cursorTo(0); - this.print(`${msg}${timer}s`, 33, true); + print(`${msg}${timer}s`, 33, true); if (timer >= 1) setTimeout(() => this.retry(msg, fn, --timer), 1e3); else { process.stdout.write("\n"); @@ -124,7 +115,7 @@ export default class GameServer { try { return POGOProtos.parseWithUnknown(buffer, schema); } catch (e) { - this.print(e, 31); + print(e, 31); } } diff --git a/src/models/Player/Avatar/index.js b/src/models/Player/Avatar/index.js index 034b501..3fb1297 100644 --- a/src/models/Player/Avatar/index.js +++ b/src/models/Player/Avatar/index.js @@ -120,4 +120,18 @@ export default class Avatar { } } + serialize() { + return ({ + skin: this.skin, + hair: this.hair, + shirt: this.shirt, + pants: this.pants, + hat: this.hat, + shoes: this.shoes, + eyes: this.eyes, + gender: this.gender, + backpack: this.backpack + }); + } + } \ No newline at end of file diff --git a/src/models/Player/Bag/CandyBag/index.js b/src/models/Player/Bag/CandyBag/index.js index 9f095e4..9b79ff4 100644 --- a/src/models/Player/Bag/CandyBag/index.js +++ b/src/models/Player/Bag/CandyBag/index.js @@ -51,7 +51,7 @@ export default class CandyBag { /** * @return {String} */ - serializeCandies() { + serialize() { let str = ""; let candies = this.candies; diff --git a/src/models/Player/Tutorial/index.js b/src/models/Player/Tutorial/index.js new file mode 100644 index 0000000..5512d30 --- /dev/null +++ b/src/models/Player/Tutorial/index.js @@ -0,0 +1,47 @@ +import Candy from "./Candy"; + +/** + * @class Tutorial + */ +export default class Tutorial { + + /** @constructor */ + constructor() { + + this.states = []; + + this.LEGAL_SCREEN = 0; + this.AVATAR_SELECTION = 1; + this.ACCOUNT_CREATION = 2; + this.POKEMON_CAPTURE = 3; + this.NAME_SELECTION = 4; + this.POKEMON_BERRY = 5; + this.USE_ITEM = 6; + this.FIRST_TIME_EXPERIENCE_COMPLETE = 7; + this.POKESTOP_TUTORIAL = 8; + this.GYM_TUTORIAL = 9; + + } + + passLegalScreen() { + + } + + passAvatarSelection() { + + } + + passAccountCreation() { + + } + + /** + * @return {Array} + */ + serialize() { + return ( + this.states + ); + } + +} \ No newline at end of file diff --git a/src/models/Player/index.js b/src/models/Player/index.js index cd6fe7a..766e116 100644 --- a/src/models/Player/index.js +++ b/src/models/Player/index.js @@ -1,5 +1,14 @@ import Avatar from "./Avatar"; import MapObject from "../World/MapObject"; +import POGOProtos from "pokemongo-protobuf"; +import jwtDecode from "jwt-decode"; + +import print from "../../print"; +import CFG from "../../../cfg"; + +import * as _packets from "./packets"; + +import { inherit } from "../../utils"; import { GAME_MASTER } from "../../master"; @@ -16,10 +25,30 @@ export default class Player extends MapObject { super(null); - this.request = obj.request; - this.response = obj.response; + this.world = obj.world; + + this._team = 0; + + this.username = "unknown"; + + this._email = null; + this.email_verified = false; + + this.isPTCAccount = false; + this.isGoogleAccount = false; + + this.hasSignature = false; + + this.authenticated = false; + + this.request = null; + this.response = null; + + this.remoteAddress = null; + + this.maxPkmnStorage = 250; + this.maxItemStorage = 350; - this.authenticated = 0; /* this.bag = new Bag(this); this.info = new Info(this); @@ -28,6 +57,96 @@ export default class Player extends MapObject { this.pokedex = new Pokedex(this); this.tutorial = new Tutorial(this); */ + + this.refreshSocket(obj.request, obj.response); + } -} \ No newline at end of file + get team() { + return (this._team); + } + set team(value) { + this.team = value; + } + + get email() { + return (this._email); + } + set email(value) { + this.email = value; + this.username = this.email; + } + + /** + * @param {Buffer} buffer + */ + sendResponse(buffer) { + this.response.end(buffer); + } + + /** + * @param {Request} req + * @param {Response} res + */ + refreshSocket(req, res) { + this.request = req; + this.response = res; + } + + authenticate() { + + let request = POGOProtos.parseWithUnknown(this.request.body, "POGOProtos.Networking.Envelopes.RequestEnvelope") + + let msg = this.GetAuthTicket(request.request_id); + + let token = request.auth_info; + + if (!token || !token.provider) { + print("Invalid authentication token! Kicking..", 31); + this.world.removePlayer(this); + return void 0; + } + + if (token.provider === "google") { + if (token.token !== null) { + let decoded = jwtDecode(token.token.contents); + this.email = decoded.email; + this.email_verified = decoded.email_verified; + this.isGoogleAccount = true; + print(`${this.email} connected!`, 36); + } + else { + print("Invalid authentication token! Kicking..", 31); + this.world.removePlayer(this); + return void 0; + } + } + else { + print("Invalid provider! Kicking..", 31); + this.world.removePlayer(this); + return void 0; + } + + this.authenticated = true; + + this.sendResponse(msg); + + } + + /** + * @param {String} type + * @param {Object} msg + */ + getPacket(type, msg) { + return new Promise((resolve) => { + switch (type) { + case "GET_PLAYER": + resolve(this.GetPlayer(msg)); + break; + }; + }); + } + +} + +inherit(Player, _packets); \ No newline at end of file diff --git a/src/models/Player/packets/GetAuthTicket.js b/src/models/Player/packets/GetAuthTicket.js new file mode 100644 index 0000000..2925352 --- /dev/null +++ b/src/models/Player/packets/GetAuthTicket.js @@ -0,0 +1,20 @@ +import POGOProtos from "pokemongo-protobuf"; + +export default function GetAuthTicket(id) { + + let buffer = ({ + status_code: 53, + request_id: id, + api_url: "pgorelease.nianticlabs.com/custom", + auth_ticket: { + start: new Buffer(""), + expire_timestamp_ms: 9999999999999, + end: new Buffer("") + } + }); + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.ResponseEnvelope") + ); + +} \ No newline at end of file diff --git a/src/models/Player/packets/GetInventory.js b/src/models/Player/packets/GetInventory.js index d3f6a64..90392c9 100644 --- a/src/models/Player/packets/GetInventory.js +++ b/src/models/Player/packets/GetInventory.js @@ -1,6 +1,6 @@ import POGOProtos from "pokemongo-protobuf"; -export function GetInventory() { +export default function GetInventory() { let stats = this.GetInventoryPlayer(); let items = this.GetInventoryItems(); diff --git a/src/models/Player/packets/GetPlayer.js b/src/models/Player/packets/GetPlayer.js index b81b18c..0bbc541 100644 --- a/src/models/Player/packets/GetPlayer.js +++ b/src/models/Player/packets/GetPlayer.js @@ -1,7 +1,25 @@ import POGOProtos from "pokemongo-protobuf"; -export function GetPlayer() { +/** + * @param {Object} msg + */ +export default function GetPlayer(msg) { - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerResponse")); + let buffer = { + creation_timestamp_ms: new Date().getTime(), + username: this.username, + team: this.team, + tutorial_state: this.tutorial.serialize(), + avatar: this.avatar.serialize(), + max_pokemon_storage: this.maxPkmnStorage, + max_item_storage: this.maxItemStorage, + daily_bonus: { + next_defender_bonus_collect_timestamp_ms: +new Date() + }, + contact_settings: this.contact.serialize(), + currencies: this.currencies.serialize() + } + + return (POGOProtos.serialize({ success: true, player_data: buffer }, "POGOProtos.Networking.Responses.GetPlayerResponse")); } \ No newline at end of file diff --git a/src/models/Player/packets/GetPlayerProfile.js b/src/models/Player/packets/GetPlayerProfile.js index a785433..8af4d07 100644 --- a/src/models/Player/packets/GetPlayerProfile.js +++ b/src/models/Player/packets/GetPlayerProfile.js @@ -1,6 +1,6 @@ import POGOProtos from "pokemongo-protobuf"; -export function GetPlayerProfile() { +export default function GetPlayerProfile() { return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse")); diff --git a/src/models/Player/packets/index.js b/src/models/Player/packets/index.js new file mode 100644 index 0000000..744ed8a --- /dev/null +++ b/src/models/Player/packets/index.js @@ -0,0 +1,4 @@ +export GetPlayer from "./GetPlayer"; +export GetInventory from "./GetInventory"; +export GetAuthTicket from "./GetAuthTicket"; +export GetPlayerProfile from "./GetPlayerProfile"; \ No newline at end of file diff --git a/src/models/World/index.js b/src/models/World/index.js index b1ef05b..6495326 100644 --- a/src/models/World/index.js +++ b/src/models/World/index.js @@ -18,12 +18,16 @@ export default class World { } + get connectedPlayers() { + return (this.players.length); + } + /** * @return {Boolean} */ isFull() { return ( - this.players.length >= CFG.MAX_CONNECTIONS + this.connectedPlayers >= CFG.MAX_CONNECTIONS ); } @@ -40,27 +44,25 @@ export default class World { return (player); } else { - this.addPlayer(req, res); + player = this.addPlayer(req, res); return (player); } } /** - * @param {Object} client + * @param {Request} req * @return {Boolean} */ - playerAlreadyConnected(client) { - - let players = this.players; + playerAlreadyConnected(req) { let ii = 0; - let length = players.length; + let length = this.connectedPlayers; - let remoteAddress = client.headers.host; + let remoteAddress = req.headers.host; for (; ii < length; ++ii) { - if (players[ii].remoteAddress === remoteAddress) { + if (this.players[ii].remoteAddress === remoteAddress) { return (true); } }; @@ -77,7 +79,7 @@ export default class World { let players = this.players; let ii = 0; - let length = players.length; + let length = this.connectedPlayers; for (; ii < length; ++ii) { if (players[ii].remoteAddress === ip) { @@ -92,23 +94,29 @@ export default class World { /** * @param {Request} req * @param {Response} res + * @return {Player} */ addPlayer(req, res) { let player = new Player({ + world: this, request: req, response: res }); + player.remoteAddress = req.headers.host; + this.players.push(player); + return (player); + } /** * @param {Player} player */ removePlayer(player) { - console.log(player); + console.log("Remove:", player.email); } spawnFort() { @@ -123,4 +131,18 @@ export default class World { } + /** + * @param {String} type + * @param {Object} msg + */ + getPacket(type, msg) { + return new Promise((resolve) => { + switch (type) { + case "CHECK_CHALLENGE": + console.log(type, msg); + break; + }; + }); + } + } \ No newline at end of file diff --git a/src/print.js b/src/print.js new file mode 100644 index 0000000..2d16862 --- /dev/null +++ b/src/print.js @@ -0,0 +1,11 @@ +import CFG from "../cfg"; + +/** + * @param {String} msg + * @param {Number} color + * @param {Boolean} nl + */ +export default function print(msg, color, newline) { + color = Number.isInteger(color) ? color : CFG.DEFAULT_CONSOLE_COLOR; + process.stdout.write(`[Console] \x1b[${color};1m${msg}\x1b[0m${newline === void 0 ? "\n" : ""}`); +} \ No newline at end of file diff --git a/src/process.js b/src/process.js index e437c21..7fd80db 100644 --- a/src/process.js +++ b/src/process.js @@ -1,5 +1,6 @@ import fs from "fs"; +import print from "./print"; import CFG from "../cfg"; const helpMessage = fs.readFileSync(".help", "utf8"); @@ -10,7 +11,7 @@ export function processCommand(cmd, data) { // How many active connections there are case "/players": var length = players.length; - this.print(`${length}:${CFG.MAX_CONNECTIONS} connected players!`, 33); + print(`${length}:${CFG.MAX_CONNECTIONS} connected players!`, 33); break; // Exit the server case "/exit": @@ -23,7 +24,7 @@ export function processCommand(cmd, data) { var length = players.length; this.removeAllPlayers(); var result = length - players.length; - this.print(`Removed ${result} player${result === 1 ? "": "s"}!`); + print(`Removed ${result} player${result === 1 ? "": "s"}!`); break; case "/clear": process.stdout.write("\x1Bc"); @@ -37,7 +38,7 @@ export function processCommand(cmd, data) { case "/save": this.saveAllPlayers(); var length = players.length; - this.print(`Saved ${length} player${length === 1 ? "": "s"} into database!`); + print(`Saved ${length} player${length === 1 ? "": "s"} into database!`); break; case "/spawn": this.spawnPkmnAtPlayer(data[1], data[2], data[3] || 1); @@ -46,7 +47,7 @@ export function processCommand(cmd, data) { eval(fs.readFileSync("update.js", "utf8")); break; default: - this.print(`${cmd} is not a valid command!`, 31); + print(`${cmd} is not a valid command!`, 31); break; }; }; @@ -62,15 +63,15 @@ export function stdinInput(data) { export function uncaughtException(excp) { switch (excp.errno) { case "EADDRINUSE": - this.print(`Port ${CFG.PORT} is already in use!`, 31); + print(`Port ${CFG.PORT} is already in use!`, 31); break; case "EACCES": - this.print("No root privileges!", 31); + print("No root privileges!", 31); break; default: console.log("Unhandled exception occurred: ", excp.code); console.log(excp.stack); break; }; - this.print("The server has crashed!", 31); + print("The server has crashed!", 31); }; \ No newline at end of file diff --git a/src/request.js b/src/request.js index 60b70b0..af13316 100644 --- a/src/request.js +++ b/src/request.js @@ -3,6 +3,7 @@ import url from "url"; import pcrypt from "pcrypt"; import POGOProtos from "pokemongo-protobuf"; +import print from "./print"; import CFG from "../cfg"; /** @@ -36,16 +37,6 @@ export function routeRequest(req, res) { } -/** - * @param {Request} req - * @param {Response} res - * @param {Array} route - */ -export function processRpcRequest(req, res, route) { - let player = this.world.getPlayerByRequest(req, res); - if (route[2] === "rpc") this.onRequest(player, req); -} - /** * @param {Request} req * @param {Response} res @@ -62,16 +53,29 @@ export function processModelRequest(req, res, route) { let folder = player.isAndroid ? "android/" : "ios/"; fs.readFile("data/" + folder + name, (error, data) => { if (error) { - this.print(`Error file resolving model ${name}:` + error, 31); + print(`Error file resolving model ${name}:` + error, 31); return void 0; } - this.print(`Sent ${name} to ${player.email}`, 36); + print(`Sent ${name} to ${player.email}`, 36); res.end(data); }); } } +/** + * @param {Request} req + * @param {Response} res + * @param {Array} route + */ +export function processRpcRequest(req, res, route) { + let player = this.world.getPlayerByRequest(req, res); + if (route[2] === "rpc") { + player.refreshSocket(req, res); + this.onRequest(player, req); + } +} + /** * @param {Player} player * @param {Request} req @@ -89,12 +93,12 @@ export function onRequest(player, req) { } if (!player.authenticated) { - player.sendResponse(this.authenticatePlayer(player)); + player.authenticate(); return void 0; } if (!request.requests.length) { - this.print("Received invalid request!", 31); + print("Received invalid request!", 31); return void 0; } @@ -132,7 +136,7 @@ export function envelopResponse(status, returns, req, player, auth) { player.isAndroid = !player.isIOS; player.hasSignature = true; player.asset_digest = this.assets[player.isAndroid ? "android" : "ios"]; - this.print(`${player.email} is playing with an ${player.isIOS ? "Apple" : "Android"} device!`, 36); + print(`${player.email} is playing with an ${player.isIOS ? "Apple" : "Android"} device!`, 36); } } diff --git a/src/response.js b/src/response.js index 2a66b78..2eda83a 100644 --- a/src/response.js +++ b/src/response.js @@ -1,5 +1,6 @@ import POGOProtos from "pokemongo-protobuf"; +import print from "./print"; import CFG from "../cfg"; import { @@ -8,16 +9,17 @@ import { } from "./utils"; /** - * @param {Request} req + * @param {Request} req + * @param {String} type * @return {Buffer} */ -export function parseMessage(req) { - let proto = `POGOProtos.Networking.Requests.Messages.${cc}Message`; +export function parseMessage(req, type) { + let proto = `POGOProtos.Networking.Requests.Messages.${type}Message`; if (req.request_message) { try { return (this.parseProtobuf(req.request_message, proto)); } catch (e) { - this.print(`Failed to parse ${cc}: ${e}`, 31); + print(`Failed to parse ${type}: ${e}`, 31); } } return void 0; @@ -33,18 +35,30 @@ export function processResponse(player, req) { let buffer = null; let cc = _toCC(req.request_type); - let msg = this.parseMessage(req); + let msg = this.parseMessage(req, cc); return new Promise((resolve) => { try { switch (req.request_type) { + case "GET_PLAYER": + player.getPacket("GET_PLAYER", msg).then((result) => { + resolve(result); + }); + return void 0; + break; + case "CHECK_CHALLENGE": + player.world.getPacket("CHECK_CHALLENGE", msg).then((result) => { + resolve(result); + }); + return void 0; + break; default: - this.print(`Unknown request: ${req.request_type}`, 31); + print(`Unknown request: ${req.request_type}`, 31); break; }; } catch (e) { - this.print(`Response error: ${e}`, 31); + print(`Response error: ${e}`, 31); }; resolve(buffer); diff --git a/src/setup.js b/src/setup.js index e14c153..cf864a3 100644 --- a/src/setup.js +++ b/src/setup.js @@ -3,6 +3,7 @@ import fse from "fs-extra"; import pogo from "pogo-asset-downloader"; import POGOProtos from "pokemongo-protobuf"; +import print from "./print"; import CFG from "../cfg"; import * as master from "./master"; @@ -19,7 +20,7 @@ export function setup() { let save = JSON.parse(fs.readFileSync(".save", "utf8")); if (save.isFirstRun) { - this.print("Required assets are missing! Preparing dump session..", 33); + print("Required assets are missing! Preparing dump session..", 33); setTimeout(() => { this.onFirstRun(() => { save.isFirstRun = false; @@ -33,7 +34,7 @@ export function setup() { // make sure all assets got loaded properly this.validateAssets().then(() => { - this.print(`Downloaded assets are valid! Proceeding..`); + print(`Downloaded assets are valid! Proceeding..`); master.GAME_MASTER = this.parseGameMaster(); @@ -41,18 +42,18 @@ export function setup() { this.setupDatabaseConnection().then(() => { if (CFG.PORT < 1) { - this.print("Invalid port!", 31); + print("Invalid port!", 31); return void 0; } this.socket = this.createHTTPServer(); setTimeout(this::this.cycle, 1); let localIPv4 = this.getLocalIPv4(); - this.print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33); + print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33); }); }).catch((e) => { //fse.removeSync(CFG.DUMP_ASSET_PATH); - this.print("Error: " + e + " was not found!", 31); + print("Error: " + e + " was not found!", 31); }); } @@ -131,7 +132,7 @@ export function parseGameMaster() { master = this.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse"); let Master = new GameMaster(master); } catch (e) { - this.print(e, 31); + print(e, 31); } return (master); } @@ -147,7 +148,7 @@ export function onFirstRun(resolve) { this.downloadAssets().then(resolve); }); }).catch((e) => { - this.print(e, 31); + print(e, 31); }); } @@ -189,11 +190,11 @@ export function downloadModels() { let caps = capitalize(name); caps = name === "ios" ? "iOS" : caps; pogo.setPlatform(name); - this.print(`Preparing to dump ${caps} assets..`, 36); + print(`Preparing to dump ${caps} assets..`, 36); this.dumpPkmnModels(CFG.DUMP_ASSET_PATH + name + "/", () => { - this.print(`Dumped ${CFG.MAX_POKEMON_NATIONAL_ID} ${caps} assets successfully!`); + print(`Dumped ${CFG.MAX_POKEMON_NATIONAL_ID} ${caps} assets successfully!`); if (++index >= limit) { - this.print("Dumped all assets successfully!"); + print("Dumped all assets successfully!"); resolve(); return void 0; } @@ -216,16 +217,16 @@ export function dumpPkmnModels(path, resolve) { if (++index <= limit) ids.push(index); pogo.getAssetByPokemonId(ids).then((downloads) => { downloads.map((item) => { - this.print(`Dumping model ${item.name}..`, 35); + print(`Dumping model ${item.name}..`, 35); try { fs.writeFileSync(path + item.name, item.body); } catch (e) { - this.print(`Error while dumping model ${item.name}:` + e, 31); + print(`Error while dumping model ${item.name}:` + e, 31); } }); if (index >= limit) { - //this.print(`Dumped ${limit} assets successfully!`); + //print(`Dumped ${limit} assets successfully!`); resolve(); return void 0; } From aa3743b4bd4ae71b2137cbf0e77bf18362208911 Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 28 Aug 2016 14:31:08 +0200 Subject: [PATCH 4/6] Update - Cleaner game master handling - Added contact and currency player subclasses - Authentication works - Cleaner device sig parsing, handle unknown as repeated field - Added various player and world packets - Seperate settings object for DownloadSettings request - Basic GetMapObjects response - Removed unnecessary code in request.js - Extended response.js - Outsourced parseSignature into utils.js --- src/dump.js | 2 + src/index.js | 12 --- src/master.js | 6 -- src/models/GameMaster/index.js | 29 +++++++- src/models/Player/Contact/index.js | 24 ++++++ src/models/Player/Currency/index.js | 30 ++++++++ src/models/Player/Info/index.js | 32 ++++---- src/models/Player/Party/index.js | 4 +- src/models/Player/Tutorial/index.js | 34 +++------ src/models/Player/index.js | 74 +++++++++++++++---- .../Player/packets/CheckAwardedBadges.js | 19 +++++ src/models/Player/packets/GetAssetDigest.js | 13 ++++ src/models/Player/packets/GetHatchedEggs.js | 21 ++++++ src/models/Player/packets/GetInventory.js | 54 +++++--------- src/models/Player/packets/GetPlayer.js | 9 ++- src/models/Player/packets/GetPlayerProfile.js | 4 +- src/models/Player/packets/index.js | 5 +- src/models/Pokemon/index.js | 2 + src/models/World/index.js | 22 +++++- src/models/World/packets/CheckChallenge.js | 16 ++++ .../World/packets/DownloadItemTemplates.js | 13 ++++ .../packets/DownloadRemoteConfigVersion.js | 19 +++++ src/models/World/packets/DownloadSettings.js | 20 +++++ src/models/World/packets/GetMapObject.js | 18 ----- src/models/World/packets/GetMapObjects.js | 38 ++++++++++ src/models/World/packets/index.js | 5 ++ src/modes/index.js | 31 ++++++++ src/request.js | 43 +++++------ src/response.js | 20 +++-- src/setup.js | 12 ++- src/shared.js | 6 ++ src/utils.js | 13 ++++ 32 files changed, 474 insertions(+), 176 deletions(-) delete mode 100644 src/master.js create mode 100644 src/models/Player/Contact/index.js create mode 100644 src/models/Player/Currency/index.js create mode 100644 src/models/Player/packets/CheckAwardedBadges.js create mode 100644 src/models/Player/packets/GetAssetDigest.js create mode 100644 src/models/Player/packets/GetHatchedEggs.js create mode 100644 src/models/World/packets/CheckChallenge.js create mode 100644 src/models/World/packets/DownloadItemTemplates.js create mode 100644 src/models/World/packets/DownloadRemoteConfigVersion.js create mode 100644 src/models/World/packets/DownloadSettings.js delete mode 100644 src/models/World/packets/GetMapObject.js create mode 100644 src/models/World/packets/GetMapObjects.js create mode 100644 src/models/World/packets/index.js create mode 100644 src/modes/index.js create mode 100644 src/shared.js diff --git a/src/dump.js b/src/dump.js index e40811c..e7e0716 100644 --- a/src/dump.js +++ b/src/dump.js @@ -4,6 +4,8 @@ import POGOProtos from "pokemongo-protobuf"; import print from "./print"; import CFG from "../cfg"; +import { _toCC } from "./utils"; + /** * @param {Request} req * @param {Array} res diff --git a/src/index.js b/src/index.js index bf81bcd..c69cf6d 100644 --- a/src/index.js +++ b/src/index.js @@ -45,8 +45,6 @@ export default class GameServer { this.apiClients = {}; - this.assets = {}; - this.master = null; this.socket = null; this.cycleInstance = null; @@ -119,16 +117,6 @@ export default class GameServer { } } - /** - * @param {Request} req - */ - parseSignature(req) { - let key = pcrypt.decrypt(req.unknown6.unknown2.encrypted_signature); - return ( - POGOProtos.parseWithUnknown(key, "POGOProtos.Networking.Envelopes.Signature") - ); - } - /** * @param {String} path * @return {Boolean} diff --git a/src/master.js b/src/master.js deleted file mode 100644 index f80580f..0000000 --- a/src/master.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Global shared parsed - * game master file - * @type {GameMaster} - */ -export let GAME_MASTER = null; \ No newline at end of file diff --git a/src/models/GameMaster/index.js b/src/models/GameMaster/index.js index 2229041..3fb70fb 100644 --- a/src/models/GameMaster/index.js +++ b/src/models/GameMaster/index.js @@ -1,29 +1,41 @@ +import POGOProtos from "pokemongo-protobuf"; + /** * @class GameMaster */ export default class GameMaster { /** - * @param {Buffer} buffer + * @param {Object} decode * @constructor */ - constructor(buffer) { + constructor(decode) { this.settings = this.buildSettings(); - this.buffer = buffer; + this.decode = decode; + this.buffer = this.encode(); this.parse(); } + /** + * @return {Buffer} + */ + encode() { + return ( + POGOProtos.serialize(this.decode, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse") + ); + } + parse() { let ii = 0; let length = 0; let item = null; - let items = this.buffer.item_templates; + let items = this.decode.item_templates; length = items.length; @@ -60,4 +72,13 @@ export default class GameMaster { ); } + /** + * @return {Buffer} + */ + serialize() { + return ( + this.buffer + ); + } + } \ No newline at end of file diff --git a/src/models/Player/Contact/index.js b/src/models/Player/Contact/index.js new file mode 100644 index 0000000..68a26c1 --- /dev/null +++ b/src/models/Player/Contact/index.js @@ -0,0 +1,24 @@ +/** + * @class Contact + */ +export default class Contact { + + /** @constructor */ + constructor() { + + this.sendMarketingEmails = false; + this.sendPushNotifications = false; + + } + + /** + * @return {Object} + */ + serialize() { + return ({ + send_marketing_emails: this.sendMarketingEmails, + send_push_notifications: this.sendPushNotifications + }); + } + +} \ No newline at end of file diff --git a/src/models/Player/Currency/index.js b/src/models/Player/Currency/index.js new file mode 100644 index 0000000..5530123 --- /dev/null +++ b/src/models/Player/Currency/index.js @@ -0,0 +1,30 @@ +/** + * @class Currency + */ +export default class Currency { + + /** @constructor */ + constructor() { + + this.currencies = { + "POKECOINS": 0, + "STARDUST": 0 + }; + + } + + /** + * @return {Array} + */ + serialize() { + let out = []; + for (let key in this.currencies) { + out.push({ + name: key, + amount: this.currencies[key] + }); + }; + return (out); + } + +} \ No newline at end of file diff --git a/src/models/Player/Info/index.js b/src/models/Player/Info/index.js index 88ed579..093ca5d 100644 --- a/src/models/Player/Info/index.js +++ b/src/models/Player/Info/index.js @@ -1,7 +1,7 @@ /** * @class Info */ -class Info { +export default class Info { /** @constructor */ constructor() { @@ -68,23 +68,21 @@ class Info { } } - getPlayerInfo() { + serialize() { return ({ - player_stats: { - level: this.lvl, - experience: this.exp, - prev_level_xp: this.prevLvlExp, - next_level_xp: this.nextLvlExp, - km_walked: this.kmWalked, - pokemons_encountered: this.pkmnEncountered, - unique_pokedex_entries: this.uniquePokedexEntries, - pokemons_captured: this.pkmnCaptured, - poke_stop_visits: this.pokeStopVisits, - pokeballs_thrown: this.pokeballsThrown, - eggs_hatched: this.eggsHatched, - big_magikarp_caught: this.bigMagikarpCaught, - pokemon_deployed: this.pkmnDeployed - } + level: this.lvl, + experience: this.exp, + prev_level_xp: this.prevLvlExp, + next_level_xp: this.nextLvlExp, + km_walked: this.kmWalked, + pokemons_encountered: this.pkmnEncountered, + unique_pokedex_entries: this.uniquePokedexEntries, + pokemons_captured: this.pkmnCaptured, + poke_stop_visits: this.pokeStopVisits, + pokeballs_thrown: this.pokeballsThrown, + eggs_hatched: this.eggsHatched, + big_magikarp_caught: this.bigMagikarpCaught, + pokemon_deployed: this.pkmnDeployed }); } diff --git a/src/models/Player/Party/index.js b/src/models/Player/Party/index.js index d2b3276..370b8be 100644 --- a/src/models/Player/Party/index.js +++ b/src/models/Player/Party/index.js @@ -1,7 +1,9 @@ +import Pokemon from "../../Pokemon"; + /** * @class Party */ -class Party { +export default class Party { /** @constructor */ constructor() { diff --git a/src/models/Player/Tutorial/index.js b/src/models/Player/Tutorial/index.js index 5512d30..c7c0e00 100644 --- a/src/models/Player/Tutorial/index.js +++ b/src/models/Player/Tutorial/index.js @@ -1,5 +1,3 @@ -import Candy from "./Candy"; - /** * @class Tutorial */ @@ -7,38 +5,24 @@ export default class Tutorial { /** @constructor */ constructor() { - this.states = []; - - this.LEGAL_SCREEN = 0; - this.AVATAR_SELECTION = 1; - this.ACCOUNT_CREATION = 2; - this.POKEMON_CAPTURE = 3; - this.NAME_SELECTION = 4; - this.POKEMON_BERRY = 5; - this.USE_ITEM = 6; - this.FIRST_TIME_EXPERIENCE_COMPLETE = 7; - this.POKESTOP_TUTORIAL = 8; - this.GYM_TUTORIAL = 9; - } - passLegalScreen() { - - } - - passAvatarSelection() { - - } - - passAccountCreation() { - + skipTutorial() { + this.states = [ + "LEGAL_SCREEN", + "AVATAR_SELECTION", + "POKEMON_CAPTURE", + "NAME_SELECTION", + "FIRST_TIME_EXPERIENCE_COMPLETE" + ]; } /** * @return {Array} */ serialize() { + this.skipTutorial(); return ( this.states ); diff --git a/src/models/Player/index.js b/src/models/Player/index.js index 766e116..89881d7 100644 --- a/src/models/Player/index.js +++ b/src/models/Player/index.js @@ -1,16 +1,26 @@ -import Avatar from "./Avatar"; -import MapObject from "../World/MapObject"; import POGOProtos from "pokemongo-protobuf"; import jwtDecode from "jwt-decode"; +import Info from "./Info"; +import Party from "./Party"; +import Avatar from "./Avatar"; +import Contact from "./Contact"; +import Tutorial from "./Tutorial"; +import Currency from "./Currency"; + +import MapObject from "../World/MapObject"; + import print from "../../print"; import CFG from "../../../cfg"; import * as _packets from "./packets"; -import { inherit } from "../../utils"; +import { + inherit, + parseSignature +} from "../../utils"; -import { GAME_MASTER } from "../../master"; +import { GAME_MASTER } from "../../shared"; /** * @class Player @@ -28,15 +38,20 @@ export default class Player extends MapObject { this.world = obj.world; this._team = 0; + this._email = null; this.username = "unknown"; - this._email = null; this.email_verified = false; + this.platform = null; + this.isPTCAccount = false; this.isGoogleAccount = false; + this.isIOS = false; + this.isAndroid = false; + this.hasSignature = false; this.authenticated = false; @@ -49,13 +64,17 @@ export default class Player extends MapObject { this.maxPkmnStorage = 250; this.maxItemStorage = 350; + this.info = new Info(this); + this.party = new Party(this); + this.avatar = new Avatar(this); + this.contact = new Contact(this); + this.currency = new Currency(this); + this.tutorial = new Tutorial(this); + /* this.bag = new Bag(this); this.info = new Info(this); - this.pary = new Party(this); - this.avatar = new Avatar(this); this.pokedex = new Pokedex(this); - this.tutorial = new Tutorial(this); */ this.refreshSocket(obj.request, obj.response); @@ -66,15 +85,15 @@ export default class Player extends MapObject { return (this._team); } set team(value) { - this.team = value; + this._team = value; } get email() { return (this._email); } set email(value) { - this.email = value; - this.username = this.email; + this._email = value; + this.username = value.replace("@gmail.com", ""); } /** @@ -89,13 +108,13 @@ export default class Player extends MapObject { * @param {Response} res */ refreshSocket(req, res) { - this.request = req; + this.request = POGOProtos.parseWithUnknown(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope"); this.response = res; } authenticate() { - let request = POGOProtos.parseWithUnknown(this.request.body, "POGOProtos.Networking.Envelopes.RequestEnvelope") + let request = this.request; let msg = this.GetAuthTicket(request.request_id); @@ -133,6 +152,23 @@ export default class Player extends MapObject { } + getDevicePlatform() { + + let request = this.request; + + if (request.unknown6 && request.unknown6[0]) { + let sig = parseSignature(request); + if (sig.device_info !== void 0) { + this.hasSignature = true; + this.isIOS = sig.device_info.device_brand === "Apple"; + this.isAndroid = !this.isIOS; + this.platform = this.isIOS ? "ios" : "android"; + print(`${this.email} is playing with an ${this.isIOS ? "Apple" : "Android"} device!`, 36); + } + } + + } + /** * @param {String} type * @param {Object} msg @@ -143,6 +179,18 @@ export default class Player extends MapObject { case "GET_PLAYER": resolve(this.GetPlayer(msg)); break; + case "GET_INVENTORY": + resolve(this.GetInventory(msg)); + break; + case "GET_ASSET_DIGEST": + resolve(this.GetAssetDigest(msg)); + break; + case "GET_HATCHED_EGGS": + resolve(this.GetHatchedEggs(msg)); + break; + case "CHECK_AWARDED_BADGES": + resolve(this.CheckAwardedBadges(msg)); + break; }; }); } diff --git a/src/models/Player/packets/CheckAwardedBadges.js b/src/models/Player/packets/CheckAwardedBadges.js new file mode 100644 index 0000000..9046520 --- /dev/null +++ b/src/models/Player/packets/CheckAwardedBadges.js @@ -0,0 +1,19 @@ +import POGOProtos from "pokemongo-protobuf"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function CheckAwardedBadges(msg) { + + let buffer = { + success: true, + awarded_badges: [], + awarded_badge_levels: [] + }; + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CheckAwardedBadgesResponse") + ); + +} \ No newline at end of file diff --git a/src/models/Player/packets/GetAssetDigest.js b/src/models/Player/packets/GetAssetDigest.js new file mode 100644 index 0000000..671185a --- /dev/null +++ b/src/models/Player/packets/GetAssetDigest.js @@ -0,0 +1,13 @@ +import POGOProtos from "pokemongo-protobuf"; + +import * as shared from "../../../shared"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function GetAssetDigest(msg) { + return ( + shared.GAME_ASSETS[this.platform].buffer + ); +} \ No newline at end of file diff --git a/src/models/Player/packets/GetHatchedEggs.js b/src/models/Player/packets/GetHatchedEggs.js new file mode 100644 index 0000000..ae16e21 --- /dev/null +++ b/src/models/Player/packets/GetHatchedEggs.js @@ -0,0 +1,21 @@ +import POGOProtos from "pokemongo-protobuf"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function GetHatchedEggs(msg) { + + let buffer = { + success: true, + pokemon_id: [], + experience_awarded: [], + candy_awarded: [], + stardust_awarded: [] + }; + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetHatchedEggsResponse") + ); + +} \ No newline at end of file diff --git a/src/models/Player/packets/GetInventory.js b/src/models/Player/packets/GetInventory.js index 90392c9..956568b 100644 --- a/src/models/Player/packets/GetInventory.js +++ b/src/models/Player/packets/GetInventory.js @@ -1,47 +1,33 @@ import POGOProtos from "pokemongo-protobuf"; -export default function GetInventory() { - - let stats = this.GetInventoryPlayer(); +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function GetInventory(msg) { +/* let items = this.GetInventoryItems(); let party = this.GetInventoryParty(); +*/ - let buffer = ({ + let stats = { + modified_timestamp_ms: +new Date(), + inventory_item_data: { + player_stats: this.info.serialize() + } + }; + + let buffer = { success: true, inventory_delta: { new_timestamp_ms: new Date().getTime(), - inventory_items: stats.concat(items).concat(party) + inventory_items: [stats] } - }); + }; - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetInventoryResponse")); - -} - -export function GetInventoryPlayer() { - - let player = this.player; - - return ({ - modified_timestamp_ms: new Date().getTime(), - inventory_item_data: { - player_stats: { - level: player.level, - experience: player.exp, - prev_level_xp: "21000", - next_level_xp: "36000", - km_walked: 3.921541213989258, - pokemons_encountered: 75, - unique_pokedex_entries: 25, - pokemons_captured: 71, - poke_stop_visits: 123, - pokeballs_thrown: 74, - eggs_hatched: 1, - big_magikarp_caught: 1, - pokemon_deployed: 1 - } - } - }); + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetInventoryResponse") + ); } diff --git a/src/models/Player/packets/GetPlayer.js b/src/models/Player/packets/GetPlayer.js index 0bbc541..256c946 100644 --- a/src/models/Player/packets/GetPlayer.js +++ b/src/models/Player/packets/GetPlayer.js @@ -2,6 +2,7 @@ import POGOProtos from "pokemongo-protobuf"; /** * @param {Object} msg + * @return {Buffer} */ export default function GetPlayer(msg) { @@ -17,9 +18,11 @@ export default function GetPlayer(msg) { next_defender_bonus_collect_timestamp_ms: +new Date() }, contact_settings: this.contact.serialize(), - currencies: this.currencies.serialize() - } + currencies: this.currency.serialize() + }; - return (POGOProtos.serialize({ success: true, player_data: buffer }, "POGOProtos.Networking.Responses.GetPlayerResponse")); + return ( + POGOProtos.serialize({ success: true, player_data: buffer }, "POGOProtos.Networking.Responses.GetPlayerResponse") + ); } \ No newline at end of file diff --git a/src/models/Player/packets/GetPlayerProfile.js b/src/models/Player/packets/GetPlayerProfile.js index 8af4d07..51b6576 100644 --- a/src/models/Player/packets/GetPlayerProfile.js +++ b/src/models/Player/packets/GetPlayerProfile.js @@ -2,6 +2,8 @@ import POGOProtos from "pokemongo-protobuf"; export default function GetPlayerProfile() { - return (POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse")); + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse") + ); } \ No newline at end of file diff --git a/src/models/Player/packets/index.js b/src/models/Player/packets/index.js index 744ed8a..a3a9b28 100644 --- a/src/models/Player/packets/index.js +++ b/src/models/Player/packets/index.js @@ -1,4 +1,7 @@ export GetPlayer from "./GetPlayer"; export GetInventory from "./GetInventory"; export GetAuthTicket from "./GetAuthTicket"; -export GetPlayerProfile from "./GetPlayerProfile"; \ No newline at end of file +export GetHatchedEggs from "./GetHatchedEggs"; +export GetAssetDigest from "./GetAssetDigest"; +export GetPlayerProfile from "./GetPlayerProfile"; +export CheckAwardedBadges from "./CheckAwardedBadges"; \ No newline at end of file diff --git a/src/models/Pokemon/index.js b/src/models/Pokemon/index.js index 9013c2e..79ba71e 100644 --- a/src/models/Pokemon/index.js +++ b/src/models/Pokemon/index.js @@ -17,6 +17,8 @@ export default class Pokemon extends MapObject { */ constructor(obj) { + super(null); + this.id = 0; this.dexNumber = 0; diff --git a/src/models/World/index.js b/src/models/World/index.js index 6495326..453c64b 100644 --- a/src/models/World/index.js +++ b/src/models/World/index.js @@ -2,6 +2,10 @@ import Player from "../Player"; import CFG from "../../../cfg"; +import * as _packets from "./packets"; + +import { inherit } from "../../utils"; + /** * @class World */ @@ -138,11 +142,25 @@ export default class World { getPacket(type, msg) { return new Promise((resolve) => { switch (type) { + case "GET_MAP_OBJECTS": + resolve(this.GetMapObjects(msg)); + break; case "CHECK_CHALLENGE": - console.log(type, msg); + resolve(this.CheckChallenge(msg)); + break; + case "DOWNLOAD_SETTINGS": + resolve(this.DownloadSettings(msg)); + break; + case "DOWNLOAD_REMOTE_CONFIG_VERSION": + resolve(this.DownloadRemoteConfigVersion(msg)); + break; + case "DOWNLOAD_ITEM_TEMPLATES": + resolve(this.DownloadItemTemplates(msg)); break; }; }); } -} \ No newline at end of file +} + +inherit(World, _packets); \ No newline at end of file diff --git a/src/models/World/packets/CheckChallenge.js b/src/models/World/packets/CheckChallenge.js new file mode 100644 index 0000000..308c7ca --- /dev/null +++ b/src/models/World/packets/CheckChallenge.js @@ -0,0 +1,16 @@ +import POGOProtos from "pokemongo-protobuf"; + +/** + * @param {Object} msg + */ +export default function CheckChallenge(msg) { + + let buffer = { + challenge_url: " " + }; + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CheckChallengeResponse") + ); + +} \ No newline at end of file diff --git a/src/models/World/packets/DownloadItemTemplates.js b/src/models/World/packets/DownloadItemTemplates.js new file mode 100644 index 0000000..af04952 --- /dev/null +++ b/src/models/World/packets/DownloadItemTemplates.js @@ -0,0 +1,13 @@ +import POGOProtos from "pokemongo-protobuf"; + +import * as shared from "../../../shared"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function DownloadItemTemplates(msg) { + return ( + shared.GAME_MASTER.serialize() + ); +} \ No newline at end of file diff --git a/src/models/World/packets/DownloadRemoteConfigVersion.js b/src/models/World/packets/DownloadRemoteConfigVersion.js new file mode 100644 index 0000000..f837e9e --- /dev/null +++ b/src/models/World/packets/DownloadRemoteConfigVersion.js @@ -0,0 +1,19 @@ +import POGOProtos from "pokemongo-protobuf"; + +/** + * @param {Object} msg + */ +export default function DownloadRemoteConfigVersion(msg) { + + let buffer = { + "result": "SUCCESS", + "item_templates_timestamp_ms": "1471650700946", + "asset_digest_timestamp_ms": "1467338276561000", + "$unknownFields": [] + } + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.DownloadRemoteConfigVersionResponse") + ); + +} \ No newline at end of file diff --git a/src/models/World/packets/DownloadSettings.js b/src/models/World/packets/DownloadSettings.js new file mode 100644 index 0000000..040a4ff --- /dev/null +++ b/src/models/World/packets/DownloadSettings.js @@ -0,0 +1,20 @@ +import POGOProtos from "pokemongo-protobuf"; + +import Settings from "../../../modes"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function DownloadSettings(msg) { + + let buffer = { + hash: "2788184af4004004d6ab0740f7632983332106f6", + settings: Settings.GAME_SETTINGS + }; + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.DownloadSettingsResponse") + ); + +} \ No newline at end of file diff --git a/src/models/World/packets/GetMapObject.js b/src/models/World/packets/GetMapObject.js deleted file mode 100644 index d9c628a..0000000 --- a/src/models/World/packets/GetMapObject.js +++ /dev/null @@ -1,18 +0,0 @@ -import POGOProtos from "pokemongo-protobuf"; - -/** - * @param {Request} request - */ -export function GetMapObject(request) { - - let player = this.player; - - let latitude = player.latitude; - let longitude = player.longitude; - - return new Promise(() => { - // db call - // - }); - -} \ No newline at end of file diff --git a/src/models/World/packets/GetMapObjects.js b/src/models/World/packets/GetMapObjects.js new file mode 100644 index 0000000..9b0b0b1 --- /dev/null +++ b/src/models/World/packets/GetMapObjects.js @@ -0,0 +1,38 @@ +import POGOProtos from "pokemongo-protobuf"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function GetMapObjects(msg) { + + let latitude = msg.latitude; + let longitude = msg.longitude; + + let cells = []; + + let buffer = { + status: "SUCCESS", + map_cells: cells + }; + + for (let cell in msg.cell_id) { + cells.push({ + s2_cell_id: cell, + current_timestamp_ms: +new Date(), + forts: [], + spawn_points: [], + deleted_objects: [], + fort_summaries: [], + decimated_spawn_points: [], + wild_pokemons: [], + catchable_pokemons: [], + nearby_pokemons: [] + }); + }; + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse") + ); + +} \ No newline at end of file diff --git a/src/models/World/packets/index.js b/src/models/World/packets/index.js new file mode 100644 index 0000000..de55e09 --- /dev/null +++ b/src/models/World/packets/index.js @@ -0,0 +1,5 @@ +export GetMapObjects from "./GetMapObjects"; +export CheckChallenge from "./CheckChallenge"; +export DownloadSettings from "./DownloadSettings"; +export DownloadItemTemplates from "./DownloadItemTemplates"; +export DownloadRemoteConfigVersion from "./DownloadRemoteConfigVersion"; \ No newline at end of file diff --git a/src/modes/index.js b/src/modes/index.js new file mode 100644 index 0000000..f8e134d --- /dev/null +++ b/src/modes/index.js @@ -0,0 +1,31 @@ +import CFG from "../../cfg"; + +export default { + GAME_SETTINGS: { + fort_settings: { + interaction_range_meters: 40.25098039215686, + max_total_deployed_pokemon: 10, + max_player_deployed_pokemon: 1, + deploy_stamina_multiplier: 8.062745098039215, + deploy_attack_multiplier: 0, + far_interaction_range_meters: 1000.0156862745098 + }, + map_settings: { + pokemon_visible_range: 70.00196078431372, + poke_nav_range_meters: 751.0156862745098, + encounter_range_meters: 50.25098039215686, + get_map_objects_min_refresh_seconds: 10.007843017578125, + get_map_objects_max_refresh_seconds: 11.01568603515625, + get_map_objects_min_distance_meters: 10.007843017578125, + google_maps_api_key: CFG.GMAPS_KEY + }, + inventory_settings: { + max_pokemon: 1000, + max_bag_items: 1000, + base_pokemon: 250, + base_bag_items: 350, + base_eggs: 9 + }, + minimum_client_version: CFG.MINIMUM_CLIENT_VERSION + } +} \ No newline at end of file diff --git a/src/request.js b/src/request.js index af13316..16abd7e 100644 --- a/src/request.js +++ b/src/request.js @@ -1,6 +1,5 @@ import fs from "fs"; import url from "url"; -import pcrypt from "pcrypt"; import POGOProtos from "pokemongo-protobuf"; import print from "./print"; @@ -72,23 +71,22 @@ export function processRpcRequest(req, res, route) { let player = this.world.getPlayerByRequest(req, res); if (route[2] === "rpc") { player.refreshSocket(req, res); - this.onRequest(player, req); + this.onRequest(player); } } /** * @param {Player} player - * @param {Request} req */ -export function onRequest(player, req) { +export function onRequest(player) { - let request = this.parseProtobuf(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope"); + let request = player.request; request.requests = request.requests || []; if (CFG.DEBUG_LOG_REQUESTS) { - console.log("#####"); + print("#####"); request.requests.map((request) => { - console.log("Got request:", request.request_type); + print(request.request_type, 35); }).join(","); } @@ -97,6 +95,10 @@ export function onRequest(player, req) { return void 0; } + if (player.hasSignature === false) { + player.getDevicePlatform(); + } + if (!request.requests.length) { print("Received invalid request!", 31); return void 0; @@ -106,42 +108,31 @@ export function onRequest(player, req) { if (CFG.DEBUG_DUMP_TRAFFIC) { this.dumpTraffic(request, returns); } - let msg = this.envelopResponse(1, returns, request, player, !!request.auth_ticket); + let msg = this.envelopResponse(returns, request, player); player.sendResponse(msg); }); } /** - * @param {Number} status * @param {Array} returns - * @param {Request} req + * @param {Request} request * @param {Player} player - * @param {Boolean} auth * @return {Buffer} */ -export function envelopResponse(status, returns, req, player, auth) { +export function envelopResponse(returns, request, player) { - let buffer = req; + let buffer = request; delete buffer.requests; buffer.returns = returns; - // get players device platform one time - if (player.hasSignature === false && buffer.unknown6 && buffer.unknown6.unknown2) { - let sig = this.parseSignature(buffer); - if (sig.device_info !== void 0) { - player.isIOS = sig.device_info.device_brand === "Apple"; - player.isAndroid = !player.isIOS; - player.hasSignature = true; - player.asset_digest = this.assets[player.isAndroid ? "android" : "ios"]; - print(`${player.email} is playing with an ${player.isIOS ? "Apple" : "Android"} device!`, 36); - } + if (request.auth_ticket) { + print("Authenticate!", 31); + buffer.auth_ticket = AuthTicket(); } - if (auth) buffer.auth_ticket = AuthTicket(); - if (buffer.unknown6) { buffer.unknown6 = [{ "response_type": 6, @@ -153,7 +144,7 @@ export function envelopResponse(status, returns, req, player, auth) { }]; } - buffer.status_code = status; + buffer.status_code = 1; return ( POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.ResponseEnvelope") diff --git a/src/response.js b/src/response.js index 2eda83a..9c7b203 100644 --- a/src/response.js +++ b/src/response.js @@ -32,8 +32,6 @@ export function parseMessage(req, type) { */ export function processResponse(player, req) { - let buffer = null; - let cc = _toCC(req.request_type); let msg = this.parseMessage(req, cc); @@ -41,14 +39,26 @@ export function processResponse(player, req) { try { switch (req.request_type) { + // Player case "GET_PLAYER": - player.getPacket("GET_PLAYER", msg).then((result) => { + case "GET_INVENTORY": + case "GET_ASSET_DIGEST": + case "GET_HATCHED_EGGS": + case "CHECK_AWARDED_BADGES": + player.getPacket(req.request_type, msg).then((result) => { + print(`Success: ${req.request_type} => ${result.toString().length}`); resolve(result); }); return void 0; break; + // Global + case "GET_MAP_OBJECTS": case "CHECK_CHALLENGE": - player.world.getPacket("CHECK_CHALLENGE", msg).then((result) => { + case "DOWNLOAD_SETTINGS": + case "DOWNLOAD_REMOTE_CONFIG_VERSION": + case "DOWNLOAD_ITEM_TEMPLATES": + player.world.getPacket(req.request_type, msg).then((result) => { + print(`Success: ${req.request_type} => ${result.toString().length}`); resolve(result); }); return void 0; @@ -61,8 +71,6 @@ export function processResponse(player, req) { print(`Response error: ${e}`, 31); }; - resolve(buffer); - }); } \ No newline at end of file diff --git a/src/setup.js b/src/setup.js index cf864a3..4de954e 100644 --- a/src/setup.js +++ b/src/setup.js @@ -6,7 +6,7 @@ import POGOProtos from "pokemongo-protobuf"; import print from "./print"; import CFG from "../cfg"; -import * as master from "./master"; +import * as shared from "./shared"; import GameMaster from "./models/GameMaster"; @@ -36,9 +36,8 @@ export function setup() { print(`Downloaded assets are valid! Proceeding..`); - master.GAME_MASTER = this.parseGameMaster(); - - this.master = POGOProtos.serialize(fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master"), "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse"); + let parsedMaster = this.parseGameMaster(); + shared.GAME_MASTER = new GameMaster(parsedMaster); this.setupDatabaseConnection().then(() => { if (CFG.PORT < 1) { @@ -98,10 +97,10 @@ export function validateModels() { } else { let buffer = fs.readFileSync(path + "asset_digest"); - this.assets[name] = { + shared.GAME_ASSETS[name] = { buffer: buffer, decode: this.parseProtobuf(buffer, "POGOProtos.Networking.Responses.GetAssetDigestResponse") - } + }; } // validate models inside folder @@ -130,7 +129,6 @@ export function parseGameMaster() { try { let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master"); master = this.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse"); - let Master = new GameMaster(master); } catch (e) { print(e, 31); } diff --git a/src/shared.js b/src/shared.js new file mode 100644 index 0000000..3ef52cc --- /dev/null +++ b/src/shared.js @@ -0,0 +1,6 @@ +/** + * Global shared assets + */ + +export let GAME_ASSETS = {}; +export let GAME_MASTER = null; \ No newline at end of file diff --git a/src/utils.js b/src/utils.js index 0bfe004..396ed48 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,3 +1,6 @@ +import POGOProtos from "pokemongo-protobuf"; +import pcrypt from "pcrypt"; + import CFG from "../cfg"; /** @@ -79,4 +82,14 @@ export function validUsername(str) { return ( !!(rx_username.test(str)) ); +} + +/** + * @param {Request} req + */ +export function parseSignature(req) { + let key = pcrypt.decrypt(req.unknown6[0].unknown2.encrypted_signature); + return ( + POGOProtos.parseWithUnknown(key, "POGOProtos.Networking.Envelopes.Signature") + ); } \ No newline at end of file From 1a3a6fd181f88adf18856a38e75413def4c8ce7b Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 28 Aug 2016 21:32:41 +0200 Subject: [PATCH 5/6] Update - Added add pokestop api call - Fixed candies table bug - Added FortSearch, FortDetails - Unique fort ids gets properly generated - Cell id based map objects --- src/api.js | 95 +++++++++++++++++++---- src/db/tables/users.table | 2 +- src/models/World/index.js | 14 +++- src/models/World/packets/FortDetails.js | 35 +++++++++ src/models/World/packets/FortSearch.js | 32 ++++++++ src/models/World/packets/GetMapObjects.js | 65 ++++++++++++---- src/models/World/packets/index.js | 2 + src/response.js | 2 + 8 files changed, 214 insertions(+), 33 deletions(-) create mode 100644 src/models/World/packets/FortDetails.js create mode 100644 src/models/World/packets/FortSearch.js diff --git a/src/api.js b/src/api.js index 7bf6a1a..cb82875 100644 --- a/src/api.js +++ b/src/api.js @@ -1,10 +1,13 @@ import fs from "fs"; import url from "url"; import prompt from "prompt"; +import s2 from "s2-geometry"; import print from "./print"; import CFG from "../cfg"; +const S2Geo = s2.S2; + prompt.start({ message: " ", delimiter: " " @@ -36,13 +39,15 @@ export function processApiCall(req, res, route) { if (this.isApiCall(json)) { json.host = hoster; if (json.action === "login") { - let result = this["api_login"](json); - this.answerApiCall(res, JSON.stringify(result)); + this["api_login"](json).then((result) => { + this.answerApiCall(res, JSON.stringify(result)); + }); } else { if (this.apiClients[hoster]) { - let result = this["api_" + json.action](json); - this.answerApiCall(res, JSON.stringify(result)); + this["api_" + json.action](json).then((result) => { + this.answerApiCall(res, JSON.stringify(result)); + }); } else { print(`${hoster} isnt logged in! Kicking..`, 31); @@ -112,26 +117,36 @@ export function api_login(data) { timestamp: +new Date() }; } - return ({ - success: success + + return new Promise((resolve) => { + resolve({ + success: success + }); }); + } export function api_heartBeat() { - return ({ - timestamp: +new Date() + return new Promise((resolve) => { + resolve({ + timestamp: +new Date() + }); }); } export function api_getConnectedPlayers() { - return ({ - connected_players: this.world.connectedPlayers + return new Promise((resolve) => { + resolve({ + connected_players: this.world.connectedPlayers + }); }); } export function api_getServerVersion() { - return ({ - version: CFG.VERSION + return new Promise((resolve) => { + resolve({ + version: CFG.VERSION + }); }); } @@ -139,7 +154,59 @@ export function api_spawnPkmnToPlayer(data) { let name = String(data.player); let pkmn = String(data.pkmn).toUpperCase(); print(`Spawned 1x ${pkmn}'s to ${name}!`); - return ({ - success: true + return new Promise((resolve) => { + resolve({ + success: true + }); }); +} + +export function api_addFortToPosition(data) { + + let latitude = data.lat; + let longitude = data.lng; + + let name = data.name; + let description = data.desc; + + let cellId = S2Geo.keyToId(S2Geo.latLngToKey(latitude, longitude, data.zoom)); + + let query = ` + INSERT INTO forts + SET + cell_id=?, + latitude=?, + longitude=?, + enabled=?, + name=?, + description=?, + image_url=?, + rewards=? + `; + + let queryData = [ + cellId, + latitude, + longitude, + true, + name, + description, + "http://thecatapi.com/api/images/get?format=src&type=png", + "" + ]; + + return new Promise((resolve) => { + this.db.query(query, queryData, (res) => { + resolve({ + result: res, + cellId: cellId, + success: true + }); + }); + }); + +} + +export function api_getFortsByCellIds(data) { + console.log(data); } \ No newline at end of file diff --git a/src/db/tables/users.table b/src/db/tables/users.table index b777823..cc0f1a2 100644 --- a/src/db/tables/users.table +++ b/src/db/tables/users.table @@ -50,5 +50,5 @@ tutorial_avatar_selection tinyint(1) NOT NULL, tutorial_pokemon_capture tinyint(1) NOT NULL, tutorial_name_selection tinyint(1) NOT NULL, tutorial_first_time_exp tinyint(1) NOT NULL, -candies text(11) NOT NULL, +candies text NOT NULL, PRIMARY KEY (id) \ No newline at end of file diff --git a/src/models/World/index.js b/src/models/World/index.js index 453c64b..18593fa 100644 --- a/src/models/World/index.js +++ b/src/models/World/index.js @@ -142,8 +142,20 @@ export default class World { getPacket(type, msg) { return new Promise((resolve) => { switch (type) { + case "FORT_SEARCH": + this.FortSearch(msg).then((result) => { + resolve(result); + }); + break; + case "FORT_DETAILS": + this.FortDetails(msg).then((result) => { + resolve(result); + }); + break; case "GET_MAP_OBJECTS": - resolve(this.GetMapObjects(msg)); + this.GetMapObjects(msg).then((result) => { + resolve(result); + }); break; case "CHECK_CHALLENGE": resolve(this.CheckChallenge(msg)); diff --git a/src/models/World/packets/FortDetails.js b/src/models/World/packets/FortDetails.js new file mode 100644 index 0000000..88d8cd3 --- /dev/null +++ b/src/models/World/packets/FortDetails.js @@ -0,0 +1,35 @@ +import POGOProtos from "pokemongo-protobuf"; + +import print from "../../../print"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function FortDetails(msg) { + + let id = String(msg.fort_id); + let number = id.substring(id.lastIndexOf(".") + 1); + + return new Promise((resolve) => { + this.instance.getQueryByColumnFromTable("id", number, "forts").then((forts) => { + let fort = forts[0]; + let buffer = { + fort_id: id, + name: fort.name, + description: fort.description, + image_urls: [ + fort.image_url + ], + type: "CHECKPOINT", + latitude: fort.latitude, + longitude: fort.longitude, + modifiers: [] + }; + resolve( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortDetailsResponse") + ); + }); + }); + +} \ No newline at end of file diff --git a/src/models/World/packets/FortSearch.js b/src/models/World/packets/FortSearch.js new file mode 100644 index 0000000..5dda307 --- /dev/null +++ b/src/models/World/packets/FortSearch.js @@ -0,0 +1,32 @@ +import POGOProtos from "pokemongo-protobuf"; + +import print from "../../../print"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function FortSearch(msg) { + + let id = String(msg.fort_id); + let number = id.substring(id.lastIndexOf(".") + 1); + + return new Promise((resolve) => { + this.instance.getQueryByColumnFromTable("id", number, "forts").then((forts) => { + let buffer = ({ + result: "SUCCESS", + items_awarded: [{ + "item_id": 3, + "item_count": 1 + }], + experience_awarded: 1337, + cooldown_complete_timestamp_ms: +new Date() + 5e3, + chain_hack_sequence_number: 2 + }); + resolve( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortSearchResponse") + ); + }); + }); + +} \ No newline at end of file diff --git a/src/models/World/packets/GetMapObjects.js b/src/models/World/packets/GetMapObjects.js index 9b0b0b1..6f8146c 100644 --- a/src/models/World/packets/GetMapObjects.js +++ b/src/models/World/packets/GetMapObjects.js @@ -1,5 +1,11 @@ import POGOProtos from "pokemongo-protobuf"; +import s2 from "s2-geometry"; + +import print from "../../../print"; + +const S2Geo = s2.S2; + /** * @param {Object} msg * @return {Buffer} @@ -16,23 +22,48 @@ export default function GetMapObjects(msg) { map_cells: cells }; - for (let cell in msg.cell_id) { - cells.push({ - s2_cell_id: cell, - current_timestamp_ms: +new Date(), - forts: [], - spawn_points: [], - deleted_objects: [], - fort_summaries: [], - decimated_spawn_points: [], - wild_pokemons: [], - catchable_pokemons: [], - nearby_pokemons: [] - }); - }; + let limit = msg.cell_id.length; - return ( - POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse") - ); + // TODO: oop, forts are world objects! + + return new Promise((resolve) => { + var self = this; + function getForts(index) { + self.instance.getQueryByColumnFromTable("cell_id", msg.cell_id[index], "forts").then((items) => { + items = items || []; + let forts = []; + items.map((fort) => { + forts.push({ + id: fort.cell_id + "." + fort.id, + last_modified_timestamp_ms: "1471621873766", + latitude: fort.latitude, + longitude: fort.longitude, + enabled: true, + type: "CHECKPOINT" + }); + }); + cells.push({ + s2_cell_id: msg.cell_id[index], + current_timestamp_ms: +new Date(), + forts: forts, + spawn_points: [], + deleted_objects: [], + fort_summaries: [], + decimated_spawn_points: [], + wild_pokemons: [], + catchable_pokemons: [], + nearby_pokemons: [] + }); + if (index >= limit) { + resolve( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse") + ); + return void 0; + } + getForts(++index); + }); + }; + getForts(0); + }); } \ No newline at end of file diff --git a/src/models/World/packets/index.js b/src/models/World/packets/index.js index de55e09..99f977a 100644 --- a/src/models/World/packets/index.js +++ b/src/models/World/packets/index.js @@ -1,3 +1,5 @@ +export FortSearch from "./FortSearch"; +export FortDetails from "./FortDetails"; export GetMapObjects from "./GetMapObjects"; export CheckChallenge from "./CheckChallenge"; export DownloadSettings from "./DownloadSettings"; diff --git a/src/response.js b/src/response.js index 9c7b203..4708be7 100644 --- a/src/response.js +++ b/src/response.js @@ -52,6 +52,8 @@ export function processResponse(player, req) { return void 0; break; // Global + case "FORT_SEARCH": + case "FORT_DETAILS": case "GET_MAP_OBJECTS": case "CHECK_CHALLENGE": case "DOWNLOAD_SETTINGS": From e0538b22b84e9c083429723248aa96352132303f Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 29 Aug 2016 19:27:19 +0200 Subject: [PATCH 6/6] Update - Anticache for fort images - Show forts as markers on gmaps api - Added doubleclick on blank map to create fort - Added doubclick on fort marker to delete fort - Check for newer version on boot - Moved various things - Improved fort handling - Extended api --- src/api.js | 95 ++++++----- src/index.js | 42 ++++- src/models/Player/Bag/index.js | 4 +- src/models/Player/Info/index.js | 5 + src/models/Player/Party/index.js | 3 +- src/models/Player/index.js | 67 +------- src/models/Pokemon/index.js | 2 +- src/models/World/Fort/index.js | 107 ++++++++++++ src/models/World/MapObject/index.js | 29 +++- src/models/World/index.js | 188 ++++++++++++++++++++++ src/models/World/packets/FortDetails.js | 8 +- src/models/World/packets/FortSearch.js | 15 +- src/models/World/packets/GetMapObjects.js | 40 ++--- src/modes/index.js | 4 +- src/request.js | 43 ++++- src/response.js | 3 +- 16 files changed, 499 insertions(+), 156 deletions(-) create mode 100644 src/models/World/Fort/index.js diff --git a/src/api.js b/src/api.js index cb82875..2879c7b 100644 --- a/src/api.js +++ b/src/api.js @@ -1,11 +1,15 @@ import fs from "fs"; import url from "url"; -import prompt from "prompt"; import s2 from "s2-geometry"; +import prompt from "prompt"; import print from "./print"; import CFG from "../cfg"; +import { + getHashCodeFrom +} from "./utils"; + const S2Geo = s2.S2; prompt.start({ @@ -162,51 +166,62 @@ export function api_spawnPkmnToPlayer(data) { } export function api_addFortToPosition(data) { - - let latitude = data.lat; - let longitude = data.lng; - - let name = data.name; - let description = data.desc; - - let cellId = S2Geo.keyToId(S2Geo.latLngToKey(latitude, longitude, data.zoom)); - - let query = ` - INSERT INTO forts - SET - cell_id=?, - latitude=?, - longitude=?, - enabled=?, - name=?, - description=?, - image_url=?, - rewards=? - `; - - let queryData = [ - cellId, - latitude, - longitude, - true, - name, - description, - "http://thecatapi.com/api/images/get?format=src&type=png", - "" - ]; - return new Promise((resolve) => { - this.db.query(query, queryData, (res) => { + this.world.insertFort(data).then((fort) => { resolve({ - result: res, - cellId: cellId, success: true }); }); }); - } -export function api_getFortsByCellIds(data) { - console.log(data); +export function api_deleteFortById(data) { + let cellId = data.cell_id; + let uid = data.cell_uid; + return new Promise((resolve) => { + this.world.getFortById(cellId, uid).then((fort) => { + fort.deleted = true; + this.world.processDeletedFort(fort); + }); + }); +} + +export function getNeighbors(lvl, lat, lng) { + let origin = S2Geo.latLngToKey(lat, lng, lvl); + let walk = [S2Geo.keyToId(origin)]; + let next = S2Geo.nextKey(origin); + let prev = S2Geo.prevKey(origin); + let ii = 0; + let length = 20; + for (; ii < length; ++ii) { + walk.push(S2Geo.toId(prev)); + walk.push(S2Geo.toId(next)); + next = S2Geo.nextKey(next); + prev = S2Geo.prevKey(prev); + }; + return (walk); +} + +export function getNeighboredForts(cells, out, index) { + return new Promise((resolve) => { + let id = cells[index]; + this.world.getFortsByCell(id).then((res) => { + out = out.concat(res.forts); + if (++index >= cells.length) resolve(out); + else resolve(this.getNeighboredForts(cells, out, index)); + }); + }); +} + +export function api_getFortsByPosition(data) { + return new Promise((resolve) => { + let lat = data.lat; + let lng = data.lng; + this.getNeighboredForts(this.getNeighbors(15, lat, lng), [], 0).then((res) => { + resolve({ + forts: res, + success: true + }); + }); + }); } \ No newline at end of file diff --git a/src/index.js b/src/index.js index c69cf6d..2a4990a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import fs from "fs"; import os from "os"; +import request from "request"; import POGOProtos from "pokemongo-protobuf"; import { @@ -58,12 +59,47 @@ export default class GameServer { if (CFG.GREET) this.greet(); - print(`Booting Server v${require("../package.json").version}-dev`, 33); + this.getLatestVersion().then((latest) => { + let current = require("../package.json").version; + print(`Booting Server v${current}`, 33); + if (current < latest) { + print(`WARNING: Please update to the latest build v${latest}!`, 33); + } + this.world = new World(this); + this.setup(); + }); - this.world = new World(this); + } - this.setup(); + fetchVersioningUrl() { + return new Promise((resolve) => { + let url = ""; + let branch = "dev"; + let base = "https://raw.githubusercontent.com"; + url = require("../package.json").repository.url; + url = url.replace("git://", ""); + url = url.replace(".git", ""); + url = url.replace("github.com/", ""); + url = `${base}/${url}/${branch}/package.json`; + resolve(url); + }); + } + getLatestVersion() { + return new Promise((resolve) => { + this.fetchVersioningUrl().then((url) => { + request({url: url}, (error, response, body) => { + let json = null; + try { + json = JSON.parse(body); + } catch (e) { + json = {}; + print(e, 31); + } + resolve(json.version); + }); + }); + }); } /** diff --git a/src/models/Player/Bag/index.js b/src/models/Player/Bag/index.js index 128ead3..a953db5 100644 --- a/src/models/Player/Bag/index.js +++ b/src/models/Player/Bag/index.js @@ -1,5 +1,3 @@ -import Candy from "./Candy"; - /** * @class Bag */ @@ -8,7 +6,7 @@ export default class Bag { /** @constructor */ constructor() { - this.candies = new CandyBag(); + //this.candies = new CandyBag(); this.stardust = 0; this.pokecoins = 0; diff --git a/src/models/Player/Info/index.js b/src/models/Player/Info/index.js index 093ca5d..2af8af2 100644 --- a/src/models/Player/Info/index.js +++ b/src/models/Player/Info/index.js @@ -8,6 +8,8 @@ export default class Info { this.lvl = 0; + this.team = 0; + this.exp = 0; this.prevLvlExp = 0; this.nextLvlExp = 0; @@ -22,6 +24,9 @@ export default class Info { this.bigMagikarpCaught = 0; this.pkmnDeployed = 0; + this.maxPkmnStorage = 250; + this.maxItemStorage = 350; + } upgradeLevel() { diff --git a/src/models/Player/Party/index.js b/src/models/Player/Party/index.js index 370b8be..7a1e7ff 100644 --- a/src/models/Player/Party/index.js +++ b/src/models/Player/Party/index.js @@ -38,11 +38,12 @@ export default class Party { /** * @param {Number} id + * @return {Pokemon} */ deletePkmn(id) { let index = this.getPkmnIndexById(id); let pkmn = this.party[index]; - if (pkmn) this.party.splice(index, 1); + if (pkmn) this.party.splice(index, 1)[0]; } /** diff --git a/src/models/Player/index.js b/src/models/Player/index.js index 89881d7..e30b0e6 100644 --- a/src/models/Player/index.js +++ b/src/models/Player/index.js @@ -1,6 +1,6 @@ import POGOProtos from "pokemongo-protobuf"; -import jwtDecode from "jwt-decode"; +import Bag from "./Bag"; import Info from "./Info"; import Party from "./Party"; import Avatar from "./Avatar"; @@ -37,7 +37,6 @@ export default class Player extends MapObject { this.world = obj.world; - this._team = 0; this._email = null; this.username = "unknown"; @@ -61,33 +60,20 @@ export default class Player extends MapObject { this.remoteAddress = null; - this.maxPkmnStorage = 250; - this.maxItemStorage = 350; - + this.bag = new Bag(this); this.info = new Info(this); this.party = new Party(this); this.avatar = new Avatar(this); this.contact = new Contact(this); - this.currency = new Currency(this); this.tutorial = new Tutorial(this); - -/* - this.bag = new Bag(this); - this.info = new Info(this); + this.currency = new Currency(this); + /* this.pokedex = new Pokedex(this); -*/ - + */ this.refreshSocket(obj.request, obj.response); } - get team() { - return (this._team); - } - set team(value) { - this._team = value; - } - get email() { return (this._email); } @@ -112,50 +98,8 @@ export default class Player extends MapObject { this.response = res; } - authenticate() { - - let request = this.request; - - let msg = this.GetAuthTicket(request.request_id); - - let token = request.auth_info; - - if (!token || !token.provider) { - print("Invalid authentication token! Kicking..", 31); - this.world.removePlayer(this); - return void 0; - } - - if (token.provider === "google") { - if (token.token !== null) { - let decoded = jwtDecode(token.token.contents); - this.email = decoded.email; - this.email_verified = decoded.email_verified; - this.isGoogleAccount = true; - print(`${this.email} connected!`, 36); - } - else { - print("Invalid authentication token! Kicking..", 31); - this.world.removePlayer(this); - return void 0; - } - } - else { - print("Invalid provider! Kicking..", 31); - this.world.removePlayer(this); - return void 0; - } - - this.authenticated = true; - - this.sendResponse(msg); - - } - getDevicePlatform() { - let request = this.request; - if (request.unknown6 && request.unknown6[0]) { let sig = parseSignature(request); if (sig.device_info !== void 0) { @@ -166,7 +110,6 @@ export default class Player extends MapObject { print(`${this.email} is playing with an ${this.isIOS ? "Apple" : "Android"} device!`, 36); } } - } /** diff --git a/src/models/Pokemon/index.js b/src/models/Pokemon/index.js index 79ba71e..e1b6f48 100644 --- a/src/models/Pokemon/index.js +++ b/src/models/Pokemon/index.js @@ -109,7 +109,7 @@ export default class Pokemon extends MapObject { /** * @return {Object} */ - getPokemonData() { + serialize() { return ({ pokemon_id: this.dexNumber, cp: this.cp, diff --git a/src/models/World/Fort/index.js b/src/models/World/Fort/index.js new file mode 100644 index 0000000..5b7bdbf --- /dev/null +++ b/src/models/World/Fort/index.js @@ -0,0 +1,107 @@ +import MapObject from "../MapObject"; + +import print from "../../../print"; +import CFG from "../../../../cfg"; + +import { + validName +} from "../../../utils"; + +/** + * @class Fort + */ +export default class Fort extends MapObject { + + /** + * @param {Object} obj + * @constructor + */ + constructor(obj) { + + super(obj); + + this.enabled = true; + this.deleted = false; + + this.name = null; + this.description = null; + + this.image_url = null; + + this.cooldown = 5e3; + + this.experience = 0; + + this.rewards = this.parseRewards(obj.rewards); + + this.init(obj); + + } + + /** + * @param {Object} obj + */ + init(obj) { + obj = obj || {}; + for (let key in obj) { + if (this.hasOwnProperty(key)) { + this[key] = obj[key]; + } + }; + } + + /** + * @return {Object} + */ + parseRewards(rewards) { + rewards = rewards || "{}"; + let json = null; + try { + json = JSON.parse(rewards); + } catch (e) { + json = {}; + print(e, 31); + } + return (json); + } + + /** + * @return {Array} + */ + serializeRewards() { + let out = []; + for (let key in this.rewards) { + let amount = this.rewards[key] << 0; + for (let ii = 0; ii < amount; ++ii) { + out.push({ + item_id: key + }); + }; + }; + return (out); + } + + /** + * @return {String} + */ + getId() { + return ( + this.cellId + "." + this.uid + ); + } + + /** + * @return {Object} + */ + serialize() { + return ({ + id: this.getId(), + last_modified_timestamp_ms: +new Date(), + latitude: this.latitude, + longitude: this.longitude, + enabled: this.enabled, + type: "CHECKPOINT" + }); + } + +} \ No newline at end of file diff --git a/src/models/World/MapObject/index.js b/src/models/World/MapObject/index.js index b616555..b055f9d 100644 --- a/src/models/World/MapObject/index.js +++ b/src/models/World/MapObject/index.js @@ -3,15 +3,40 @@ */ export default class MapObject { - /** @constructor */ - constructor() { + /** + * @param {Object} obj + * @constructor + */ + constructor(obj) { this.uid = 0; + this.cellId = null; + this.altitude = 0; this.latitude = 0; this.longitude = 0; + this._init(obj); + + } + + /** + * @param {Object} obj + */ + _init(obj) { + obj = obj || {}; + for (let key in obj) { + if (this.hasOwnProperty(key)) { + this[key] = obj[key]; + } + else if (key === "cell_id") { + this.cellId = obj[key]; + } + else if (key === "cell_uid") { + this.uid = obj[key]; + } + }; } } \ No newline at end of file diff --git a/src/models/World/index.js b/src/models/World/index.js index 18593fa..940d25c 100644 --- a/src/models/World/index.js +++ b/src/models/World/index.js @@ -1,11 +1,21 @@ +import s2 from "s2-geometry"; +import Fort from "./Fort"; import Player from "../Player"; +import Settings from "../../modes"; + import CFG from "../../../cfg"; +import print from "../../print"; + import * as _packets from "./packets"; import { inherit } from "../../utils"; +const S2Geo = s2.S2; + +const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds; + /** * @class World */ @@ -20,6 +30,8 @@ export default class World { this.players = []; + this.cells = {}; + } get connectedPlayers() { @@ -135,6 +147,182 @@ export default class World { } + /** + * @param {String} id + * @return {Object} + */ + getCellById(id) { + if (!this.cells[id]) { + this.cells[id] = { + id: id, + forts: [], + synced: false + }; + } + return (this.cells[id]); + } + + getFortsByCells(cells, out, index) { + return new Promise((resolve) => { + this.getFortsByCell(cells[index]).then((result) => { + out.push(result); + if (++index >= cells.length) { + print(`Syncing forts with database!`, 33); + resolve(out); + } + else resolve(this.getFortsByCells(cells, out, index)); + }); + }); + } + + /** + * Dirty hack to display disappearing forts + * @param {Fort} fort + */ + processDeletedFort(fort) { + if (fort.deleted) { + fort.latitude = 0; + fort.longitude = 0; + this.instance.db.query("DELETE FROM forts WHERE cell_id=? AND cell_uid=? LIMIT 1", [fort.cellId, fort.uid], (e, res) => { + setTimeout(() => { + this.deleteFortById(fort.cellId, fort.uid); + }, MAP_REFRESH_RATE + 2e3); + }); + } + } + + /** + * @param {String} id + * @param {Number} uid + * @return {Number} + */ + getFortIndexById(id, uid) { + let cell = this.getCellById(id); + let index = 0; + if (!cell) return (-1); + for (let fort of cell.forts) { + if (fort.uid === uid) return (index); + ++index; + }; + return (-1); + } + + /** + * @param {String} id + * @param {Number} uid + * @return {Fort} + */ + getFortById(id, uid) { + let cell = this.getCellById(id); + let index = this.getFortIndexById(id, uid); + return new Promise((resolve) => { + if (!cell.forts[index]) { + this.getFortsByCell(id).then((forts) => { + resolve(this.getFortById(id, uid)); + }); + } + else { + resolve(cell.forts[index]); + } + }); + } + + /** + * @param {String} id + * @param {Number} uid + * @return {Fort} + */ + deleteFortById(id, uid) { + let cell = this.getCellById(id); + let index = this.getFortIndexById(id, uid); + if (index < 0) return (null); + return (cell.forts.splice(index, 1)); + } + + /** + * @param {Object} obj + */ + insertFort(obj) { + + let lat = obj.latitude; + let lng = obj.longitude; + + let id = S2Geo.keyToId(S2Geo.latLngToKey(lat, lng, obj.zoom)); + + let name = obj.name || ""; + let desc = obj.description || ""; + + let img = "http://thecatapi.com/api/images/get?format=src&type=png&d=" + +new Date(); + + let query = "INSERT INTO forts SET cell_id=?, cell_uid=?, latitude=?, longitude=?, name=?, description=?, image_url=?, rewards=?"; + + obj.cell_id = id; + + return new Promise((resolve) => { + this.instance.db.query(query, [id, 0, lat, lng, name, desc, img, ""], (e, res) => { + let insertId = res.insertId; + this.instance.db.query("SELECT * from forts WHERE cell_id=? ORDER BY cell_uid DESC", [id], (e, res) => { + let index = res instanceof Array ? res[0].cell_uid + 1 : 0; + this.instance.db.query("UPDATE forts SET cell_uid=? WHERE id=?", [index, insertId], (e, res) => { + obj.cell_uid = index; + resolve(this.addFort(obj)); + }); + }); + }); + }); + + } + + /** + * @param {Object} obj + * @return {Fort} + */ + addFort(obj) { + let cell = this.getCellById(obj.cell_id); + let fort = new Fort(obj); + cell.forts.push(fort); + return (fort); + } + + /** + * @param {String} cell + * @return {Array} + */ + getFortsByCell(cellId) { + return new Promise((resolve) => { + let cell = this.getCellById(cellId); + // cached + if (cell && cell.synced) { + cell.forts.map((fort) => { + this.processDeletedFort(fort); + }); + resolve(cell); + } + // load from db + else this.loadFortsFromDbByCell(cellId).then((forts) => { + let result = this.getCellById(cellId); + forts.map((fort) => { + let index = result.forts.push(new Fort(fort)) - 1; + this.processDeletedFort(result.forts[index]); + }); + cell.synced = true; + resolve(result); + }); + }); + } + + /** + * @param {String} cell + */ + loadFortsFromDbByCell(cell) { + return new Promise((resolve) => { + this.instance.getQueryByColumnFromTable("cell_id", cell, "forts").then((forts) => { + forts = forts || []; + resolve(forts); + }); + }); + } + /** * @param {String} type * @param {Object} msg diff --git a/src/models/World/packets/FortDetails.js b/src/models/World/packets/FortDetails.js index 88d8cd3..a316d59 100644 --- a/src/models/World/packets/FortDetails.js +++ b/src/models/World/packets/FortDetails.js @@ -8,14 +8,14 @@ import print from "../../../print"; */ export default function FortDetails(msg) { - let id = String(msg.fort_id); - let number = id.substring(id.lastIndexOf(".") + 1); + let id = msg.fort_id.split("."); return new Promise((resolve) => { - this.instance.getQueryByColumnFromTable("id", number, "forts").then((forts) => { + this.instance.db.query("SELECT * from forts WHERE cell_id=? AND cell_uid=?", [id[0], id[1]], (e, forts) => { let fort = forts[0]; + if (!fort) return void 0; let buffer = { - fort_id: id, + fort_id: msg.fort_id, name: fort.name, description: fort.description, image_urls: [ diff --git a/src/models/World/packets/FortSearch.js b/src/models/World/packets/FortSearch.js index 5dda307..7fe9378 100644 --- a/src/models/World/packets/FortSearch.js +++ b/src/models/World/packets/FortSearch.js @@ -8,17 +8,18 @@ import print from "../../../print"; */ export default function FortSearch(msg) { - let id = String(msg.fort_id); - let number = id.substring(id.lastIndexOf(".") + 1); + let id = msg.fort_id.split("."); return new Promise((resolve) => { - this.instance.getQueryByColumnFromTable("id", number, "forts").then((forts) => { + this.instance.db.query("SELECT * from forts WHERE cell_id=? AND cell_uid=?", [id[0], id[1]], (e, forts) => { + let fort = forts[0]; + if (!fort) return void 0; let buffer = ({ result: "SUCCESS", - items_awarded: [{ - "item_id": 3, - "item_count": 1 - }], + items_awarded: [ + { item_id: 3 }, + { item_id: 4 } + ], experience_awarded: 1337, cooldown_complete_timestamp_ms: +new Date() + 5e3, chain_hack_sequence_number: 2 diff --git a/src/models/World/packets/GetMapObjects.js b/src/models/World/packets/GetMapObjects.js index 6f8146c..28749cb 100644 --- a/src/models/World/packets/GetMapObjects.js +++ b/src/models/World/packets/GetMapObjects.js @@ -15,11 +15,11 @@ export default function GetMapObjects(msg) { let latitude = msg.latitude; let longitude = msg.longitude; - let cells = []; + let mapCells = []; let buffer = { status: "SUCCESS", - map_cells: cells + map_cells: mapCells }; let limit = msg.cell_id.length; @@ -27,25 +27,12 @@ export default function GetMapObjects(msg) { // TODO: oop, forts are world objects! return new Promise((resolve) => { - var self = this; - function getForts(index) { - self.instance.getQueryByColumnFromTable("cell_id", msg.cell_id[index], "forts").then((items) => { - items = items || []; - let forts = []; - items.map((fort) => { - forts.push({ - id: fort.cell_id + "." + fort.id, - last_modified_timestamp_ms: "1471621873766", - latitude: fort.latitude, - longitude: fort.longitude, - enabled: true, - type: "CHECKPOINT" - }); - }); - cells.push({ - s2_cell_id: msg.cell_id[index], + this.getFortsByCells(msg.cell_id, [], 0).then((cells) => { + cells.map((cell) => { + mapCells.push({ + s2_cell_id: cell.id, current_timestamp_ms: +new Date(), - forts: forts, + forts: cell.forts.map((fort) => { return fort.serialize() }), spawn_points: [], deleted_objects: [], fort_summaries: [], @@ -54,16 +41,11 @@ export default function GetMapObjects(msg) { catchable_pokemons: [], nearby_pokemons: [] }); - if (index >= limit) { - resolve( - POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse") - ); - return void 0; - } - getForts(++index); }); - }; - getForts(0); + resolve( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse") + ); + }); }); } \ No newline at end of file diff --git a/src/modes/index.js b/src/modes/index.js index f8e134d..72392cc 100644 --- a/src/modes/index.js +++ b/src/modes/index.js @@ -14,8 +14,8 @@ export default { pokemon_visible_range: 70.00196078431372, poke_nav_range_meters: 751.0156862745098, encounter_range_meters: 50.25098039215686, - get_map_objects_min_refresh_seconds: 10.007843017578125, - get_map_objects_max_refresh_seconds: 11.01568603515625, + get_map_objects_min_refresh_seconds: 0, + get_map_objects_max_refresh_seconds: 0, get_map_objects_min_distance_meters: 10.007843017578125, google_maps_api_key: CFG.GMAPS_KEY }, diff --git a/src/request.js b/src/request.js index 16abd7e..a7899fb 100644 --- a/src/request.js +++ b/src/request.js @@ -1,5 +1,6 @@ import fs from "fs"; import url from "url"; +import jwtDecode from "jwt-decode"; import POGOProtos from "pokemongo-protobuf"; import print from "./print"; @@ -91,7 +92,7 @@ export function onRequest(player) { } if (!player.authenticated) { - player.authenticate(); + this.authenticatePlayer(player); return void 0; } @@ -114,6 +115,46 @@ export function onRequest(player) { } +/** + * @param {Player} player + */ +export function authenticatePlayer(player) { + + let request = player.request; + let token = request.auth_info; + let msg = player.GetAuthTicket(request.request_id); + + if (!token || !token.provider) { + print("Invalid authentication token! Kicking..", 31); + player.world.removePlayer(player); + return void 0; + } + + if (token.provider === "google") { + if (token.token !== null) { + let decoded = jwtDecode(token.token.contents); + player.email = decoded.email; + player.email_verified = decoded.email_verified; + player.isGoogleAccount = true; + print(`${player.email} connected!`, 36); + } + else { + print("Invalid authentication token! Kicking..", 31); + player.world.removePlayer(player); + return void 0; + } + } + else { + print("Invalid provider! Kicking..", 31); + player.world.removePlayer(player); + return void 0; + } + + player.authenticated = true; + player.sendResponse(msg); + +} + /** * @param {Array} returns * @param {Request} request diff --git a/src/response.js b/src/response.js index 4708be7..0a68407 100644 --- a/src/response.js +++ b/src/response.js @@ -33,7 +33,7 @@ export function parseMessage(req, type) { export function processResponse(player, req) { let cc = _toCC(req.request_type); - let msg = this.parseMessage(req, cc); + let msg = this.parseMessage(req, cc) || {}; return new Promise((resolve) => { @@ -59,6 +59,7 @@ export function processResponse(player, req) { case "DOWNLOAD_SETTINGS": case "DOWNLOAD_REMOTE_CONFIG_VERSION": case "DOWNLOAD_ITEM_TEMPLATES": + msg.player = player; player.world.getPacket(req.request_type, msg).then((result) => { print(`Success: ${req.request_type} => ${result.toString().length}`); resolve(result);