mirror of
https://github.com/maierfelix/POGOserver.git
synced 2026-04-18 07:36:04 -05:00
Mega update, 0.4.7
- Support pkmn models for ios devices - Dump android and ios assets in seperate phase as well as into seperate folders - Add local ip to fix GetDownloadUrls bug - SetFavoritePokemon and ReleasePokemon working - Update players pkmn party into database - Extract players device signature on first auth to figure out, what models and asset_digest we need to response with - Heavily extended setup dump process - Added method to capitalize strings - Added db query to delete rows
This commit is contained in:
parent
63f65a95df
commit
3dc5e303ad
|
|
@ -15,6 +15,20 @@ export function getQueryByColumnFromTable(column, value, table) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} column
|
||||
* @param {String} value
|
||||
* @param {String} table
|
||||
*/
|
||||
export function deleteQueryByColumnFromTable(column, value, table) {
|
||||
return new Promise((resolve) => {
|
||||
this.db.instance.query(`DELETE FROM ${table} WHERE ${column}=?`, [value], (e, rows) => {
|
||||
if (e) console.log(e);
|
||||
else resolve(void 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} column
|
||||
* @param {String} value
|
||||
|
|
|
|||
|
|
@ -76,10 +76,23 @@ export function createOwnedPokemon(obj) {
|
|||
/**
|
||||
* @param {Object} obj
|
||||
*/
|
||||
export function updateUser(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(obj);
|
||||
let data = this.getUserQueryData(player);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.db.instance.query(query, data, resolve);
|
||||
|
|
@ -88,15 +101,40 @@ export function updateUser(obj) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {Object} obj
|
||||
* @param {Player} player
|
||||
*/
|
||||
export function updateUserItems(obj) {
|
||||
export function updateUserItems(player) {
|
||||
|
||||
let query = this.getUserItemQuery("UPDATE", "WHERE email=? LIMIT 1");
|
||||
let data = this.getUserItemQueryData(obj);
|
||||
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;
|
||||
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();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
66
src/index.js
66
src/index.js
|
|
@ -3,10 +3,10 @@ import os from "os";
|
|||
import fse from "fs-extra";
|
||||
import http from "http";
|
||||
import proto from "./proto";
|
||||
import decode from "pogo-decode";
|
||||
|
||||
import {
|
||||
inherit
|
||||
inherit,
|
||||
_toCC
|
||||
} from "./utils";
|
||||
|
||||
import CFG from "../cfg";
|
||||
|
|
@ -45,7 +45,7 @@ class GameServer {
|
|||
collections: {}
|
||||
};
|
||||
|
||||
this.asset = null;
|
||||
this.assets = {};
|
||||
this.master = null;
|
||||
this.socket = null;
|
||||
this.cycleInstance = null;
|
||||
|
|
@ -173,16 +173,66 @@ class GameServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} req
|
||||
* @param {Array} res
|
||||
* @return {Object}
|
||||
*/
|
||||
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) {
|
||||
|
||||
// decode opts
|
||||
let opts = {
|
||||
removeNulls: true,
|
||||
encodeBuffers: true
|
||||
let decoded = this.decode(req, res);
|
||||
|
||||
let out = {
|
||||
Request: decoded.req,
|
||||
Response: decoded.res
|
||||
};
|
||||
|
||||
try {
|
||||
let decoded = JSON.stringify(decode(req, res, opts), null, 2);
|
||||
let decoded = JSON.stringify(out, null, 2);
|
||||
fse.outputFileSync(CFG.DEBUG_DUMP_PATH + Date.now(), decoded);
|
||||
} catch (e) {
|
||||
this.print("Dump traffic: " + e, 31);
|
||||
|
|
|
|||
105
src/models/GetInventory.js
Normal file
105
src/models/GetInventory.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import POGOProtos from "pokemongo-protobuf";
|
||||
|
||||
export function GetInventory() {
|
||||
|
||||
let stats = this.GetInventoryPlayer();
|
||||
let items = this.GetInventoryItems();
|
||||
let party = this.GetInventoryParty();
|
||||
|
||||
let buffer = ({
|
||||
success: true,
|
||||
inventory_delta: {
|
||||
new_timestamp_ms: new Date().getTime(),
|
||||
inventory_items: stats.concat(items).concat(party)
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function GetInventoryItems() {
|
||||
|
||||
let player = this.player;
|
||||
|
||||
let items = [];
|
||||
let match = "item_";
|
||||
|
||||
for (let key in player) {
|
||||
if (key.substring(0, 5) === match) {
|
||||
items.push({
|
||||
modified_timestamp_ms: new Date().getTime(),
|
||||
inventory_item_data: {
|
||||
item: {
|
||||
item_id: key.toUpperCase(),
|
||||
count: parseFloat(player[key])
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (items);
|
||||
|
||||
}
|
||||
|
||||
export function GetInventoryParty() {
|
||||
|
||||
let player = this.player;
|
||||
let pkmns = [];
|
||||
|
||||
for (let pkmn of player.party) {
|
||||
pkmns.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
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (pkmns);
|
||||
|
||||
}
|
||||
18
src/models/GetMapObject.js
Normal file
18
src/models/GetMapObject.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
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
|
||||
//
|
||||
});
|
||||
|
||||
}
|
||||
14
src/models/index.js
Normal file
14
src/models/index.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @class GameServer
|
||||
*/
|
||||
class PacketModel {
|
||||
|
||||
/**
|
||||
* @param {Player} player
|
||||
* @constructor
|
||||
*/
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ import proto from "../proto";
|
|||
import CFG from "../../cfg";
|
||||
|
||||
/**
|
||||
* @param {Request} req
|
||||
* @param {Player} player
|
||||
* @return {Object}
|
||||
*/
|
||||
export default function GetAssetDigest(req) {
|
||||
return (fs.readFileSync("data/asset_digest"));
|
||||
export default function GetAssetDigest(player) {
|
||||
return (player.asset_digest.buffer);
|
||||
}
|
||||
|
|
@ -92,7 +92,8 @@ export default function GetInventoryData(player) {
|
|||
"cp_multiplier": pkmn.cp_multiplier,
|
||||
"pokeball": pkmn.pokeball,
|
||||
"captured_cell_id": pkmn.captured_cell_id,
|
||||
"creation_time_ms": pkmn.creation_time_ms
|
||||
"creation_time_ms": pkmn.creation_time_ms,
|
||||
"favorite": pkmn.favorite
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ import POGOProtos from "pokemongo-protobuf";
|
|||
* @param {Request} req
|
||||
* @return {Object}
|
||||
*/
|
||||
export default function SetFavoritePokemon(req) {
|
||||
export default function SetFavoritePokemon(player, req) {
|
||||
|
||||
buffer = ({
|
||||
result: proto.Networking.Responses.SetFavoritePokemonResponse.Result.ERROR_POKEMON_NOT_FOUND
|
||||
});
|
||||
let buffer = {
|
||||
"result": "SUCCESS",
|
||||
};
|
||||
|
||||
player.setFavoritePkmn(req.pokemon_id << 0, req.is_favorite);
|
||||
|
||||
return (
|
||||
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.SetFavoritePokemonResponse")
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class Player {
|
|||
this.uid = -1;
|
||||
this.owner_id = -1;
|
||||
|
||||
this.email = null;
|
||||
this._email = null;
|
||||
this.username = null;
|
||||
|
||||
this.latitude = 0;
|
||||
|
|
@ -91,11 +91,17 @@ class Player {
|
|||
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;
|
||||
// gets updated after each chunk end event
|
||||
this.response = null;
|
||||
this.connection = obj.connection;
|
||||
|
||||
|
|
@ -113,6 +119,13 @@ class Player {
|
|||
|
||||
}
|
||||
|
||||
get email() {
|
||||
return (this._email);
|
||||
}
|
||||
set email(value) {
|
||||
this._email = value.replace("@gmail.com", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} email
|
||||
*/
|
||||
|
|
@ -191,6 +204,29 @@ class Player {
|
|||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -325,7 +361,7 @@ export function spawnPkmnAtPlayer(name, pkmn, amount) {
|
|||
|
||||
if (player !== null) {
|
||||
let index = 0;
|
||||
while (++index < amount) {
|
||||
while (++index <= amount) {
|
||||
spawn = {
|
||||
pokemon_id: pkmn.toUpperCase(),
|
||||
cp: Math.random() * 1e3 << 0,
|
||||
|
|
@ -384,7 +420,12 @@ export function savePlayer(player) {
|
|||
return new Promise((resolve) => {
|
||||
if (player.authenticated) {
|
||||
this.updateUser(player).then(() => {
|
||||
this.updateUserItems(player).then(resolve);
|
||||
this.updateUserItems(player).then(() => {
|
||||
this.updateUserParty(player).then(() => {
|
||||
// refresh player data by database
|
||||
this.loginPlayer(player).then(resolve);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -399,7 +440,7 @@ export function forwardPlayer(player) {
|
|||
this.getUserByEmail(player.email).then((doc) => {
|
||||
if (player.email.length) {
|
||||
let provider = player.isGoogleAccount ? "Google" : "PTC";
|
||||
this.print(`${player.email.replace("@gmail.com", "")} authenticated via ${provider}!`, 36);
|
||||
this.print(`${player.email} authenticated via ${provider}!`, 36);
|
||||
}
|
||||
if (doc) {
|
||||
this.loginPlayer(player).then((res) => {
|
||||
|
|
@ -442,7 +483,7 @@ export function registerPlayer(player) {
|
|||
|
||||
return new Promise((resolve) => {
|
||||
this.createUser(player).then(() => {
|
||||
this.print(`${player.email.replace("@gmail.com", "")} registered!`, 36);
|
||||
this.print(`${player.email} registered!`, 36);
|
||||
player.tutorial_state = [];
|
||||
this.loginPlayer(player).then((res) => {
|
||||
resolve(res);
|
||||
|
|
@ -481,7 +522,7 @@ export function authenticatePlayer(player) {
|
|||
player.email = decoded.email;
|
||||
player.email_verified = decoded.email_verified;
|
||||
player.isGoogleAccount = true;
|
||||
this.print(`${player.email.replace("@gmail.com", "")} connected!`, 36);
|
||||
this.print(`${player.email} connected!`, 36);
|
||||
}
|
||||
else {
|
||||
this.print("Invalid authentication token! Kicking..", 31);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ export function routeRequest(req, res) {
|
|||
if (!player.authenticated || !player.email_verified) return void 0;
|
||||
let name = route[2];
|
||||
if (name && name.length > 1) {
|
||||
fs.readFile("data/" + name, (error, data) => {
|
||||
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;
|
||||
|
|
@ -44,6 +45,14 @@ export function routeRequest(req, res) {
|
|||
});
|
||||
}
|
||||
break;
|
||||
case "om":
|
||||
if (
|
||||
route[2] === "glm" &&
|
||||
route[3] === "mmap"
|
||||
) {
|
||||
this.print(`Received gmaps request!`, 33);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log(`Unknown request url: https://${req.headers.host}${req.url}`);
|
||||
break;
|
||||
|
|
@ -79,7 +88,7 @@ export function onRequest(req) {
|
|||
// Validate email verification
|
||||
if (player.authenticated) {
|
||||
if (!player.email_verified) {
|
||||
this.print(`${player.email.replace("@gmail.com", "")}'s email isnt verified, kicking..`, 31);
|
||||
this.print(`${player.email}'s email isnt verified, kicking..`, 31);
|
||||
this.removePlayer(player);
|
||||
return void 0;
|
||||
}
|
||||
|
|
@ -115,10 +124,10 @@ export function onRequest(req) {
|
|||
}
|
||||
|
||||
this.processRequests(player, request.requests).then((returns) => {
|
||||
let msg = this.envelopResponse(1, returns, request, !!request.auth_ticket, false);
|
||||
if (CFG.DEBUG_DUMP_TRAFFIC) {
|
||||
this.dumpTraffic(req.body, msg);
|
||||
this.dumpTraffic(request, returns);
|
||||
}
|
||||
let msg = this.envelopResponse(1, returns, request, player, !!request.auth_ticket, false);
|
||||
player.sendResponse(msg);
|
||||
});
|
||||
|
||||
|
|
@ -128,11 +137,12 @@ export function onRequest(req) {
|
|||
* @param {Number} status
|
||||
* @param {Array} returns
|
||||
* @param {Request} req
|
||||
* @param {Player} player
|
||||
* @param {Boolean} auth
|
||||
* @param {Boolean} shop
|
||||
* @return {Buffer}
|
||||
*/
|
||||
export function envelopResponse(status, returns, req, auth, shop) {
|
||||
export function envelopResponse(status, returns, req, player, auth, shop) {
|
||||
|
||||
let buffer = req;
|
||||
|
||||
|
|
@ -140,6 +150,18 @@ export function envelopResponse(status, returns, req, auth, shop) {
|
|||
|
||||
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 (auth) buffer.auth_ticket = AuthTicket();
|
||||
if (shop) buffer.unknown6 = [ShopData()];
|
||||
|
||||
|
|
@ -154,7 +176,7 @@ export function envelopResponse(status, returns, req, auth, shop) {
|
|||
}];
|
||||
}
|
||||
|
||||
buffer.status_code = 1;
|
||||
buffer.status_code = status;
|
||||
|
||||
return (
|
||||
POGOProtos.serialize(buffer, "POGOProtos.Networking.Envelopes.ResponseEnvelope")
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export function processResponse(player, req) {
|
|||
buffer = DownloadRemoteConfigVersion(msg);
|
||||
break;
|
||||
case "GET_ASSET_DIGEST":
|
||||
buffer = GetAssetDigest(msg);
|
||||
buffer = GetAssetDigest(player);
|
||||
break;
|
||||
case "GET_PLAYER_PROFILE":
|
||||
buffer = GetPlayerProfile();
|
||||
|
|
@ -107,7 +107,7 @@ export function processResponse(player, req) {
|
|||
return void 0;
|
||||
break;
|
||||
case "GET_DOWNLOAD_URLS":
|
||||
GetDownloadUrls(this.asset, this.getLocalIPv4(), msg).then((res) => {
|
||||
GetDownloadUrls(player.asset_digest.decode, CFG.LOCAL_IP || this.getLocalIPv4(), msg).then((res) => {
|
||||
resolve(res);
|
||||
});
|
||||
return void 0;
|
||||
|
|
@ -195,7 +195,20 @@ export function processResponse(player, req) {
|
|||
buffer = EvolvePokemon(msg);
|
||||
break;
|
||||
case "SET_FAVORITE_POKEMON":
|
||||
buffer = SetFavoritePokemon(msg);
|
||||
buffer = SetFavoritePokemon(player, msg);
|
||||
break;
|
||||
case "RELEASE_POKEMON":
|
||||
buffer = {
|
||||
result: "SUCCESS",
|
||||
candy_awarded: 5
|
||||
};
|
||||
let id = msg.pokemon_id << 0;
|
||||
this.deleteOwnedPokemon(id).then(() => {
|
||||
player.deletePkmnFromParty(id);
|
||||
buffer = POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.ReleasePokemonResponse");
|
||||
resolve(buffer);
|
||||
});
|
||||
return void 0;
|
||||
break;
|
||||
default:
|
||||
this.print(`Unknown request: ${req.request_type}`, 31);
|
||||
|
|
|
|||
179
src/setup.js
179
src/setup.js
|
|
@ -6,14 +6,17 @@ import POGOProtos from "pokemongo-protobuf";
|
|||
|
||||
import CFG from "../cfg";
|
||||
|
||||
import { idToPkmnBundleName } from "./utils";
|
||||
import {
|
||||
capitalize,
|
||||
idToPkmnBundleName
|
||||
} from "./utils";
|
||||
|
||||
export function setup() {
|
||||
|
||||
let isFirstRun = !this.directoryExists(CFG.DUMP_ASSET_PATH);
|
||||
|
||||
if (isFirstRun) {
|
||||
this.print("Preparing to dump required assets..", 36);
|
||||
this.print("Required assets are missing! Preparing dump session..", 31);
|
||||
setTimeout(() => {
|
||||
this.onFirstRun(() => {
|
||||
this.setup();
|
||||
|
|
@ -27,7 +30,6 @@ export function setup() {
|
|||
|
||||
this.print(`Downloaded assets are valid! Proceeding..`);
|
||||
|
||||
this.asset = this.parseAssetDigest();
|
||||
this.master = POGOProtos.serialize(this.parseGameMaster(), "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse");
|
||||
|
||||
this.setupDatabaseConnection().then(() => {
|
||||
|
|
@ -54,43 +56,65 @@ export function setup() {
|
|||
*/
|
||||
export function validateAssets() {
|
||||
|
||||
let index = 0;
|
||||
let max = CFG.MAX_POKEMON_NATIONAL_ID;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (!this.fileExists(CFG.DUMP_ASSET_PATH + "asset_digest")) {
|
||||
return reject("File asset_digest");
|
||||
}
|
||||
|
||||
// validate game master
|
||||
if (!this.fileExists(CFG.DUMP_ASSET_PATH + "game_master")) {
|
||||
return reject("File game_master");
|
||||
}
|
||||
|
||||
// validate models
|
||||
while (++index <= max) {
|
||||
let id = idToPkmnBundleName(index);
|
||||
if (!this.fileExists(CFG.DUMP_ASSET_PATH + id)) {
|
||||
return reject("Model " + id);
|
||||
}
|
||||
};
|
||||
|
||||
resolve();
|
||||
this.validateModels().then(() => {
|
||||
resolve();
|
||||
}).catch((e) => {
|
||||
reject(e);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function parseAssetDigest() {
|
||||
let asset = null;
|
||||
try {
|
||||
let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "asset_digest");
|
||||
asset = this.parseProtobuf(data, "POGOProtos.Networking.Responses.GetAssetDigestResponse");
|
||||
} catch (e) {
|
||||
this.print(e, 31);
|
||||
}
|
||||
return (asset);
|
||||
export function validateModels() {
|
||||
|
||||
let max = CFG.MAX_POKEMON_NATIONAL_ID;
|
||||
let limit = pogo.platforms.length;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const validate = (index) => {
|
||||
let platform = pogo.platforms[index];
|
||||
let name = platform.name;
|
||||
let path = CFG.DUMP_ASSET_PATH + name + "/";
|
||||
|
||||
// ups, validate asset_digest's too
|
||||
if (!this.fileExists(path + "asset_digest")) {
|
||||
return reject(`${path}asset_digest`);
|
||||
}
|
||||
else {
|
||||
let buffer = fs.readFileSync(path + "asset_digest");
|
||||
this.assets[name] = {
|
||||
buffer: buffer,
|
||||
decode: this.parseProtobuf(buffer, "POGOProtos.Networking.Responses.GetAssetDigestResponse")
|
||||
}
|
||||
}
|
||||
|
||||
// validate models inside folder
|
||||
let ii = 0;
|
||||
while (++ii <= max) {
|
||||
let id = idToPkmnBundleName(ii);
|
||||
if (!this.fileExists(path + id)) {
|
||||
return reject("Model " + id);
|
||||
}
|
||||
};
|
||||
|
||||
if (++index >= limit) {
|
||||
resolve();
|
||||
return void 0;
|
||||
}
|
||||
validate(index);
|
||||
};
|
||||
|
||||
validate(0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function parseGameMaster() {
|
||||
|
|
@ -105,66 +129,75 @@ export function parseGameMaster() {
|
|||
}
|
||||
|
||||
export function onFirstRun(resolve) {
|
||||
// make sure to login first!
|
||||
pogo.login({
|
||||
provider: CFG.DOWNLOAD_PROVIDER, // google or ptc
|
||||
username: CFG.DOWNLOAD_USERNAME,
|
||||
password: CFG.DOWNLOAD_PASSWORD
|
||||
}).then((res) => {
|
||||
// create data dir, if login successed
|
||||
fse.ensureDirSync(CFG.DUMP_ASSET_PATH);
|
||||
// write game master
|
||||
fs.writeFileSync(CFG.DUMP_ASSET_PATH + "game_master", res.master.toBuffer());
|
||||
// get and write asset digests
|
||||
this.dumpAssetDigests(res.client).then(() => {
|
||||
// dump pkmn models
|
||||
this.dumpPkmnModels(() => {
|
||||
resolve();
|
||||
});
|
||||
}).then(() => {
|
||||
this.downloadAssetDigests().then(() => {
|
||||
this.downloadAssets().then(resolve);
|
||||
});
|
||||
}).catch((e) => {
|
||||
this.print(e, 31);
|
||||
});
|
||||
}
|
||||
|
||||
export function dumpAssetDigests(client) {
|
||||
|
||||
this.print(`Dumping asset digests..`, 35);
|
||||
|
||||
let platforms = [
|
||||
{
|
||||
name: "android",
|
||||
platform: 2,
|
||||
manufacturer: "LGE",
|
||||
model: "Nexus 5",
|
||||
locale: "",
|
||||
version: 3300
|
||||
}
|
||||
];
|
||||
|
||||
export function downloadAssetDigests(assets) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let ii = 0;
|
||||
// create data folder for each support platform
|
||||
// and download each asset digest and related models
|
||||
let index = 0;
|
||||
for (; ii < platforms.length; ++ii) {
|
||||
let key = platforms[ii];
|
||||
client.getAssetDigest(
|
||||
key.platform,
|
||||
key.manufacturer,
|
||||
key.model,
|
||||
key.locale,
|
||||
key.version
|
||||
).then((asset) => {
|
||||
this.print(`Dumping ${key.name} asset digest..`, 35);
|
||||
fs.writeFileSync(CFG.DUMP_ASSET_PATH + "asset_digest", asset.toBuffer());
|
||||
if (++index >= platforms.length) {
|
||||
resolve();
|
||||
}
|
||||
let length = pogo.platforms.length;
|
||||
for (let platform of pogo.platforms) {
|
||||
fse.ensureDirSync(CFG.DUMP_ASSET_PATH + platform.name);
|
||||
pogo.getAssetDigest(platform).then((asset) => {
|
||||
fs.writeFileSync(CFG.DUMP_ASSET_PATH + platform.name + "/asset_digest", asset.toBuffer());
|
||||
if (++index >= length) resolve();
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadAssets() {
|
||||
return new Promise((resolve, reject) => {
|
||||
pogo.getGameMaster().then((master) => {
|
||||
fs.writeFileSync(CFG.DUMP_ASSET_PATH + "game_master", master.toBuffer());
|
||||
this.downloadModels().then(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadModels() {
|
||||
|
||||
let limit = pogo.platforms.length;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const dump = (index) => {
|
||||
let platform = pogo.platforms[index];
|
||||
let name = platform.name;
|
||||
let caps = capitalize(name);
|
||||
caps = name === "ios" ? "iOS" : caps;
|
||||
pogo.setPlatform(name);
|
||||
this.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!`);
|
||||
if (++index >= limit) {
|
||||
this.print("Dumped all assets successfully!");
|
||||
resolve();
|
||||
return void 0;
|
||||
}
|
||||
dump(index);
|
||||
});
|
||||
};
|
||||
dump(0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function dumpPkmnModels(resolve) {
|
||||
export function dumpPkmnModels(path, resolve) {
|
||||
|
||||
let limit = CFG.MAX_POKEMON_NATIONAL_ID;
|
||||
|
||||
|
|
@ -177,18 +210,18 @@ export function dumpPkmnModels(resolve) {
|
|||
downloads.map((item) => {
|
||||
this.print(`Dumping model ${item.name}..`, 35);
|
||||
try {
|
||||
fs.writeFileSync(CFG.DUMP_ASSET_PATH + item.name, item.body);
|
||||
fs.writeFileSync(path + item.name, item.body);
|
||||
}
|
||||
catch (e) {
|
||||
this.print(`Error while dumping model ${item.name}:` + e, 31);
|
||||
}
|
||||
});
|
||||
if (index >= limit) {
|
||||
this.print(`Dumped ${limit} assets successfully!`);
|
||||
//this.print(`Dumped ${limit} assets successfully!`);
|
||||
resolve();
|
||||
return void 0;
|
||||
}
|
||||
setTimeout(() => dump(index), 2e3);
|
||||
setTimeout(() => dump(index), 1e3);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
10
src/utils.js
10
src/utils.js
|
|
@ -86,4 +86,14 @@ export function idToPkmnBundleName(index) {
|
|||
return (
|
||||
"pm" + (index >= 10 ? index >= 100 ? "0" : "00" : "000") + index
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
export function capitalize(str) {
|
||||
return (
|
||||
str[0].toUpperCase() + str.slice(1)
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user