- Added pokemon spawn point api support
- Improved api access security
- Extend player bag
- Basic encountering and catching pkmn
This commit is contained in:
Felix
2016-09-08 21:21:38 +02:00
parent adc27df811
commit 8435544963
16 changed files with 361 additions and 129 deletions

View File

@@ -1,7 +1,6 @@
import fs from "fs";
import url from "url";
import s2 from "s2-geometry";
import prompt from "prompt";
import Cell from "./models/World/Cell";
@@ -14,10 +13,7 @@ import {
const S2Geo = s2.S2;
prompt.start({
message: " ",
delimiter: " "
});
let showHint = true;
export function processApiCall(req, res, route) {
@@ -27,7 +23,16 @@ export function processApiCall(req, res, route) {
let hoster = url.parse(req.headers.referer).host;
if (!(allowedHosts.indexOf(hoster) > -1)) {
this.grantApiAccess(req, res, route);
print(`Denied API access for ${hoster}!`, 31);
if (showHint) {
print(`To grant ${hoster} API access, add it to the allowed hosts in .save`, 33);
showHint = false;
}
let result = {
success:false,
reason: "API access denied!"
};
this.answerApiCall(res, JSON.stringify(result));
return void 0;
}
@@ -68,29 +73,6 @@ export function processApiCall(req, res, route) {
}
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, "");
}
});
}
export function answerApiCall(res, data) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
@@ -103,12 +85,10 @@ 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;
let username = CFG.API_USERNAME;
let password = CFG.API_PASSWORD;
if (
username === data.username &&
@@ -197,12 +177,12 @@ export function api_getFortsByPosition(data) {
let zoom = data.zoom;
this.getNeighboredForts(this.getNeighbors(lat, lng, zoom), [], 0).then((forts) => {
let result = [];
for (let fort of forts) {
forts.map((fort) => {
let fortData = fort.serialize();
fortData.name = fort.name;
fortData.uid = fort.uid;
result.push(fortData);
};
});
resolve({
forts: result,
success: true

View File

@@ -78,6 +78,28 @@ export default class Bag {
);
}
/**
* @param {String} key
* @return {String}
*/
getLocalItemKey(key) {
return (
key.replace("ITEM_", "").toLowerCase()
);
}
/**
* @param {String} key
* @return {Number}
*/
getItemAmountByItemKey(key) {
let name = this.getLocalItemKey(key);
if (this.hasOwnProperty(name)) {
return (this[name]);
}
return (-1);
}
/**
* @param {String} name
* @return {Boolean}
@@ -94,7 +116,7 @@ export default class Bag {
* @return {Number}
*/
updateItem(name, amount) {
let key = name.replace("ITEM_", "").toLowerCase();
let key = this.getLocalItemKey(name);
if (!this.isValidItemKey(key)) return (-1);
let currentAmount = this[key] << 0;
if (amount < 0) {

View File

@@ -1,7 +1,10 @@
import Pokemon from "../../Pokemon";
import WildPokemon from "../../Pokemon/WildPokemon";
import print from "../../../print";
import CFG from "../../../cfg";
/**
* @class Party
*/
@@ -29,9 +32,8 @@ export default class Party {
fetchFromDatabase() {
let instance = this.player.world.db;
this.player.world.db.query(`SELECT * FROM owned_pkmn WHERE owner_id=?`, [this.player.id], (e, rows) => {
this.player.world.db.query(`SELECT * FROM ${CFG.MYSQL_OWNED_PKMN_TABLE} WHERE owner_id=?`, [this.player.id], (e, rows) => {
if (e) return print(e, 31);
console.log("Pokemon:", rows);
});
}
@@ -40,10 +42,7 @@ export default class Party {
*/
addPkmn(obj) {
if (!obj.owner) obj.owner = this.player;
if (!(obj instanceof Pokemon)) {
this.party.push(new Pokemon(obj));
}
else this.party.push(obj);
this.party.push(new Pokemon(obj));
}
/**

View File

@@ -256,6 +256,18 @@ export default class Player extends MapObject {
this.world.triggerSpawnAt(this.latitude, this.longitude);
}
/**
* @param {WildPokemon} pkmn
*/
catchPkmn(pkmn) {
this.info.exp += 100;
this.info.stardust += 100;
this.info.pkmnCaptured += 1;
this.currentEncounter = null;
this.party.addPkmn(pkmn);
pkmn.catchedBy(this);
}
}
inherit(Player, _packets);

View File

@@ -20,8 +20,7 @@ export default class WildPokemon extends Pokemon {
this.uid = getUniqueHash();
this.encounterId = 0;
this.spawnPointId = 0;
this.encounterId = this.getEncounterId();
this.minExpire = obj.minExpire;
this.maxExpire = obj.maxExpire;
@@ -30,6 +29,29 @@ export default class WildPokemon extends Pokemon {
this.expiration = ~~(Math.random() * this.maxExpire) + this.minExpire;
// players who already catched this pkmn
this.hasCatched = [];
}
/**
* @param {Player} player
* @return {Boolean}
*/
catchedBy(player) {
if (!this.alreadyCatchedBy(player)) {
this.hasCatched.push(player.uid);
}
}
/**
* @param {Player} player
* @return {Boolean}
*/
alreadyCatchedBy(player) {
return (
this.hasCatched.indexOf(player.uid) > -1
);
}
/**
@@ -45,7 +67,7 @@ export default class WildPokemon extends Pokemon {
* @return {Number}
*/
getEncounterId() {
return (getHashCodeFrom(this.cellId + "" + this.uid));
return (this.uid);
}
/**
@@ -62,12 +84,24 @@ export default class WildPokemon extends Pokemon {
*/
serializeWild() {
return ({
encounter_id: this.getEncounterId(),
encounter_id: this.encounterId,
last_modified_timestamp_ms: +new Date(),
latitude: this.latitude,
longitude: this.longitude,
spawn_point_id: this.spawnPointId,
pokemon_data: {
pokemon_id: this.getPkmnId()
pokemon_id: this.getPkmnId(),
cp: 66,
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: this.expiration
});
@@ -78,7 +112,7 @@ export default class WildPokemon extends Pokemon {
*/
serializeCatchable() {
return ({
encounter_id: this.getEncounterId(),
encounter_id: this.encounterId,
pokemon_id: this.getPkmnId(),
expiration_timestamp_ms: this.creation + this.expiration,
latitude: this.latitude,

View File

@@ -64,6 +64,8 @@ export default class Pokemon extends MapObject {
this.isWild = false;
this.spawnPoint = null;
this.init(obj);
}

View File

@@ -32,7 +32,6 @@ export default class Cell extends MapObject {
this.synced = false;
this.forts = [];
this.spawns = [];
this.type = obj.type;
@@ -55,27 +54,20 @@ export default class Cell extends MapObject {
*/
addFort(obj) {
obj.world = this.world;
let fort = null;
fort = obj.type === "CHECKPOINT" ? new Pokestop(obj) : new Gym(obj);
let fort = (
obj.type === "CHECKPOINT" ? new Pokestop(obj) :
obj.type === "SPAWN" ? new SpawnPoint(obj) :
new Gym(obj)
);
this.forts.push(fort);
return (fort);
}
/**
* @param {Object} obj
* @return {SpawnPoint}
*/
addSpawnPoint(obj) {
obj.world = this.world;
let spawn = null;
spawn = new SpawnPoint(obj);
this.spawns.push(spawn);
return (spawn);
}
refreshSpawnPoints() {
this.spawns.map((spawn) => {
spawn.refresh();
this.forts.map((fort) => {
if (fort.isSpawn === true) {
fort.refresh();
}
});
}
@@ -88,32 +80,19 @@ export default class Cell extends MapObject {
resolve(this.forts);
}
else {
this.getSpawnsFromDatabase().then((spawns) => {
spawns.map((spawn) => {
this.addSpawnPoint(spawn);
});
this.getFortsFromDatabase().then((forts) => {
this.forts = [];
forts.map((fort) => {
this.processDeletedFort(this.addFort(fort));
});
this.synced = true;
//print(`Synced ${this.cellId} with database..`, 33);
resolve(this.forts);
this.getFortsFromDatabase().then((forts) => {
this.forts = [];
forts.map((fort) => {
this.processDeletedFort(this.addFort(fort));
});
this.synced = true;
print(`Synced ${this.cellId} with database..`, 33);
resolve(this.forts);
});
}
});
}
getSpawnsFromDatabase() {
return new Promise((resolve) => {
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_SPAWN_TABLE).then((spawns) => {
resolve(spawns || []);
});
});
}
/**
* @param {String} type
* @return {String}
@@ -122,10 +101,22 @@ export default class Cell extends MapObject {
return (
type === "CHECKPOINT" ?
CFG.MYSQL_POKESTOP_TABLE :
CFG.MYSQL_GYM_TABLE
type === "SPAWN" ?
CFG.MYSQL_SPAWN_TABLE :
type === "GYM" ?
CFG.MYSQL_GYM_TABLE :
"INVALID"
);
}
getSpawnsFromDatabase() {
return new Promise((resolve) => {
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_SPAWN_TABLE).then((spawns) => {
resolve(spawns || []);
});
});
}
getFortsFromDatabase() {
return new Promise((resolve) => {
let out = [];
@@ -141,7 +132,14 @@ export default class Cell extends MapObject {
fort.type = "GYM";
out.push(fort);
});
resolve(out);
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_SPAWN_TABLE).then((forts) => {
forts = forts || [];
forts.map((fort) => {
fort.type = "SPAWN";
out.push(fort);
});
resolve(out);
});
});
});
});
@@ -215,69 +213,98 @@ export default class Cell extends MapObject {
/**
* @return {Array}
*/
serializeWildPkmns() {
serializeSpawnPoints() {
let ii = 0;
let length = this.spawns.length;
let length = this.forts.length;
let out = [];
let spawn = null;
let fort = null;
for (; ii < length; ++ii) {
spawn = this.spawns[ii];
spawn.activeSpawns.map((encounter) => {
out.push(encounter.serializeWild());
});
fort = this.forts[ii];
if (!(fort.isSpawn === true)) continue;
out.push(fort.serialize());
};
return (out);
}
/**
* @param {Player} player
* @return {Array}
*/
serializeCatchablePkmns() {
serializeWildPkmns(player) {
let ii = 0;
let length = this.spawns.length;
let length = this.forts.length;
let out = [];
let spawn = null;
let fort = null;
for (; ii < length; ++ii) {
spawn = this.spawns[ii];
spawn.activeSpawns.map((encounter) => {
out.push(encounter.serializeCatchable());
fort = this.forts[ii];
if (!(fort.isSpawn === true)) continue;
fort.activeSpawns.map((encounter) => {
if (!encounter.alreadyCatchedBy(player)) {
out.push(encounter.serializeWild());
}
});
};
return (out);
}
/**
* @param {Player} player
* @return {Array}
*/
serializeNearbyPkmns() {
serializeCatchablePkmns(player) {
let ii = 0;
let length = this.spawns.length;
let length = this.forts.length;
let out = [];
let spawn = null;
let fort = null;
for (; ii < length; ++ii) {
spawn = this.spawns[ii];
spawn.activeSpawns.map((encounter) => {
out.push(encounter.serializeNearby());
fort = this.forts[ii];
if (!(fort.isSpawn === true)) continue;
fort.activeSpawns.map((encounter) => {
if (!encounter.alreadyCatchedBy(player)) {
out.push(encounter.serializeCatchable());
}
});
};
return (out);
}
/**
* @param {Player} player
* @return {Array}
*/
serializeNearbyPkmns(player) {
let ii = 0;
let length = this.forts.length;
let out = [];
let fort = null;
for (; ii < length; ++ii) {
fort = this.forts[ii];
if (!(fort.isSpawn === true)) continue;
fort.activeSpawns.map((encounter) => {
if (!encounter.alreadyCatchedBy(player)) {
out.push(encounter.serializeNearby());
}
});
};
return (out);
}
/**
* @param {Player} player
* @return {Object}
*/
serialize() {
serialize(player) {
return ({
s2_cell_id: this.cellId,
current_timestamp_ms: +new Date(),
forts: this.forts.map((fort) => { return fort.serialize(); }),
spawn_points: this.spawns.map((spawn) => { return spawn.serialize(); }),
forts: this.forts.map((fort) => { if (!fort.isSpawn) return fort.serialize(); }),
spawn_points: this.serializeSpawnPoints(),
deleted_objects: [],
fort_summaries: [],
decimated_spawn_points: [],
wild_pokemons: this.serializeWildPkmns(),
catchable_pokemons: this.serializeCatchablePkmns(),
nearby_pokemons: this.serializeNearbyPkmns()
wild_pokemons: this.serializeWildPkmns(player),
catchable_pokemons: this.serializeCatchablePkmns(player),
nearby_pokemons: this.serializeNearbyPkmns(player)
});
}

View File

@@ -21,15 +21,23 @@ export default class SpawnPoint extends MapObject {
super(obj);
this.type = null;
this.range = 3;
this.spawns = JSON.parse(obj.encounters);
this.minExpire = ((obj.min_spawn_expire * 1e3) * 60) << 0;
this.maxExpire = ((obj.max_spawn_expire * 1e3) * 60) << 0;
this.isSpawn = true;
this.activeSpawns = [];
this.init(obj);
this.uid += this.type[0].toUpperCase();
}
/**
@@ -54,7 +62,7 @@ export default class SpawnPoint extends MapObject {
* @return {Object}
*/
getRandomPosition() {
let range = .0005;
let range = this.range / 1e4;
let latitude = this.latitude + (Math.random() * (range * 2)) - range;
let longitude = this.longitude + (Math.random() * (range * 2)) - range;
return ({
@@ -70,15 +78,14 @@ export default class SpawnPoint extends MapObject {
dexNumber: randId,
latitude: randPos.lat,
longitude: randPos.lng,
pokeball: "ITEM_POKE_BALL",
favorite: 0,
isWild: true,
cellId: this.cellId,
spawnPointId: this.uid,
minExpire: this.minExpire,
maxExpire: this.maxExpire
});
this.activeSpawns.push(pkmn);
print(`Spawned ${pkmn.getPkmnName()} at ${this.cellId}}`);
print(`Spawned ${pkmn.getPkmnName()} at ${this.cellId}`);
}
/**
@@ -95,6 +102,21 @@ export default class SpawnPoint extends MapObject {
});
}
/**
* @param {String} id
* @return {WildPokemon}
*/
getPkmnSpawnById(id) {
let ii = 0;
let length = this.activeSpawns.length;
let spawn = null;
for (; ii < length; ++ii) {
spawn = this.activeSpawns[ii];
if (spawn.uid === id) return (spawn);
}
return (null);
}
/**
* @return {Object}
*/

View File

@@ -113,6 +113,11 @@ export function insertFortIntoDatabase(obj) {
resolve(pokestop);
});
}
else if (obj.type === "SPAWN") {
this.insertSpawnIntoDatabase(obj).then((spawn) => {
resolve(spawn);
});
}
});
}
@@ -152,4 +157,24 @@ export function insertGymIntoDatabase(obj) {
});
});
});
}
export function insertSpawnIntoDatabase(obj) {
let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
let lat = obj.latitude;
let lng = obj.longitude;
let encounters = [];
obj.encounters.split(",").map((encounter) => {
encounters.push(encounter << 0);
});
let query = `INSERT INTO ${Cell.getFortTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, encounters=?, update_interval=?`;
return new Promise((resolve) => {
this.instance.db.query(query, [cellId, lat, lng, `[${encounters}]`, obj.interval << 0], (e, res) => {
obj.uid = res.insertId;
obj.cell_id = cellId;
this.addFort(obj).then((fort) => {
resolve(fort);
});
});
});
}

View File

@@ -65,9 +65,11 @@ export default class World {
}
refreshSpawns() {
this.cells.map((cell) => {
cell.refreshSpawnPoints();
});
let ii = 0;
let length = this.cells.length;
for (; ii < length; ++ii) {
this.cells[ii].refreshSpawnPoints();
};
}
/**
@@ -78,14 +80,43 @@ export default class World {
let cell = this.getCellById(Cell.getIdByPosition(lat, lng, 15));
// Wait until cell got registered
if (cell === null) return void 0;
cell.spawns.map((spawn) => {
//if (spawn.activeSpawns.length >= 4) return void 0;
if (Math.random() < .85) {
spawn.spawnPkmn();
cell.forts.map((fort) => {
if (fort.isSpawn) {
if (fort.activeSpawns.length >= fort.spawns.length) return void 0;
fort.spawnPkmn();
}
});
}
/**
* @param {String} id
* @return {WildPokemon}
*/
getEncounterById(id) {
id <<= 0;
let ii = 0;
let jj = 0;
let fortLength = 0;
let cellLength = this.cells.length;
let cell = null;
let fort = null;
let pkmn = null;
for (; ii < cellLength; ++ii) {
cell = this.cells[ii];
fortLength = cell.forts.length;
for (; jj < fortLength; ++jj) {
fort = cell.forts[jj];
if (fort.isSpawn === true) {
if ((pkmn = fort.getPkmnSpawnById(id)) !== null) {
return (pkmn);
}
}
};
jj = 0;
};
return (pkmn);
}
/**
* @param {String} type
* @param {Object} msg
@@ -94,9 +125,10 @@ export default class World {
return new Promise((resolve) => {
switch (type) {
case "ENCOUNTER":
this.Encounter(msg).then((result) => {
resolve(result);
});
resolve(this.Encounter(msg));
break;
case "CATCH_POKEMON":
resolve(this.CatchPokemon(msg));
break;
case "FORT_SEARCH":
this.FortSearch(msg).then((result) => {

View File

@@ -0,0 +1,52 @@
import POGOProtos from "pokemongo-protobuf";
/**
* @param {Object} msg
*/
export default function CatchPokemon(msg) {
let buffer = null;
let player = msg.player;
let bag = player.bag;
let item = bag.getLocalItemKey(msg.pokeball);
let pkmn = msg.player.currentEncounter;
player.bag[item] -= 1;
// Invalid pkmn
if (!pkmn) {
buffer = {
status: "CATCH_ERROR"
};
// Missed
} else if (!msg.hit_pokemon || !bag[item]) {
buffer = {
status: "CATCH_MISSED"
};
// Escaped
} else if (Math.random() < .35) {
buffer = {
status: "CATCH_ESCAPE"
};
// Catched
} else {
player.catchPkmn(pkmn);
buffer = {
status: "CATCH_SUCCESS",
captured_pokemon_id: pkmn.encounterId,
capture_award: {
activity_type: ["ACTIVITY_CATCH_POKEMON"],
xp: [100],
candy: [3],
stardust: [100]
}
};
}
return (
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CatchPokemonResponse")
);
}

View File

@@ -5,7 +5,30 @@ import POGOProtos from "pokemongo-protobuf";
*/
export default function Encounter(msg) {
let buffer = {};
let encounter = msg.player.currentEncounter || this.getEncounterById(msg.encounter_id);
let buffer = {
status: "ENCOUNTER_SUCCESS",
capture_probability: {
pokeball_type: ["ITEM_POKE_BALL", "ITEM_GREAT_BALL", "ITEM_ULTRA_BALL"],
capture_probability: [1, 1, 1]
}
};
// Invalid pkmn
if (!encounter) {
msg.player.currentEncounter = null;
buffer.status = "ENCOUNTER_NOT_FOUND";
}
// Already encountered
else if (encounter.alreadyCatchedBy(msg.player)) {
buffer.status = "ENCOUNTER_ALREADY_HAPPENED";
}
// Encounter success
else {
msg.player.currentEncounter = encounter;
buffer.wild_pokemon = encounter.serializeWild();
}
return (
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.EncounterResponse")

View File

@@ -30,7 +30,7 @@ export default function GetMapObjects(msg) {
else ids.push(id);
});
}
mapCells.push(cell.serialize());
mapCells.push(cell.serialize(msg.player));
});
resolve(
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse")

View File

@@ -1,6 +1,7 @@
export Encounter from "./Encounter";
export FortSearch from "./FortSearch";
export FortDetails from "./FortDetails";
export CatchPokemon from "./CatchPokemon";
export GetMapObjects from "./GetMapObjects";
export CheckChallenge from "./CheckChallenge";
export GetDownloadUrls from "./GetDownloadUrls";

View File

@@ -31,7 +31,7 @@ export function routeRequest(req, res) {
this.processModelRequest(req, res, route);
break;
case "api":
if (!CFG.ENABLE_API) {
if (!CFG.API_ENABLE) {
print(`API is disabled! Denied API access for ${host}!`, 31);
return void 0;
}

View File

@@ -59,6 +59,7 @@ export function processResponse(player, req) {
case "ENCOUNTER":
case "FORT_SEARCH":
case "FORT_DETAILS":
case "CATCH_POKEMON":
case "GET_MAP_OBJECTS":
case "CHECK_CHALLENGE":
case "GET_DOWNLOAD_URLS":