Merge pull request #92 from maierfelix/dev

Dev
This commit is contained in:
Felix Maier
2016-08-30 09:57:15 +02:00
committed by GitHub
96 changed files with 2484 additions and 3553 deletions

5
.gitignore vendored
View File

@@ -1,6 +1,3 @@
node_modules/
scripts/
logs/
data/
cfg.js
npm-debug.*
data/

16
.help
View File

@@ -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

1
.save Normal file
View File

@@ -0,0 +1 @@
{"isFirstRun":false,"allowedApiHosts":["localhost"],"loginDetails":{"username":"root","password":"13377"}}

View File

@@ -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,

2
install-windows.bat vendored Normal file
View File

@@ -0,0 +1,2 @@
set LIBPROTOBUF=%CD%\protobuf
npm install node-protobuf && npm install

View File

@@ -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,12 @@
"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",
"url": "^0.11.0",
"prompt": "^1.0.0",
"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"
},

2
run-windows.bat vendored
View File

@@ -1,2 +1,2 @@
npm install && npm run start
npm run start
pause

View File

@@ -1,44 +1,227 @@
import fs from "fs";
import url from "url";
import s2 from "s2-geometry";
import prompt from "prompt";
import print from "./print";
import CFG from "../cfg";
export function ready(e) {
if (e) console.log(e);
else console.log("Ready!");
import {
getHashCodeFrom
} from "./utils";
const S2Geo = s2.S2;
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) {
print(e, 31);
this.answerApiCall(res, "");
return void 0;
}
if (this.isApiCall(json)) {
json.host = hoster;
if (json.action === "login") {
this["api_login"](json).then((result) => {
this.answerApiCall(res, JSON.stringify(result));
});
}
else {
if (this.apiClients[hoster]) {
this["api_" + json.action](json).then((result) => {
this.answerApiCall(res, JSON.stringify(result));
});
}
else {
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");
print(`Successfully added ${hoster} to allowed API hosts!`);
this.processApiCall(req, res, route);
}
else {
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]) {
print(`API access for ${data.host} granted!`);
}
print(`${data.host} logged in!`, 36);
this.apiClients[data.host] = {
timestamp: +new Date()
};
}
return new Promise((resolve) => {
resolve({
success: success
});
});
}
/**
* @param {Player} player
*/
export function killPlayer(player) {
console.log(player.username + " killed!");
export function api_heartBeat() {
return new Promise((resolve) => {
resolve({
timestamp: +new Date()
});
});
}
/**
* @param {Player} player
*/
export function updatePlayerAvatar(player) {
console.log("Updated avatar of " + player.username + "!");
export function api_getConnectedPlayers() {
return new Promise((resolve) => {
resolve({
connected_players: this.world.connectedPlayers
});
});
}
export function api_getServerVersion() {
return new Promise((resolve) => {
resolve({
version: CFG.VERSION
});
});
}
export function api_spawnPkmnToPlayer(data) {
let name = String(data.player);
let pkmn = String(data.pkmn).toUpperCase();
print(`Spawned 1x ${pkmn}'s to ${name}!`);
return new Promise((resolve) => {
resolve({
success: true
});
});
}
export function api_addFortToPosition(data) {
return new Promise((resolve) => {
this.world.insertFort(data).then((fort) => {
resolve({
success: true
});
});
});
}
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
});
});
});
}

View File

@@ -1,3 +1,4 @@
import print from "./print";
import CFG from "../cfg";
export function startCycle() {
@@ -20,8 +21,6 @@ export function cycle() {
if (this.passedTicks <= 0) return void 0;
this.updatePlayers();
this.resetTimers();
return void 0;
@@ -49,7 +48,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 +56,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 = this.world.connectedPlayers;
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) {
print(`${player.remoteAddress} timed out`, 34);
this.savePlayer(player);
this.removePlayer(player);
}
};

View File

@@ -1,9 +1,11 @@
import fs from "fs";
import print from "../print";
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) => {
this.db.query(`SHOW TABLES LIKE '${name}';`, (e, rows, fields) => {
if (e) console.log(e);
else {
// exists
@@ -30,16 +32,16 @@ export function createTables() {
export function createTable(name) {
this.print(`Creating table ${name}`, 36);
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;
`;
return new Promise((resolve) => {
this.db.instance.query(query, (e, rows) => {
this.db.query(query, (e, rows) => {
if (e) console.log(e);
else resolve();
});

View File

@@ -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,44 +22,9 @@ 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);
});
});
}
/**
* @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);
});
});
}
}

View File

@@ -1,9 +1,9 @@
import fs from "fs";
import mysql from "mysql";
import print from "../print";
import CFG from "../../cfg";
export function setupConnection() {
export function setupDatabaseConnection() {
let connection = mysql.createConnection({
host : CFG.MYSQL_HOST_IP,
@@ -16,21 +16,21 @@ export function setupConnection() {
return new Promise((resolve) => {
connection.connect((error) => {
if (error) {
this.print("MySQL " + error, 31);
this.retry("Retrying again in ", () => this.setupConnection().then(resolve), 5);
print("MySQL " + error, 31);
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.print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`);
this.db = connection;
this.createTableIfNotExists(CFG.MYSQL_USERS_TABLE).then(() => {
this.createTableIfNotExists(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => {
print(`\x1b[36;1mMySQL\x1b[0m\x1b[32;1m connection established\x1b[0m`);
resolve();
});
});
});
connection.on("error", (error) => {
this.print("MySQL " + error, 31);
this.retry("Trying to reconnect in ", () => this.setupConnection().then(resolve), 5);
print("MySQL " + error, 31);
this.retry("Trying to reconnect in ", () => this.setupDatabaseConnection().then(resolve), 5);
});
});
@@ -40,106 +40,7 @@ export function setupConnection() {
* @param {Function} resolve
*/
export function closeConnection(resolve) {
this.db.instance.end(() => {
this.db.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();
});
};
});
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 NOT NULL,
PRIMARY KEY (id)

78
src/dump.js Normal file
View File

@@ -0,0 +1,78 @@
import fse from "fs-extra";
import POGOProtos from "pokemongo-protobuf";
import print from "./print";
import CFG from "../cfg";
import { _toCC } from "./utils";
/**
* @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) {
print("Dump traffic: " + e, 31);
}
}

38
src/http.js Normal file
View File

@@ -0,0 +1,38 @@
import http from "http";
import print from "./print";
import CFG from "../cfg";
/**
* @return {HTTP}
*/
export function createHTTPServer() {
let server = http.createServer((req, res) => {
if (this.world.isFull()) {
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.routeRequest(req, res);
});
});
server.listen(CFG.PORT);
return (server);
}
export function shutdown() {
this.socket.close(() => {
print("Closed http server!", 33);
this.closeConnection(() => {
print("Closed database connection!", 33);
print("Server shutdown!", 31);
setTimeout(() => process.exit(1), 2e3);
});
});
}

View File

@@ -1,21 +1,23 @@
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 request from "request";
import POGOProtos from "pokemongo-protobuf";
import {
inherit,
_toCC
} from "./utils";
import print from "./print";
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";
@@ -29,26 +31,21 @@ 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;
this.socket = null;
this.cycleInstance = null;
@@ -60,201 +57,81 @@ 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.setup();
}
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"]);
}
}
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);
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);
}
};
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);
});
this.world = new World(this);
this.setup();
});
server.listen(CFG.PORT);
return (server);
}
setupDatabaseConnection() {
fetchVersioningUrl() {
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;
};
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);
});
}
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);
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);
});
});
});
}
/**
* @param {String} msg
* @param {Number} color
* @param {Boolean} nl
* @param {Object} obj
* @return {Boolean}
*/
print(msg, color, nl) {
this.emit("print", msg, color, nl);
isApiCall(call) {
let action = String(call.action);
return (
"api_" + action in _api
);
}
/**
* @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);
print(`${msg}${timer}s`, 33, true);
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 +141,22 @@ 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) {
print(e, 31);
}
}
/**
* @param {String} path
* @return {Boolean}
*/
fileExists(path) {
try {
fs.statSync(path);
@@ -288,9 +172,11 @@ export default class GameServer extends EventEmitter {
}
inherit(GameServer, _api);
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 +185,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 +197,4 @@ inherit(GameServer, _mysql_create);
server.uncaughtException(data);
});
})(GameServer);
})();

View File

@@ -0,0 +1,84 @@
import POGOProtos from "pokemongo-protobuf";
/**
* @class GameMaster
*/
export default class GameMaster {
/**
* @param {Object} decode
* @constructor
*/
constructor(decode) {
this.settings = this.buildSettings();
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.decode.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
);
}
/**
* @return {Buffer}
*/
serialize() {
return (
this.buffer
);
}
}

View File

@@ -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
//
});
}

View File

@@ -0,0 +1,137 @@
/**
* @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;
}
}
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
});
}
}

View File

@@ -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}
*/
serialize() {
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);
}
}

View File

@@ -0,0 +1,70 @@
/**
* @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);
}
}

View File

@@ -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
});
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,94 @@
/**
* @class Info
*/
export default class Info {
/** @constructor */
constructor() {
this.lvl = 0;
this.team = 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;
this.maxPkmnStorage = 250;
this.maxItemStorage = 350;
}
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;
}
}
serialize() {
return ({
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
});
}
}

View File

@@ -0,0 +1,68 @@
import Pokemon from "../../Pokemon";
/**
* @class Party
*/
export default 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
* @return {Pokemon}
*/
deletePkmn(id) {
let index = this.getPkmnIndexById(id);
let pkmn = this.party[index];
if (pkmn) this.party.splice(index, 1)[0];
}
/**
* @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);
}
}

View File

@@ -0,0 +1,31 @@
/**
* @class Tutorial
*/
export default class Tutorial {
/** @constructor */
constructor() {
this.states = [];
}
skipTutorial() {
this.states = [
"LEGAL_SCREEN",
"AVATAR_SELECTION",
"POKEMON_CAPTURE",
"NAME_SELECTION",
"FIRST_TIME_EXPERIENCE_COMPLETE"
];
}
/**
* @return {Array}
*/
serialize() {
this.skipTutorial();
return (
this.states
);
}
}

143
src/models/Player/index.js Normal file
View File

@@ -0,0 +1,143 @@
import POGOProtos from "pokemongo-protobuf";
import Bag from "./Bag";
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,
parseSignature
} from "../../utils";
import { GAME_MASTER } from "../../shared";
/**
* @class Player
*/
export default class Player extends MapObject {
/**
* @param {Object} obj
* @constructor
*/
constructor(obj) {
super(null);
this.world = obj.world;
this._email = null;
this.username = "unknown";
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;
this.request = null;
this.response = null;
this.remoteAddress = null;
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.tutorial = new Tutorial(this);
this.currency = new Currency(this);
/*
this.pokedex = new Pokedex(this);
*/
this.refreshSocket(obj.request, obj.response);
}
get email() {
return (this._email);
}
set email(value) {
this._email = value;
this.username = value.replace("@gmail.com", "");
}
/**
* @param {Buffer} buffer
*/
sendResponse(buffer) {
this.response.end(buffer);
}
/**
* @param {Request} req
* @param {Response} res
*/
refreshSocket(req, res) {
this.request = POGOProtos.parseWithUnknown(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope");
this.response = res;
}
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
*/
getPacket(type, msg) {
return new Promise((resolve) => {
switch (type) {
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;
};
});
}
}
inherit(Player, _packets);

View File

@@ -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")
);
}

View File

@@ -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
);
}

View File

@@ -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")
);
}

View File

@@ -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")
);
}

View File

@@ -1,47 +1,33 @@
import POGOProtos from "pokemongo-protobuf";
export 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")
);
}

View File

@@ -0,0 +1,28 @@
import POGOProtos from "pokemongo-protobuf";
/**
* @param {Object} msg
* @return {Buffer}
*/
export default function GetPlayer(msg) {
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.currency.serialize()
};
return (
POGOProtos.serialize({ success: true, player_data: buffer }, "POGOProtos.Networking.Responses.GetPlayerResponse")
);
}

View File

@@ -0,0 +1,9 @@
import POGOProtos from "pokemongo-protobuf";
export default function GetPlayerProfile() {
return (
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetPlayerProfileResponse")
);
}

View File

@@ -0,0 +1,7 @@
export GetPlayer from "./GetPlayer";
export GetInventory from "./GetInventory";
export GetAuthTicket from "./GetAuthTicket";
export GetHatchedEggs from "./GetHatchedEggs";
export GetAssetDigest from "./GetAssetDigest";
export GetPlayerProfile from "./GetPlayerProfile";
export CheckAwardedBadges from "./CheckAwardedBadges";

View File

@@ -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;
}
}

129
src/models/Pokemon/index.js Normal file
View File

@@ -0,0 +1,129 @@
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) {
super(null);
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}
*/
serialize() {
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
});
}
}

View File

@@ -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"
});
}
}

View File

@@ -0,0 +1,42 @@
/**
* @class MapObject
*/
export default class MapObject {
/**
* @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];
}
};
}
}

366
src/models/World/index.js Normal file
View File

@@ -0,0 +1,366 @@
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
*/
export default class World {
/** @constructor */
constructor(instance) {
this.instance = instance;
this.db = this.instance.db;
this.players = [];
this.cells = {};
}
get connectedPlayers() {
return (this.players.length);
}
/**
* @return {Boolean}
*/
isFull() {
return (
this.connectedPlayers >= CFG.MAX_CONNECTIONS
);
}
/**
* @param {Request} req
* @param {Response} res
*/
getPlayerByRequest(req, res) {
let player = null;
if (this.playerAlreadyConnected(req)) {
player = this.getPlayerByIP(req.headers.host);
return (player);
}
else {
player = this.addPlayer(req, res);
return (player);
}
}
/**
* @param {Request} req
* @return {Boolean}
*/
playerAlreadyConnected(req) {
let ii = 0;
let length = this.connectedPlayers;
let remoteAddress = req.headers.host;
for (; ii < length; ++ii) {
if (this.players[ii].remoteAddress === remoteAddress) {
return (true);
}
};
return (false);
}
/**
* @param {String} ip
*/
getPlayerByIP(ip) {
let players = this.players;
let ii = 0;
let length = this.connectedPlayers;
for (; ii < length; ++ii) {
if (players[ii].remoteAddress === ip) {
return (players[ii]);
}
};
return (null);
}
/**
* @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("Remove:", player.email);
}
spawnFort() {
}
spawnGym() {
}
spawnEncounter() {
}
/**
* @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
*/
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":
this.GetMapObjects(msg).then((result) => {
resolve(result);
});
break;
case "CHECK_CHALLENGE":
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;
};
});
}
}
inherit(World, _packets);

View File

@@ -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")
);
}

View File

@@ -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()
);
}

View File

@@ -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")
);
}

View File

@@ -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")
);
}

View File

@@ -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 = msg.fort_id.split(".");
return new Promise((resolve) => {
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: msg.fort_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")
);
});
});
}

View File

@@ -0,0 +1,33 @@
import POGOProtos from "pokemongo-protobuf";
import print from "../../../print";
/**
* @param {Object} msg
* @return {Buffer}
*/
export default function FortSearch(msg) {
let id = msg.fort_id.split(".");
return new Promise((resolve) => {
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_id: 4 }
],
experience_awarded: 1337,
cooldown_complete_timestamp_ms: +new Date() + 5e3,
chain_hack_sequence_number: 2
});
resolve(
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.FortSearchResponse")
);
});
});
}

View File

@@ -0,0 +1,51 @@
import POGOProtos from "pokemongo-protobuf";
import s2 from "s2-geometry";
import print from "../../../print";
const S2Geo = s2.S2;
/**
* @param {Object} msg
* @return {Buffer}
*/
export default function GetMapObjects(msg) {
let latitude = msg.latitude;
let longitude = msg.longitude;
let mapCells = [];
let buffer = {
status: "SUCCESS",
map_cells: mapCells
};
let limit = msg.cell_id.length;
// TODO: oop, forts are world objects!
return new Promise((resolve) => {
this.getFortsByCells(msg.cell_id, [], 0).then((cells) => {
cells.map((cell) => {
mapCells.push({
s2_cell_id: cell.id,
current_timestamp_ms: +new Date(),
forts: cell.forts.map((fort) => { return fort.serialize() }),
spawn_points: [],
deleted_objects: [],
fort_summaries: [],
decimated_spawn_points: [],
wild_pokemons: [],
catchable_pokemons: [],
nearby_pokemons: []
});
});
resolve(
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse")
);
});
});
}

View File

@@ -0,0 +1,7 @@
export FortSearch from "./FortSearch";
export FortDetails from "./FortDetails";
export GetMapObjects from "./GetMapObjects";
export CheckChallenge from "./CheckChallenge";
export DownloadSettings from "./DownloadSettings";
export DownloadItemTemplates from "./DownloadItemTemplates";
export DownloadRemoteConfigVersion from "./DownloadRemoteConfigVersion";

View File

@@ -1,14 +0,0 @@
/**
* @class GameServer
*/
class PacketModel {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
}
}

View File

@@ -1,9 +0,0 @@
{
"exp": 0,
"level": 5,
"stardust": 1000,
"pokecoins": 0,
"team": 0,
"items": [],
"pokemons": []
}

View File

@@ -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": {
}
}

31
src/modes/index.js Normal file
View File

@@ -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: 0,
get_map_objects_max_refresh_seconds: 0,
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
}
}

View File

@@ -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
})
);
}

View File

@@ -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"));
}

View File

@@ -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()
);
}

View File

@@ -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()
);
}

View File

@@ -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]
}
}
});
}
}

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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()
);
}

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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()
);
}

View File

@@ -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"));
}

View File

@@ -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"));
}

View File

@@ -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);
}

View File

@@ -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"));
});
}

View File

@@ -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");
}

View File

@@ -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"));
}

View File

@@ -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"));
}

View File

@@ -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"));
}

View File

@@ -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"));
}

View File

@@ -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);
}

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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()
);
}

View File

@@ -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"));
}

View File

@@ -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()
);
}

View File

@@ -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")
);
}

View File

@@ -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"));
}

View File

@@ -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);
}

View File

@@ -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()
);
}

View File

@@ -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";

View File

@@ -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);
}

11
src/print.js Normal file
View File

@@ -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" : ""}`);
}

View File

@@ -1,15 +1,17 @@
import fs from "fs";
import print from "./print";
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;
this.print(`${length}:${CFG.MAX_CONNECTIONS} connected players!`, 33);
case "/players":
var length = players.length;
print(`${length}:${CFG.MAX_CONNECTIONS} connected players!`, 33);
break;
// Exit the server
case "/exit":
@@ -19,10 +21,10 @@ 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;
this.print(`Removed ${result} player${result === 1 ? "": "s"}!`);
var result = length - players.length;
print(`Removed ${result} player${result === 1 ? "": "s"}!`);
break;
case "/clear":
process.stdout.write("\x1Bc");
@@ -35,8 +37,8 @@ export function processCommand(cmd, data) {
break;
case "/save":
this.saveAllPlayers();
var length = this.clients.length;
this.print(`Saved ${length} player${length === 1 ? "": "s"} into database!`);
var length = players.length;
print(`Saved ${length} player${length === 1 ? "": "s"} into database!`);
break;
case "/spawn":
this.spawnPkmnAtPlayer(data[1], data[2], data[3] || 1);
@@ -45,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;
};
};
@@ -61,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);
};

View File

@@ -1,3 +0,0 @@
import pogo from "node-pogo-protos";
export default pogo;

View File

@@ -1,25 +1,17 @@
import fs from "fs";
import url from "url";
import proto from "./proto";
import pcrypt from "pcrypt";
import jwtDecode from "jwt-decode";
import POGOProtos from "pokemongo-protobuf";
import print from "./print";
import CFG from "../cfg";
import {
ResponseEnvelope,
AuthTicket,
ShopData
} from "./packets";
/**
* @param {Request} req
* @param {Response} res
*/
export function routeRequest(req, res) {
let player = this.getPlayerByRequest(req);
let parsed = url.parse(req.url).pathname;
let route = parsed.split("/");
let host = req.headers.host;
@@ -27,145 +19,162 @@ export function routeRequest(req, res) {
switch (route[1]) {
case "plfe":
case "custom":
if (route[2] === "rpc") this.onRequest(req);
this.processRpcRequest(req, res, route);
break;
case "model":
// make sure no random dudes can access download
if (!player.authenticated || !player.email_verified) 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 "om":
if (
route[2] === "glm" &&
route[3] === "mmap"
) {
this.print(`Received gmaps request!`, 33);
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;
};
}
export function parseProtobuf(buffer, path) {
try {
return POGOProtos.parseWithUnknown(buffer, path);
} catch (e) {
this.print(e, 31);
/**
* @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) {
print(`Error file resolving model ${name}:` + error, 31);
return void 0;
}
print(`Sent ${name} to ${player.email}`, 36);
res.end(data);
});
}
}
/**
* @param {Request} req
* @param {Response} res
* @param {Array} route
*/
export function parseSignature(req) {
let key = pcrypt.decrypt(req.unknown6.unknown2.encrypted_signature);
return (
POGOProtos.parseWithUnknown(key, "POGOProtos.Networking.Envelopes.Signature")
);
export function processRpcRequest(req, res, route) {
let player = this.world.getPlayerByRequest(req, res);
if (route[2] === "rpc") {
player.refreshSocket(req, res);
this.onRequest(player);
}
}
/**
* @param {Request} req
* @param {Player} player
*/
export function onRequest(req) {
export function onRequest(player) {
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;
}
}
let request = this.parseProtobuf(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope");
request.requests = request.requests || [];
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(",");
}
if (!player.authenticated) {
player.sendResponse(this.authenticatePlayer(player));
this.authenticatePlayer(player);
return void 0;
}
if (player.hasSignature === false) {
player.getDevicePlatform();
}
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;
}
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(returns, request, player);
player.sendResponse(msg);
});
}
/**
* @param {Number} status
* @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} req
* @param {Request} request
* @param {Player} player
* @param {Boolean} auth
* @param {Boolean} shop
* @return {Buffer}
*/
export function envelopResponse(status, returns, req, player, auth, shop) {
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"];
this.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 (shop) buffer.unknown6 = [ShopData()];
if (buffer.unknown6 && !shop) {
if (buffer.unknown6) {
buffer.unknown6 = [{
"response_type": 6,
"unknown2": {
@@ -176,7 +185,7 @@ export function envelopResponse(status, returns, req, player, auth, shop) {
}];
}
buffer.status_code = status;
buffer.status_code = 1;
return (
POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.ResponseEnvelope")
@@ -197,7 +206,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 +218,4 @@ export function processRequests(player, requests) {
});
}
/**
* @param {Request} req
* @return {Boolean}
*/
export function validRequest(req) {
return (true);
}

View File

@@ -1,40 +1,29 @@
import proto from "./proto";
import POGOProtos from "pokemongo-protobuf";
import print from "./print";
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
* @param {String} type
* @return {Buffer}
*/
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) {
print(`Failed to parse ${type}: ${e}`, 31);
}
}
return void 0;
}
/**
* @param {Player} player
@@ -43,230 +32,48 @@ const REQUEST = proto.Networking.Requests.RequestType;
*/
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, cc) || {};
return new Promise((resolve) => {
try {
switch (req.request_type) {
// Player
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);
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;
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;
// Global
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);
case "FORT_DETAILS":
case "GET_MAP_OBJECTS":
case "CHECK_CHALLENGE":
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);
});
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);
});
}
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
});
}

View File

@@ -1,11 +1,15 @@
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 print from "./print";
import CFG from "../cfg";
import * as shared from "./shared";
import GameMaster from "./models/GameMaster";
import {
capitalize,
idToPkmnBundleName
@@ -13,12 +17,14 @@ import {
export function setup() {
let isFirstRun = !this.directoryExists(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) {
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);
@@ -28,26 +34,25 @@ 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..`);
this.master = POGOProtos.serialize(this.parseGameMaster(), "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse");
let parsedMaster = this.parseGameMaster();
shared.GAME_MASTER = new GameMaster(parsedMaster);
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 running at ${localIPv4}:${CFG.PORT}`);
this.emit("ready", void 0);
print(`Server listening at ${localIPv4}:${CFG.PORT}`, 33);
});
}).catch((e) => {
//fse.removeSync(CFG.DUMP_ASSET_PATH);
this.print("Error: " + e + " was not found!", 31);
this.emit("ready", e);
print("Error: " + e + " was not found!", 31);
});
}
@@ -92,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
@@ -125,7 +130,7 @@ export function parseGameMaster() {
let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master");
master = this.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse");
} catch (e) {
this.print(e, 31);
print(e, 31);
}
return (master);
}
@@ -141,7 +146,7 @@ export function onFirstRun(resolve) {
this.downloadAssets().then(resolve);
});
}).catch((e) => {
this.print(e, 31);
print(e, 31);
});
}
@@ -183,11 +188,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;
}
@@ -210,16 +215,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;
}

6
src/shared.js Normal file
View File

@@ -0,0 +1,6 @@
/**
* Global shared assets
*/
export let GAME_ASSETS = {};
export let GAME_MASTER = null;

View File

@@ -1,5 +1,5 @@
import Long from "long";
import proto from "./proto";
import POGOProtos from "pokemongo-protobuf";
import pcrypt from "pcrypt";
import CFG from "../cfg";
@@ -20,15 +20,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 +36,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 +75,21 @@ 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))
);
}
/**
* @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")
);
}

View File

@@ -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);
});
})();