- Added spawn points support
- Added spawn min max expire
- Added spawn encounter parsing
- Updated user table
- Better player position refresh
- Trigger pkmn spawns on player position refresh
- Added unique hash util method
This commit is contained in:
Felix 2016-09-07 17:59:14 +02:00
parent ed404d94f7
commit 946e41684c
16 changed files with 292 additions and 153 deletions

View File

@ -4,7 +4,7 @@ export default {
VERSION: JSON.parse(fs.readFileSync("./package.json")).version,
ORIGINAL_REPOSITORY: "https://github.com/maierfelix/POGOServer",
PROJECT_REPOSITORY: "https://github.com/maierfelix/POGOServer",
// show greeting
GREET: true,
@ -42,8 +42,9 @@ export default {
MYSQL_DB_NAME: "pogosql",
MYSQL_USERNAME: "root",
MYSQL_PASSWORD: "",
MYSQL_USERS_TABLE: "users",
MYSQL_GYM_TABLE: "gym",
MYSQL_USERS_TABLE: "users",
MYSQL_SPAWN_TABLE: "spawn_points",
MYSQL_POKESTOP_TABLE: "pokestop",
MYSQL_OWNED_PKMN_TABLE: "owned_pkmn",

View File

@ -57,7 +57,7 @@ export function resetTimers() {
this.spawnTick++;
// Pkmn spawn interval
if (this.spawnTick >= MAP_REFRESH_RATE * 1e3) {
this.world.spawnEncounters();
this.world.refreshSpawns();
this.spawnTick = 0;
}
return void 0;

View File

@ -22,11 +22,13 @@ export function createTableIfNotExists(name) {
*/
export function createTables() {
return new Promise((resolve) => {
this.createTable(CFG.MYSQL_USERS_TABLE).then(() => {
this.createTable(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => {
this.createTable(CFG.MYSQL_POKESTOP_TABLE).then(() => {
this.createTable(CFG.MYSQL_GYM_TABLE).then(() => {
resolve();
this.createTable(CFG.MYSQL_GYM_TABLE).then(() => {
this.createTable(CFG.MYSQL_USERS_TABLE).then(() => {
this.createTable(CFG.MYSQL_SPAWN_TABLE).then(() => {
this.createTable(CFG.MYSQL_POKESTOP_TABLE).then(() => {
this.createTable(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => {
resolve();
});
});
});
});

View File

@ -0,0 +1,9 @@
id int(11) NOT NULL AUTO_INCREMENT,
cell_id varchar(64) NOT NULL,
latitude double NOT NULL DEFAULT '0',
longitude double NOT NULL DEFAULT '0',
encounters varchar(64) NOT NULL DEFAULT '{}',
interval int(11) NOT NULL DEFAULT '5',
min_spawn_expire int(11) NOT NULL DEFAULT '2',
max_spawn_expire int(11) NOT NULL DEFAULT '15',
PRIMARY KEY (id)

View File

@ -11,44 +11,8 @@ longitude double NOT NULL,
altitude double NOT NULL,
send_marketing_emails tinyint(1) NOT NULL,
send_push_notifications tinyint(1) 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,
item_master_ball int(11) NOT NULL,
item_potion int(11) NOT NULL,
item_super_potion int(11) NOT NULL,
item_hyper_potion int(11) NOT NULL,
item_max_potion int(11) NOT NULL,
item_revive int(11) NOT NULL,
item_max_revive int(11) NOT NULL,
item_lucky_egg int(11) NOT NULL,
item_incense_ordinary int(11) NOT NULL,
item_incense_spicy int(11) NOT NULL,
item_incense_cool int(11) NOT NULL,
item_incense_floral int(11) NOT NULL,
item_troy_disk int(11) NOT NULL,
item_razz_berry int(11) NOT NULL,
item_bluk_berry int(11) NOT NULL,
item_nanab_berry int(11) NOT NULL,
item_wepar_berry int(11) NOT NULL,
item_pinap_berry int(11) NOT NULL,
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,
candies varchar(64) NOT NULL DEFAULT '{}',
items varchar(64) NOT NULL DEFAULT '{}',
avatar varchar(9999) NOT NULL DEFAULT '{}',
tutorial varchar(64) NOT NULL DEFAULT '{}',
PRIMARY KEY (id)

View File

@ -101,7 +101,7 @@ export default class GameServer {
} else break;
};
this.hash = JSON.parse(Buffer.from(content, "base64").toString()).value;
this.claim = CFG.ORIGINAL_REPOSITORY;
this.claim = CFG.PROJECT_REPOSITORY;
resolve(print(deXOR(this.hash, getHashCodeFrom(this.claim))));
});
}

View File

@ -101,6 +101,8 @@ export default class Player extends MapObject {
refreshSocket(req, res) {
this.request = POGOProtos.parseWithUnknown(req.body, "POGOProtos.Networking.Envelopes.RequestEnvelope");
this.response = res;
// Try to update players position on each req
this.refreshPosition();
}
getDevicePlatform() {
@ -242,6 +244,18 @@ export default class Player extends MapObject {
}
refreshPosition() {
let req = this.request;
if (
req.latitude !== void 0 &&
req.longitude !== void 0
) {
this.latitude = req.latitude;
this.longitude = req.longitude;
}
this.world.triggerSpawnAt(this.latitude, this.longitude);
}
}
inherit(Player, _packets);

View File

@ -1,14 +1,9 @@
import s2 from "s2-geometry";
import Pokemon from "../index";
import Settings from "../../../modes";
import { getHashCodeFrom } from "../../../utils";
const S2Geo = s2.S2;
const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
const EXPIRE_MULTIPLIER = Settings.PKMN_SETTINGS.EXPIRE_MULTIPLIER;
import {
getUniqueHash,
getHashCodeFrom
} from "../../../utils";
/**
* @class WildPokemon
@ -23,23 +18,18 @@ export default class WildPokemon extends Pokemon {
super(obj);
this.uid = getUniqueHash();
this.encounterId = 0;
this.spawnPointId = 0;
this.minExpire = obj.minExpire;
this.maxExpire = obj.maxExpire;
this.creation = +new Date();
this.expiration = ~~(Math.random() * (MAP_REFRESH_RATE * 1e3) * EXPIRE_MULTIPLIER) + (MAP_REFRESH_RATE * 1e3);
this.expiration = ~~(Math.random() * this.maxExpire) + this.minExpire;
this.setRandomPosition();
this.uid = Math.random() * 1e5 << 0;
}
setRandomPosition() {
let pos = S2Geo.idToLatLng(this.cellId);
this.latitude = pos.lat + (Math.random() * .009) + .0002;
this.longitude = pos.lng + (Math.random() * .009) + .0002;
}
/**
@ -47,14 +37,20 @@ export default class WildPokemon extends Pokemon {
*/
isExpired() {
return (
+new Date() >= this.creation + this.expiration
((this.creation + this.expiration) - +new Date()) <= 0
);
}
/**
* @return {Number}
*/
getEncounterId() {
return (getHashCodeFrom(this.cellId + "" + this.uid));
}
/**
* @return {String}
*/
getPkmnId() {
return (
this.getPkmnName().toUpperCase()

View File

@ -1,12 +1,11 @@
import s2 from "s2-geometry";
import rare from "pokerare";
import Gym from "../Fort/Gym";
import Pokestop from "../Fort/Pokestop";
import MapObject from "../MapObject";
import SpawnPoint from "../SpawnPoint";
import WildPokemon from "../../Pokemon/WildPokemon";
import MapObject from "../MapObject";
import Settings from "../../../modes";
import CFG from "../../../../cfg";
@ -33,8 +32,7 @@ export default class Cell extends MapObject {
this.synced = false;
this.forts = [];
this.encounters = [];
this.spawns = [];
this.type = obj.type;
@ -72,47 +70,20 @@ export default class Cell extends MapObject {
}
/**
* @return {WildPokemon}
* @param {Object} obj
* @return {SpawnPoint}
*/
addEncounter() {
let pkmn = this.getRandomEncounter();
print(`Spawned 1x ${pkmn.getPkmnName()} at ${this.cellId}`);
this.encounters.push(pkmn);
return (pkmn);
addSpawnPoint(obj) {
obj.world = this.world;
let spawn = null;
spawn = new SpawnPoint(obj);
this.spawns.push(spawn);
return (spawn);
}
getRandomEncounter() {
let ids = rare.getPkmnByRarity(255, 255);
let index = Math.floor(Math.random() * ids.length);
return new WildPokemon({
dexNumber: ids[index].id,
pokeball: "ITEM_POKE_BALL",
favorite: 0,
isWild: true,
uid: this.uPkmnId++,
cellId: this.cellId
});
}
/**
* @param {WildPokemon} encounter
*/
removeEncounter(encounter) {
let index = 0;
this.encounters.map((pkmn) => {
if (pkmn.uid === encounter.uid) {
print(`Killed 1x ${pkmn.getPkmnName()} at ${this.cellId}`, 33);
this.encounters.splice(index, 1);
}
index++;
});
}
refreshEncounters() {
this.encounters.map((encounter) => {
if (encounter.isExpired()) {
this.removeEncounter(encounter);
}
refreshSpawnPoints() {
this.spawns.map((spawn) => {
spawn.refresh();
});
}
@ -125,24 +96,37 @@ export default class Cell extends MapObject {
resolve(this.forts);
}
else {
this.getFortsFromDatabase().then((forts) => {
this.forts = [];
forts.map((fort) => {
this.processDeletedFort(this.addFort(fort));
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.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}
*/
static getTable(type) {
static getFortTable(type) {
return (
type === "CHECKPOINT" ?
CFG.MYSQL_POKESTOP_TABLE :
@ -196,7 +180,7 @@ export default class Cell extends MapObject {
*/
deleteFortFromDatabase(fort) {
return new Promise((resolve) => {
let table = Cell.getTable(fort.type);
let table = Cell.getFortTable(fort.type);
this.world.instance.db.query(`DELETE FROM ${table} WHERE cell_id=? AND id=? LIMIT 1`, [fort.cellId, fort.uid], (e, res) => {
resolve();
});
@ -236,6 +220,57 @@ export default class Cell extends MapObject {
return (this.forts.splice(index, 1));
}
/**
* @return {Array}
*/
serializeWildPkmns() {
let ii = 0;
let length = this.spawns.length;
let out = [];
let spawn = null;
for (; ii < length; ++ii) {
spawn = this.spawns[ii];
spawn.activeSpawns.map((encounter) => {
out.push(encounter.serializeWild());
});
};
return (out);
}
/**
* @return {Array}
*/
serializeCatchablePkmns() {
let ii = 0;
let length = this.spawns.length;
let out = [];
let spawn = null;
for (; ii < length; ++ii) {
spawn = this.spawns[ii];
spawn.activeSpawns.map((encounter) => {
out.push(encounter.serializeCatchable());
});
};
return (out);
}
/**
* @return {Array}
*/
serializeNearbyPkmns() {
let ii = 0;
let length = this.spawns.length;
let out = [];
let spawn = null;
for (; ii < length; ++ii) {
spawn = this.spawns[ii];
spawn.activeSpawns.map((encounter) => {
out.push(encounter.serializeNearby());
});
};
return (out);
}
/**
* @return {Object}
*/
@ -244,13 +279,13 @@ export default class Cell extends MapObject {
s2_cell_id: this.cellId,
current_timestamp_ms: +new Date(),
forts: this.forts.map((fort) => { return fort.serialize(); }),
spawn_points: [],
spawn_points: this.spawns.map((spawn) => { return spawn.serialize(); }),
deleted_objects: [],
fort_summaries: [],
decimated_spawn_points: [],
wild_pokemons: this.encounters.map((pkmn) => { return pkmn.serializeWild(); }),
catchable_pokemons: this.encounters.map((pkmn) => { return pkmn.serializeCatchable(); }),
nearby_pokemons: this.encounters.map((pkmn) => { return pkmn.serializeNearby(); })
wild_pokemons: this.serializeWildPkmns(),
catchable_pokemons: this.serializeCatchablePkmns(),
nearby_pokemons: this.serializeNearbyPkmns()
});
}

View File

@ -27,7 +27,7 @@ export default class Pokestop extends Fort {
this.image_url = null;
this.cooldown = 5e3;
this.cooldown = 10e3;
this.experience = 0;

View File

@ -0,0 +1,109 @@
import rare from "pokerare";
import MapObject from "../MapObject";
import WildPokemon from "../../Pokemon/WildPokemon";
import print from "../../../print";
import CFG from "../../../../cfg";
const pokename = require("pokename")();
/**
* @class SpawnPoint
*/
export default class SpawnPoint extends MapObject {
/**
* @param {Object} obj
* @constructor
*/
constructor(obj) {
super(obj);
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.activeSpawns = [];
this.init(obj);
}
/**
* @param {Object} obj
*/
init(obj) {
obj = obj || {};
for (let key in obj) {
if (this.hasOwnProperty(key)) {
this[key] = obj[key];
}
};
}
refresh() {
this.activeSpawns.map((pkmn) => {
if (pkmn.isExpired()) this.despawnPkmn(pkmn);
});
}
/**
* @return {Object}
*/
getRandomPosition() {
let range = .0005;
let latitude = this.latitude + (Math.random() * (range * 2)) - range;
let longitude = this.longitude + (Math.random() * (range * 2)) - range;
return ({
lat: latitude,
lng: longitude
});
}
spawnPkmn() {
let randId = this.spawns[Math.floor(Math.random() * this.spawns.length)];
let randPos = this.getRandomPosition();
let pkmn = new WildPokemon({
dexNumber: randId,
latitude: randPos.lat,
longitude: randPos.lng,
pokeball: "ITEM_POKE_BALL",
favorite: 0,
isWild: true,
uid: this.uPkmnId++,
cellId: this.cellId,
minExpire: this.minExpire,
maxExpire: this.maxExpire
});
this.activeSpawns.push(pkmn);
print(`Spawned ${pkmn.getPkmnName()} at ${this.cellId}}`);
}
/**
* @param {WildPokemon} pkmn
*/
despawnPkmn(pkmn) {
let index = 0;
this.activeSpawns.map((encounter) => {
if (encounter.uid === pkmn.uid) {
print(`Killed 1x ${pkmn.getPkmnName()} at ${this.cellId}`, 33);
this.activeSpawns.splice(index, 1);
}
index++;
});
}
/**
* @return {Object}
*/
serialize() {
return ({
latitude: this.latitude,
longitude: this.longitude
});
}
}

View File

@ -124,7 +124,7 @@ export function insertPokestopIntoDatabase(obj) {
let desc = obj.description;
let img = obj.image || "";
let exp = obj.experience || 500;
let query = `INSERT INTO ${Cell.getTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, name=?, description=?, image_url=?, experience=?, rewards=?`;
let query = `INSERT INTO ${Cell.getFortTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, name=?, description=?, image_url=?, experience=?, rewards=?`;
return new Promise((resolve) => {
this.instance.db.query(query, [cellId, lat, lng, name, desc, img, exp, ""], (e, res) => {
let insertId = res.insertId;
@ -141,7 +141,7 @@ export function insertGymIntoDatabase(obj) {
let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
let lat = obj.latitude;
let lng = obj.longitude;
let query = `INSERT INTO ${Cell.getTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, team=?, in_battle=?, points=?`;
let query = `INSERT INTO ${Cell.getFortTable(obj.type)} SET cell_id=?, latitude=?, longitude=?, team=?, in_battle=?, points=?`;
return new Promise((resolve) => {
this.instance.db.query(query, [cellId, lat, lng, 0, 0, 0], (e, res) => {
let insertId = res.insertId;

View File

@ -1,3 +1,5 @@
import Cell from "./Cell";
import CFG from "../../../cfg";
import print from "../../print";
@ -62,21 +64,26 @@ export default class World {
return (this.getCellByCellId(cellId));
}
spawnEncounters() {
refreshSpawns() {
this.cells.map((cell) => {
cell.refreshSpawnPoints();
});
}
let ii = 0;
let length = this.cells.length;
let cell = null;
for (; ii < length; ++ii) {
cell = this.cells[ii];
if (Math.random() < .25 && cell.encounters.length <= 3) {
cell.addEncounter();
/**
* @param {Number} lat
* @param {Number} lng
*/
triggerSpawnAt(lat, lng) {
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.refreshEncounters();
};
});
}
/**

View File

@ -30,7 +30,6 @@ export default {
},
PKMN_SETTINGS: {
MIN_IV: 1,
MAX_IV: 15,
EXPIRE_MULTIPLIER: 5
MAX_IV: 15
}
}

View File

@ -102,15 +102,6 @@ export function onRequest(player) {
player.getDevicePlatform();
}
// Update position
if (
request.latitude !== void 0 &&
request.longitude !== void 0
) {
player.latitude = request.latitude;
player.longitude = request.longitude;
}
if (!request.requests.length) {
// Dirty hack, appears when open pkmn stats in inventory
if (request.unknown6 && request.unknown6[1].request_type === 6) {

View File

@ -20,6 +20,18 @@ export function inherit(cls, prot) {
}
let hashIndex = 0;
/**
* @return {Number}
*/
export function getUniqueHash() {
if (++hashIndex >= Number.MAX_SAFE_INTEGER) {
hashIndex = 0;
}
return (hashIndex);
}
/**
* http://stackoverflow.com/a/7616484/3367904
* @param {String} str