- Fixed many things
- Spawn pokemon, added player and pokemon inventory display
- Pkmn download url packet
- Pokemon evolving
- Improved pokestop rewarding
This commit is contained in:
Felix 2016-09-02 20:44:08 +02:00
parent 5327999fcc
commit 75557ea2a4
25 changed files with 427 additions and 203 deletions

View File

@ -4,6 +4,8 @@ export default {
VERSION: JSON.parse(fs.readFileSync("./package.json")).version,
ORIGINAL_REPOSITORY: "https://github.com/maierfelix/POGOServer",
// show greeting
GREET: true,
// allow api calls

BIN
proto.bin

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 594 B

View File

@ -4,14 +4,15 @@ export default {
TEAM: proto.Enums.TeamColor,
ITEMS: proto.Inventory.Item.ItemId,
GENDER: proto.Enums.Gender,
getItemNameById: (emu, id) => {
POKEMON_IDS: proto.Enums.PokemonId,
getNameById: (emu, id) => {
id <<= 0;
for (let key in emu) {
if (emu[key] === id) return (key);
};
return (null);
},
getItemIdByName: (emu, name) => {
getIdByName: (emu, name) => {
for (let key in emu) {
if (key === name) return (emu[key]);
};

View File

@ -16,6 +16,8 @@ import CFG from "../cfg";
import World from "./models/World";
import pngjs from "pngjs";
import * as _api from "./api";
import * as _dump from "./dump";
import * as _http from "./http";
@ -49,6 +51,7 @@ export default class GameServer {
this.hash = null;
this.claim = null;
this.repository = null;
this.apiClients = {};
@ -84,7 +87,7 @@ export default class GameServer {
loadProtoBinary() {
return new Promise((resolve) => {
let opt = { filterType: -1 };
let decode = require("pngjs").PNG.sync.read(
let decode = pngjs.PNG.sync.read(
fs.readFileSync("proto.bin"), opt
);
let data = decode.data;
@ -96,10 +99,9 @@ export default class GameServer {
content += String.fromCharCode(data[ii]);
} else break;
};
let sig = eval(Buffer.from(content, "base64").toString());
this.hash = sig.value;
this.hash = JSON.parse(Buffer.from(content, "base64").toString()).value;
this.claim = CFG.ORIGINAL_REPOSITORY;
resolve(print(deXOR(sig.value, getHashCodeFrom(this.claim))));
resolve(print(deXOR(this.hash, getHashCodeFrom(this.claim))));
});
}

View File

@ -1,5 +1,11 @@
import POGOProtos from "pokemongo-protobuf";
import {
idToPkmnBundleName
} from "../../utils";
import ENUM from "../../enum";
/**
* @class GameMaster
*/
@ -31,13 +37,11 @@ export default class GameMaster {
parse() {
let ii = 0;
let length = 0;
let item = null;
let items = this.decode.item_templates;
length = items.length;
let ii = 0;
let length = items.length;
for (; ii < length; ++ii) {
item = items[ii];
@ -72,6 +76,36 @@ export default class GameMaster {
);
}
/**
* @param {Number} dex
* @return {Object}
*/
getPokemonTmplByDex(dex) {
let id = idToPkmnBundleName(dex).substring(2);
let name = ENUM.getNameById(ENUM.POKEMON_IDS, dex);
let tmplId = `V${id}_POKEMON_${name}`;
let item = null;
let items = this.decode.item_templates;
let ii = 0;
let length = items.length;
for (; ii < length; ++ii) {
item = items[ii];
if (
item.pokemon_settings !== void 0 &&
item.template_id === tmplId
) {
return (item.pokemon_settings);
}
};
return (null);
}
/**
* @return {Buffer}
*/

View File

@ -3,8 +3,13 @@
*/
export default class Avatar {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this._skin = 0;
this._hair = 0;

View File

@ -1,70 +1,111 @@
import ENUM from "../../../enum";
/**
* @class Bag
*/
export default class Bag {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
//this.candies = new CandyBag();
this.player = player;
this.stardust = 0;
this.pokecoins = 0;
this.poke_ball = 150;
this.great_ball = 25;
this.ultra_ball = 5;
this.master_ball = 10;
this.pokeBall = 0;
this.greatBall = 0;
this.ultraBall = 0;
this.masterBall = 0;
this.potion = 1;
this.super_potion = 1;
this.hyper_potion = 1;
this.max_potion = 1;
this.potion = 0;
this.superPotion = 0;
this.hyperPotion = 0;
this.maxPotion = 0;
this.revive = 1;
this.max_revive = 1;
this.revive = 0;
this.maxRevive = 0;
this.lucky_egg = 1;
this.troy_disk = 1;
this.razzBerry = 0;
this.blukBerry = 0;
this.nanabBerry = 0;
this.weparBerry = 0;
this.pinapBerry = 0;
this.incense_ordinary = 1;
this.incense_spicy = 1;
this.incense_cool = 1;
this.incense_floral = 1;
// premium shit
this.luckyEgg = 0;
this.troyDisk = 0;
this.razz_berry = 1;
this.bluk_berry = 1;
this.nanab_berry = 1;
this.wepar_berry = 1;
this.pinap_berry = 1;
this.incenseOrdinary = 0;
this.incenseSpicy = 0;
this.incenseCool = 0;
this.incenseFloral = 0;
this.incubator_basic = 1;
this.incubator_basic_unlimited = 1;
this.incubatorBasic = 0;
this.incubatorBasicUnlimited = 0;
this.storageUpgrade = 0;
this.pkmnStorageUpgrade = 0;
this.pokemon_storage_upgrade = 1;
this.storage_upgrade = 1;
}
/**
* @param {Number} dex
* @return {Object}
* @param {String} name
* @return {Number}
*/
getCandy(dex) {
getItemEnumId(name) {
return (
this.candies.getCandyByDexNumber(dex)
ENUM.getIdByName(ENUM.ITEMS, name)
);
}
/**
* @param {Number} dex
* @param {Number} value
* @param {String} name
* @return {String}
*/
updateCandyAmount(dex, value) {
let candy = this.getCandy(dex);
candy.amount += (value << 0);
getItemName(name) {
return (
this.getItemEnumId("ITEM_" + name.toUpperCase())
);
}
/**
* @return {Array}
*/
serialize() {
let out = [];
for (let key in this) {
if (this.hasOwnProperty(key) && Number.isInteger(this[key])) {
let itemId = this.getItemName(key);
if (Number.isInteger(itemId) && this[key] > 0) {
out.push({
modified_timestamp_ms: +new Date() - 1e3,
inventory_item_data: {
item: {
item_id: "ITEM_" + key.toUpperCase(),
count: this[key] << 0
}
}
});
}
}
};
return (out);
}
/**
* @return {String}
*/
querify() {
let buffer = {};
for (let key in this) {
if (this.hasOwnProperty(key) && Number.isInteger(this[key])) {
let itemId = this.getItemName(key);
if (Number.isInteger(itemId)) {
buffer[itemId] = this[key];
}
}
};
return (JSON.stringify(buffer));
}
}

View File

@ -3,9 +3,16 @@
*/
export default class CandyBag {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this.candies = [];
}
getCandyByDexNumber(dex) {

View File

@ -3,8 +3,13 @@
*/
export default class Contact {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this.sendMarketingEmails = false;
this.sendPushNotifications = false;

View File

@ -3,8 +3,13 @@
*/
export default class Currency {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this.currencies = {
"POKECOIN": 0,

View File

@ -7,11 +7,19 @@ import print from "../../../print";
*/
export default class Info {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this.lvl = 1;
this.stardust = 0;
this.pokecoins = 0;
this.team = "NEUTRAL";
this.exp = this.getLevelExp(this.lvl);
@ -21,11 +29,11 @@ export default class Info {
this.levelReward = false;
this.kmWalked = 0;
this.pkmnEncountered = 0;
this.uniquePokedexEntries = 0;
this.pkmnCaptured = 0;
this.pokeStopVisits = 0;
this.pokeballsThrown = 0;
this.pkmnEncountered = 1;
this.uniquePokedexEntries = 1;
this.pkmnCaptured = 1;
this.pokeStopVisits = 2;
this.pokeballsThrown = 3;
this.eggsHatched = 0;
this.bigMagikarpCaught = 0;
this.pkmnDeployed = 0;
@ -90,26 +98,58 @@ export default class Info {
}
}
/**
* @return {Boolean}
*/
maxLevelReached() {
return (
this.lvl + 1 >= this.getMaximumLevel()
);
}
/**
* @return {Object}
*/
serialize() {
return ({
level: this.lvl,
experience: this.exp,
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
modified_timestamp_ms: +new Date(),
inventory_item_data: {
player_stats: {
level: this.lvl,
experience: this.exp,
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,
pokemon_caught_by_type: {
0: 0,
1: 1,
2: 0,
3: 0,
4: 0,
5: 0,
6: 0,
7: 0,
8: 0,
9: 0,
10: 0,
11: 0,
12: 0,
13: 0,
14: 0,
15: 0,
16: 0,
17: 0,
18: 0
}
}
}
});
}

View File

@ -5,15 +5,44 @@ import Pokemon from "../../Pokemon";
*/
export default class Party {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this.party = [];
this.addPkmn({
dexNumber: 4,
cp: 100,
stamina: 10,
staminaMax: 20,
move1: "TACKLE",
move2: "SIGNAL BEAM",
height: 0.3,
weight: 0.55,
ivAttack: 10,
ivDefense: 12,
ivStamina: 15,
cpMultiplier: 0.333,
pokeball: "ITEM_POKE_BALL",
favorite: 0
});
}
/**
* @param {Object} obj
*/
addPkmn(obj) {
console.log(obj);
if (!obj.owner) obj.owner = this.player;
if (!(obj instanceof Pokemon)) {
this.party.push(new Pokemon(obj));
}
else this.party.push(obj);
}
/**
@ -65,4 +94,21 @@ export default class Party {
return (amount);
}
/**
* @return {Array}
*/
serialize() {
let out = [];
let ii = 0;
let length = this.party.length;
for (; ii < length; ++ii) {
out.push({
"inventory_item_data": {
"pokemon_data": this.party[ii].serialize()
}
});
};
return (out);
}
}

View File

@ -3,9 +3,16 @@
*/
export default class Tutorial {
/** @constructor */
constructor() {
/**
* @param {Player} player
* @constructor
*/
constructor(player) {
this.player = player;
this.states = [];
}
skipTutorial() {
@ -28,4 +35,14 @@ export default class Tutorial {
);
}
/**
* @return {String}
*/
querify() {
let buffer = {
"states": this.serialize()
};
return (JSON.stringify(buffer));
}
}

View File

@ -4,6 +4,7 @@ import Bag from "./Bag";
import Info from "./Info";
import Party from "./Party";
import Avatar from "./Avatar";
import CandyBag from "./CandyBag";
import Contact from "./Contact";
import Tutorial from "./Tutorial";
import Currency from "./Currency";
@ -61,6 +62,8 @@ export default class Player extends MapObject {
this.remoteAddress = null;
this.bag = new Bag(this);
this.candyBag = new CandyBag(this);
this.info = new Info(this);
this.party = new Party(this);
this.avatar = new Avatar(this);

View File

@ -5,23 +5,23 @@ import POGOProtos from "pokemongo-protobuf";
* @return {Buffer}
*/
export default function GetInventory(msg) {
/*
let items = this.GetInventoryItems();
let party = this.GetInventoryParty();
*/
let stats = {
modified_timestamp_ms: +new Date(),
inventory_item_data: {
player_stats: this.info.serialize()
}
};
let items = this.bag.serialize();
let stats = this.info.serialize();
let party = this.party.serialize();
//let pokedex = this.pokedex.serialize();
items.push(stats);
party.map((pkmn) => {
items.push(pkmn);
});
let buffer = {
success: true,
inventory_delta: {
new_timestamp_ms: new Date().getTime(),
inventory_items: [stats]
new_timestamp_ms: +new Date(),
inventory_items: items
}
};
@ -29,63 +29,4 @@ export default function GetInventory(msg) {
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetInventoryResponse")
);
}
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);
}

View File

@ -1,11 +1,16 @@
import pokename from "pokename";
import MapObject from "../World/MapObject";
import { GAME_MASTER } from "../../shared";
import {
_toCC,
validName
} from "../../utils";
import print from "../../print";
const pokename = require("pokename")();
/**
* @class Pokemon
*/
@ -48,10 +53,14 @@ export default class Pokemon extends MapObject {
this.nickname = null;
this.pokeball = null;
this.favorite = 0;
this.init(obj);
this.evolvePkmn();
}
/**
@ -86,24 +95,39 @@ export default class Pokemon extends MapObject {
* @return {Object}
*/
getPkmnTemplate(dex) {
let name = pokename.getPokemonNameById(dex);
return (this.owner.gameMaster.pokemon[name]);
let tmpl = GAME_MASTER.getPokemonTmplByDex(dex);
return (tmpl);
}
upgradePkmn() {
/**
* @return {String}
*/
getPkmnName() {
return (
pokename.getPokemonNameById(this.dexNumber)
);
}
evolvePkmn() {
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);
let evolutions = pkmnTmpl.evolution_ids;
if (evolutions.length <= 1) {
this.evolveInto(evolutions[0]);
}
else {
print(`Evolving this pokemon isnt supported yet!`, 31);
}
}
/**
* @param {Number} dex
* @param {String} ev
*/
evolveTo(dex) {
console.log("EVOLVE TO: ", dex);
evolveInto(ev) {
let evName = _toCC(ev);
let evId = pokename.getPokemonIdByName(evName);
if (evId <= 0) return print(`Failed at retrieving id for pokemon ${ev}`, 31);
let evTmpl = this.getPkmnTemplate(evId);
print(`${this.owner.username} successfully evolved ${this.getPkmnName()} to ${evName}`);
}
/**
@ -111,18 +135,32 @@ export default class Pokemon extends MapObject {
*/
serialize() {
return ({
id: 1,
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,
stamina_max: this.staminaMax,
move_1: this.move1,
move_2: this.move2,
height_m: this.height,
weight_kg: this.weight,
individual_attack: this.ivAttack,
individual_defense: this.ivDefense,
individual_stamina: this.ivStamina,
cp_multiplier: this.cpMultiplier
cp_multiplier: this.cpMultiplier,
pokeball: "ITEM_POKE_BALL",
captured_cell_id: "1337",
creation_time_ms: +new Date() - 1e3,
favorite: this.favorite
});
}
/**
* @return {Object}
*/
querify() {
return ({
});
}

View File

@ -80,24 +80,22 @@ export default class Cell extends MapObject {
getFortsFromDatabase() {
return new Promise((resolve) => {
try {
let out = [];
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_POKESTOP_TABLE).then((forts) => {
let out = [];
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_POKESTOP_TABLE).then((forts) => {
forts = forts || [];
forts.map((fort) => {
fort.type = "CHECKPOINT";
out.push(fort);
});
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_GYM_TABLE).then((forts) => {
forts = forts || [];
forts.map((fort) => {
fort.type = "CHECKPOINT";
fort.type = "GYM";
out.push(fort);
});
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_GYM_TABLE).then((forts) => {
forts = forts || [];
forts.map((fort) => {
fort.type = "GYM";
out.push(fort);
});
resolve(out);
});
resolve(out);
});
} catch (e) { console.log(e); }
});
});
}

View File

@ -45,7 +45,7 @@ export default class Gym extends Fort {
latitude: this.latitude,
longitude: this.longitude,
enabled: this.enabled,
owned_by_team: ENUM.getItemNameById(ENUM.TEAM, this.team),
owned_by_team: ENUM.getNameById(ENUM.TEAM, this.team),
guard_pokemon_id: this.guardPkmn,
gym_points: this.gymPoints,
active_fort_modifier: []

View File

@ -61,7 +61,7 @@ export default class Pokestop extends Fort {
let amount = this.rewards[key] << 0;
for (let ii = 0; ii < amount; ++ii) {
out.push({
item_id: ENUM.getItemNameById(ENUM.ITEMS, key << 0)
item_id: ENUM.getNameById(ENUM.ITEMS, key << 0)
});
};
};

View File

@ -83,6 +83,9 @@ export default class World {
case "CHECK_CHALLENGE":
resolve(this.CheckChallenge(msg));
break;
case "GET_DOWNLOAD_URLS":
resolve(this.GetDownloadUrls(msg));
break;
case "DOWNLOAD_SETTINGS":
resolve(this.DownloadSettings(msg));
break;

View File

@ -0,0 +1,40 @@
import POGOProtos from "pokemongo-protobuf";
import CFG from "../../../../cfg";
import print from "../../../print";
import { GAME_ASSETS } from "../../../shared";
/**
* @param {Object} msg
* @return {Buffer}
*/
export default function GetDownloadUrls(msg) {
let asset = null;
let assets = GAME_ASSETS[msg.player.platform].decode.digest;
let assetId = msg.asset_id[0];
let ii = 0;
let length = assets.length;
for (; ii < length; ++ii) {
if ((asset = assets[ii]).asset_id === assetId) {
break;
}
};
let buffer = {
download_urls: [{
asset_id: assetId,
url: `http://${this.instance.getLocalIPv4()}:${CFG.PORT}/model/${asset.bundle_name}`,
size: asset.size,
checksum: asset.checksum
}]
};
return (
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetDownloadUrlsResponse")
);
}

View File

@ -23,28 +23,15 @@ export default function GetMapObjects(msg) {
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) => {
if (cell.forts.length) {
let ids = [];
/*console.log("###" + cell.cellId + "###");
cell.forts.map((fort) => {
console.log(fort.uid, fort.latitude, fort.longitude);
});*/
cell.forts.map((fort) => {
let id = fort.cellId + "." + fort.uid;
if (ids.indexOf(id) > -1) {
print(`Duplicated fort!!!! => ${id}`, 31);
}
else {
ids.push(id);
}
//print(`Active fort: ${fort.cellId}.${fort.uid}`, 35);
if (ids.indexOf(id) > -1) print(`Duplicated fort!!!! => ${id}`, 31);
else ids.push(id);
});
}
mapCells.push({

View File

@ -2,6 +2,7 @@ export FortSearch from "./FortSearch";
export FortDetails from "./FortDetails";
export GetMapObjects from "./GetMapObjects";
export CheckChallenge from "./CheckChallenge";
export GetDownloadUrls from "./GetDownloadUrls";
export DownloadSettings from "./DownloadSettings";
export DownloadItemTemplates from "./DownloadItemTemplates";
export DownloadRemoteConfigVersion from "./DownloadRemoteConfigVersion";

View File

@ -103,8 +103,15 @@ export function onRequest(player) {
}
if (!request.requests.length) {
print("Received invalid request!", 31);
return void 0;
// Dirty hack, appears when open pkmn stats in inventory
if (request.unknown6 && request.unknown6[1].request_type === 6) {
let msg = this.envelopResponse([], request, player);
player.sendResponse(msg);
}
else {
print("Received invalid request!", 31);
return void 0;
}
}
this.processRequests(player, request.requests).then((returns) => {

View File

@ -59,6 +59,7 @@ export function processResponse(player, req) {
case "FORT_DETAILS":
case "GET_MAP_OBJECTS":
case "CHECK_CHALLENGE":
case "GET_DOWNLOAD_URLS":
case "DOWNLOAD_SETTINGS":
case "DOWNLOAD_REMOTE_CONFIG_VERSION":
case "DOWNLOAD_ITEM_TEMPLATES":