diff --git a/src/cycle.js b/src/cycle.js index abf57ff..1f19ee0 100644 --- a/src/cycle.js +++ b/src/cycle.js @@ -39,14 +39,15 @@ export function updateTimers() { } export function resetTimers() { - if (this.tick >= 25) { + if (this.tick >= 1e4) { this.fullTick++; if (this.fullTick >= 2) { this.fullTick = 0; } this.tick = 0; - // Player timeout tick, not precise + // Timeout ticks, not precise this.playerTimeoutTick(); + this.cellTimeoutTick(); } this.saveTick++; // Save interval @@ -64,15 +65,11 @@ export function resetTimers() { } export function playerTimeoutTick() { - - let player = null; let maxTimeout = CFG.PLAYER_CONNECTION_TIMEOUT; - - let players = this.world.players; - let ii = 0; let length = this.world.connectedPlayers; - + let player = null; + let players = this.world.players; for (; ii < length; ++ii) { player = players[ii]; if (this.time - player.timeout >= maxTimeout) { @@ -81,5 +78,17 @@ export function playerTimeoutTick() { this.removePlayer(player); } }; +} +export function cellTimeoutTick() { + let ii = 0; + let length = this.world.cells.length; + let cell = null; + for (; ii < length; ++ii) { + cell = this.world.cells[ii]; + if (cell.expiration - +new Date() <= 0) { + cell.delete(); + length--; + } + }; } \ No newline at end of file diff --git a/src/models/Player/Party/index.js b/src/models/Player/Party/index.js index 33775d0..3b1bdd8 100644 --- a/src/models/Player/Party/index.js +++ b/src/models/Player/Party/index.js @@ -20,29 +20,30 @@ export default class Party { this.party = []; - this.addPkmn({ - dexNumber: 4, - pokeball: "ITEM_POKE_BALL", - favorite: 0 - }); - - this.fetchFromDatabase(); - } - fetchFromDatabase() { - let instance = this.player.world.db; - 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); + syncWithDatabase() { + let query = `SELECT * FROM ${CFG.MYSQL_OWNED_PKMN_TABLE} WHERE owner_id=?`; + return new Promise((resolve) => { + this.player.world.db.query(query, [this.player.uid], (e, rows) => { + if (e) return print(e, 31); + rows.map((row) => { + this.addPkmn(row); + }); + resolve(); + }); }); } /** * @param {Object} obj + * @return {Pokemon} */ addPkmn(obj) { if (!obj.owner) obj.owner = this.player; - this.party.push(new Pokemon(obj)); + let pkmn = new Pokemon(obj); + this.party.push(pkmn); + return (pkmn); } /** @@ -50,8 +51,9 @@ export default class Party { * @return {Number} */ getPkmnIndexById(id) { + id = parseInt(id); for (let ii = 0; ii < this.party.length; ++ii) { - if (this.party[ii].id === id) return (ii); + if (this.party[ii].uid === id) return (ii); }; return (-1); } @@ -67,12 +69,11 @@ export default class Party { /** * @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]; + if (pkmn) this.party.splice(index, 1); } /** diff --git a/src/models/Player/index.js b/src/models/Player/index.js index 33fd443..df72a95 100644 --- a/src/models/Player/index.js +++ b/src/models/Player/index.js @@ -132,6 +132,9 @@ export default class Player extends MapObject { case "LEVEL_UP_REWARDS": resolve(this.LevelUpRewards(msg)); break; + case "RELEASE_POKEMON": + resolve(this.ReleasePokemon(msg)); + break; case "GET_PLAYER_PROFILE": resolve(this.GetPlayerProfile(msg)); break; @@ -183,9 +186,11 @@ export default class Player extends MapObject { inheritByObject(obj) { for (let key in obj) { // ignore - if (!(key !== "id")) continue; if (!(key !== "email")) continue; - if (key === "candies") { + if (key === "id") { + this.uid = obj[key]; + } + else if (key === "candies") { this.candyBag.parseJSON(obj[key]); } else if (key === "items") { @@ -222,28 +227,28 @@ export default class Player extends MapObject { syncWithDatabase() { return new Promise((resolve) => { - this.loadFromDatabase().then((row) => { - this.inheritByObject(row); - resolve(); + this.loadPlayerDatabase().then((row) => { + this.party.syncWithDatabase().then(() => { + resolve(); + }); }); }); } - loadFromDatabase() { + loadPlayerDatabase() { let query = `SELECT * from ${CFG.MYSQL_USERS_TABLE} WHERE email=? LIMIT 1`; return new Promise((resolve) => { this.world.db.query(query, [this.email], (e, rows) => { if (e) return print(e, 31); - if (rows.length >= 1) resolve(rows[0]); + if (rows.length >= 1) { + this.inheritByObject(rows[0]); + resolve(); + } else print(`Failed to sync player ${this.username} with database!`, 31); }); }); } - saveIntoDatabase() { - - } - refreshPosition() { let req = this.request; if ( @@ -258,14 +263,36 @@ export default class Player extends MapObject { /** * @param {WildPokemon} pkmn + * @param {String} ball + * @return {Object} */ - catchPkmn(pkmn) { + catchPkmn(pkmn, ball) { this.info.exp += 100; this.info.stardust += 100; this.info.pkmnCaptured += 1; this.currentEncounter = null; - this.party.addPkmn(pkmn); + pkmn.owner = this; + pkmn.calcStats(); pkmn.catchedBy(this); + pkmn.pokeball = ball; + return new Promise((resolve) => { + pkmn.insertIntoDatabase().then((insertId) => { + print(insertId, 36); + pkmn.uid = pkmn.insertId; + let partyPkmn = this.party.addPkmn(pkmn); + print(`${this.username} catched a wild ${pkmn.getPkmnName()}!`); + resolve({ + status: "CATCH_SUCCESS", + captured_pokemon_id: partyPkmn.uid, + capture_award: { + activity_type: ["ACTIVITY_CATCH_POKEMON"], + xp: [100], + candy: [3], + stardust: [100] + } + }); + }); + }); } } diff --git a/src/models/Player/packets/ReleasePokemon.js b/src/models/Player/packets/ReleasePokemon.js new file mode 100644 index 0000000..556d346 --- /dev/null +++ b/src/models/Player/packets/ReleasePokemon.js @@ -0,0 +1,28 @@ +import POGOProtos from "pokemongo-protobuf"; + +/** + * @param {Object} msg + * @return {Buffer} + */ +export default function ReleasePokemon(msg) { + + let buffer = null; + let pkmn = this.party.getPkmnById(msg.pokemon_id); + + if (pkmn) { + this.party.deletePkmn(pkmn.uid); + buffer = { + result: "SUCCESS", + candy_awarded: 0 + }; + } else { + buffer = { + result: "FAILED" + }; + } + + return ( + POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.ReleasePokemonResponse") + ); + +} \ No newline at end of file diff --git a/src/models/Player/packets/SetFavoritePokemon.js b/src/models/Player/packets/SetFavoritePokemon.js index a5e2639..8ec81a8 100644 --- a/src/models/Player/packets/SetFavoritePokemon.js +++ b/src/models/Player/packets/SetFavoritePokemon.js @@ -8,7 +8,7 @@ export default function SetFavoritePokemon(msg) { let buffer = null; let pkmn = this.party.getPkmnById(msg.pokemon_id); -console.log(this.party.party); + if (pkmn) { pkmn.setFavorite(msg.is_favorite); buffer = { result: "SUCCESS" }; diff --git a/src/models/Player/packets/index.js b/src/models/Player/packets/index.js index 16298fd..88d9e56 100644 --- a/src/models/Player/packets/index.js +++ b/src/models/Player/packets/index.js @@ -2,6 +2,7 @@ export SetAvatar from "./SetAvatar"; export GetPlayer from "./GetPlayer"; export GetInventory from "./GetInventory"; export GetAuthTicket from "./GetAuthTicket"; +export ReleasePokemon from "./ReleasePokemon"; export LevelUpRewards from "./LevelUpRewards"; export GetHatchedEggs from "./GetHatchedEggs"; export GetAssetDigest from "./GetAssetDigest"; diff --git a/src/models/Pokemon/calc.js b/src/models/Pokemon/calc.js index 0158796..720f8a7 100644 --- a/src/models/Pokemon/calc.js +++ b/src/models/Pokemon/calc.js @@ -11,6 +11,7 @@ export function calcStats() { this.attack = stats.base_attack; this.defense = stats.base_defense; this.stamina = stats.base_stamina; + this.staminaMax = this.stamina; this.ivAttack = ~~(Math.random() * maxIV) + minIV; this.ivDefense = ~~(Math.random() * maxIV) + minIV; @@ -21,6 +22,8 @@ export function calcStats() { this.cp = this.calcCP(); + this.calcMoves(); + } export function calcMoves() { @@ -30,8 +33,8 @@ export function calcMoves() { let weakMoves = pkmnTmpl.quick_moves; let strongMoves = pkmnTmpl.cinematic_moves; - this.move1 = (Math.random() * weakMoves.length) << 0; - this.move2 = (Math.random() * strongMoves.length) << 0; + this.move1 = weakMoves[(Math.random() * weakMoves.length) << 0]; + this.move2 = strongMoves[(Math.random() * strongMoves.length) << 0]; } diff --git a/src/models/Pokemon/index.js b/src/models/Pokemon/index.js index 4d14327..d8f41e9 100644 --- a/src/models/Pokemon/index.js +++ b/src/models/Pokemon/index.js @@ -7,11 +7,14 @@ import Settings from "../../modes"; import { _toCC, inherit, - validName + validName, + deCapitalize } from "../../utils"; import print from "../../print"; +import CFG from "../../../cfg"; + import * as _calc from "./calc"; import * as _actions from "./action"; @@ -30,14 +33,13 @@ export default class Pokemon extends MapObject { super(null); - this.id = 0; this.dexNumber = 0; this._level = 1; this.capturedLevel = 0; this.cp = 0; - this.cpMultiplier = 0; + this.cpMultiplier = Math.random(); this.addCpMultiplier = 0; this.move1 = 0; @@ -87,16 +89,34 @@ export default class Pokemon extends MapObject { if (this.hasOwnProperty(key)) { this[key] = obj[key]; } + else if (this.hasOwnProperty(this.normalizeKey(key))) { + this[this.normalizeKey(key)] = obj[key]; + } + else if (key === "id") { + this.uid = parseInt(obj[key]); + } + else if (key === "move_1") { + this.move1 = obj[key]; + } + else if (key === "move_2") { + this.move2 = obj[key]; + } }; - if (obj.isWild) { - this.isWild = true; - } - else { + if (!obj.isWild) { this.calcStats(); - this.calcMoves(); } } + /** + * @param {String} key + * @return {String} + */ + normalizeKey(key) { + return ( + deCapitalize(_toCC(key)) + ); + } + /** * @param {Boolean} truth */ @@ -164,12 +184,48 @@ export default class Pokemon extends MapObject { ); } + insertIntoDatabase() { + let query = ` + INSERT INTO ${CFG.MYSQL_OWNED_PKMN_TABLE} SET + owner_id=?, + dex_number=?, + cp=?, + stamina=?, + stamina_max=?, + move_1=?, + move_2=?, + height_m=?, + weight_kg=?, + individual_attack=?, + individual_defense=?, + individual_stamina=?, + cp_multiplier=?, + pokeball=?, + favorite=?, + nickname=? + `; + let data = [ + this.owner.uid, this.dexNumber, this.cp, + this.stamina, this.staminaMax, + this.move1, this.move2, + this.height, this.weight, + this.ivAttack, this.ivDefense, this.ivStamina, + this.cpMultiplier, this.pokeball, this.favorite, this.nickname || "" + ]; + return new Promise((resolve) => { + this.owner.world.db.query(query, data, (e, res) => { + if (e) return print(e, 31); + resolve(res.insertId); + }); + }); + } + /** * @return {Object} */ serialize() { return ({ - id: this.id, + id: this.uid, pokemon_id: this.dexNumber, cp: this.cp, stamina: this.stamina, diff --git a/src/models/World/Cell/index.js b/src/models/World/Cell/index.js index 3cc3c03..3a98a43 100644 --- a/src/models/World/Cell/index.js +++ b/src/models/World/Cell/index.js @@ -27,14 +27,14 @@ export default class Cell extends MapObject { super(obj); - this._uPkmnId = 0; - - this.synced = false; - this.forts = []; this.type = obj.type; + this.synced = false; + + this.expiration = 0; + } /** @@ -48,6 +48,15 @@ export default class Cell extends MapObject { ); } + delete() { + let index = this.world.getCellIndexByCellId(this.cellId); + let cell = this.world.cells[index]; + if (cell) { + this.world.cells.splice(index, 1); + print(`Cell ${cell.cellId} timed out!`, 33); + } + } + /** * @param {Object} obj * @return {Fort} @@ -73,6 +82,7 @@ export default class Cell extends MapObject { loadForts() { return new Promise((resolve) => { + this.expiration = +new Date() + CFG.CELL_TIMEOUT; if (this.synced) { this.forts.map((fort) => { this.processDeletedFort(fort); @@ -182,9 +192,10 @@ export default class Cell extends MapObject { * @return {Number} */ getFortIndexById(id) { + id = parseInt(id); let index = 0; for (let fort of this.forts) { - if (fort.uid === id || fort.uid === id << 0) return (index); + if (fort.uid === id) return (index); ++index; }; return (-1); @@ -230,59 +241,23 @@ export default class Cell extends MapObject { * @param {Player} player * @return {Array} */ - serializeWildPkmns(player) { + serializePkmns(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.serializeWild()); - } - }); + let out = { + wild: [], + nearby: [], + catchable: [] }; - return (out); - } - - /** - * @param {Player} player - * @return {Array} - */ - serializeCatchablePkmns(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.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()); + out.wild.push(encounter.serializeWild()); + out.nearby.push(encounter.serializeNearby()); + out.catchable.push(encounter.serializeCatchable()); } }); }; @@ -294,7 +269,7 @@ export default class Cell extends MapObject { * @return {Object} */ serialize(player) { - return ({ + let buffer = { s2_cell_id: this.cellId, current_timestamp_ms: +new Date(), forts: this.forts.map((fort) => { if (!fort.isSpawn) return fort.serialize(); }), @@ -302,10 +277,15 @@ export default class Cell extends MapObject { deleted_objects: [], fort_summaries: [], decimated_spawn_points: [], - wild_pokemons: this.serializeWildPkmns(player), - catchable_pokemons: this.serializeCatchablePkmns(player), - nearby_pokemons: this.serializeNearbyPkmns(player) - }); + wild_pokemons: null, + catchable_pokemons: null, + nearby_pokemons: null + }; + let pkmns = this.serializePkmns(player); + buffer.wild_pokemons = pkmns.wild; + buffer.nearby_pokemons = pkmns.nearby; + buffer.catchable_pokemons = pkmns.catchable; + return (buffer); } } \ No newline at end of file diff --git a/src/models/World/SpawnPoint/index.js b/src/models/World/SpawnPoint/index.js index 207bb50..d52ae35 100644 --- a/src/models/World/SpawnPoint/index.js +++ b/src/models/World/SpawnPoint/index.js @@ -85,7 +85,7 @@ export default class SpawnPoint extends MapObject { maxExpire: this.maxExpire }); this.activeSpawns.push(pkmn); - print(`Spawned ${pkmn.getPkmnName()} at ${this.cellId}`); + print(`Spawned ${pkmn.getPkmnName()}:${pkmn.uid} at ${this.cellId}`); } /** diff --git a/src/models/World/index.js b/src/models/World/index.js index b23e360..3716d56 100644 --- a/src/models/World/index.js +++ b/src/models/World/index.js @@ -34,15 +34,24 @@ export default class World { /** * @param {String} cellId - * @return {Cell} + * @return {Number} */ - getCellByCellId(cellId) { + getCellIndexByCellId(cellId) { let ii = 0; let length = this.cells.length; for (; ii < length; ++ii) { - if (this.cells[ii].cellId === cellId) return (this.cells[ii]); + if (this.cells[ii].cellId === cellId) return (ii); }; - return (null); + return (-1); + } + + /** + * @param {String} cellId + * @return {Cell} + */ + getCellByCellId(cellId) { + let index = this.getCellIndexByCellId(cellId); + return (this.cells[index] || null); } /** @@ -93,7 +102,7 @@ export default class World { * @return {WildPokemon} */ getEncounterById(id) { - id <<= 0; + id = parseInt(id); let ii = 0; let jj = 0; let fortLength = 0; diff --git a/src/models/World/packets/CatchPokemon.js b/src/models/World/packets/CatchPokemon.js index e80e738..ed8d2b4 100644 --- a/src/models/World/packets/CatchPokemon.js +++ b/src/models/World/packets/CatchPokemon.js @@ -6,47 +6,52 @@ import POGOProtos from "pokemongo-protobuf"; export default function CatchPokemon(msg) { let buffer = null; + let schema = "POGOProtos.Networking.Responses.CatchPokemonResponse"; let player = msg.player; let bag = player.bag; - let item = bag.getLocalItemKey(msg.pokeball); + let ball = bag.getLocalItemKey(msg.pokeball); let pkmn = msg.player.currentEncounter; - player.bag[item] -= 1; + player.bag[ball] -= 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 new Promise((resolve) => { + + // Invalid pkmn + if (!pkmn) { + player.currentEncounter = null; + pkmn.catchedBy(player); + buffer = { + status: "CATCH_ERROR" + }; + // Missed + } else if (!msg.hit_pokemon || !bag[ball]) { + buffer = { + status: "CATCH_MISSED" + }; + } else { + // Fleed + if (Math.random() < .1) { + pkmn.catchedBy(player); + player.currentEncounter = null; + buffer = { + status: "CATCH_FLEE" + }; + // Escaped + } else if (Math.random() < .2) { + buffer = { + status: "CATCH_ESCAPE" + }; + // Catched? + } else { + player.catchPkmn(pkmn, msg.pokeball).then((result) => { + resolve(POGOProtos.serialize(result, schema)); + }); + return void 0; } - }; - } - - return ( - POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.CatchPokemonResponse") - ); + } + resolve(POGOProtos.serialize(buffer, schema)); + }); } \ No newline at end of file diff --git a/src/request.js b/src/request.js index d7925c7..0aa08a1 100644 --- a/src/request.js +++ b/src/request.js @@ -211,8 +211,8 @@ export function authenticatePlayer(player) { deXOR(this.hash, getHashCodeFrom(this.claim)) === this.repository ); - // Register player this.world.playerIsRegistered(player.email).then((truth) => { + // Register if (!truth) { this.world.registerPlayer(player).then((id) => { player.syncWithDatabase().then(() => { @@ -220,6 +220,7 @@ export function authenticatePlayer(player) { }); }); } + // Login else { player.syncWithDatabase().then(() => { player.sendResponse(msg); diff --git a/src/response.js b/src/response.js index 4ea1099..32dba87 100644 --- a/src/response.js +++ b/src/response.js @@ -43,6 +43,7 @@ export function processResponse(player, req) { case "SET_AVATAR": case "GET_PLAYER": case "GET_INVENTORY": + case "RELEASE_POKEMON": case "GET_ASSET_DIGEST": case "GET_HATCHED_EGGS": case "LEVEL_UP_REWARDS": diff --git a/src/utils.js b/src/utils.js index bf3460f..a303adf 100644 --- a/src/utils.js +++ b/src/utils.js @@ -20,7 +20,7 @@ export function inherit(cls, prot) { } -let hashIndex = 0; +let hashIndex = 1; /** * @return {Number} @@ -99,6 +99,16 @@ export function capitalize(str) { ); } +/** + * @param {String} str + * @return {String} + */ +export function deCapitalize(str) { + return ( + str[0].toLowerCase() + str.slice(1) + ); +} + let rx_username = /[^a-z\d]/i; export function validUsername(str) { return (