- Spawn cycle
- Fixed avatar gender change
- Basic wild pokemon
- Basic map encounter spawns
- Store cells inside array instead of object, to improve performance
- Increased map visibility range dramatically
- Lint game master parsing
This commit is contained in:
Felix 2016-09-06 20:26:38 +02:00
parent 2de38e5568
commit 00793fd42d
17 changed files with 299 additions and 67 deletions

View File

@ -1,6 +1,9 @@
import print from "./print";
import CFG from "../cfg";
import Settings from "./modes";
const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
export function startCycle() {
this.cycleInstance = setTimeout(() => this.cycle(), CFG.TICK_INTERVAL);
}
@ -51,6 +54,12 @@ export function resetTimers() {
//this.saveAllPlayers();
this.saveTick = 0;
}
this.spawnTick++;
// Pkmn spawn interval
if (this.spawnTick >= MAP_REFRESH_RATE * 1e3) {
this.world.spawnEncounters();
this.spawnTick = 0;
}
return void 0;
}

View File

@ -63,6 +63,7 @@ export default class GameServer {
this.time = 0;
this.fullTick = 0;
this.saveTick = 0;
this.spawnTick = 0;
this.timeoutTick = 0;
this.passedTicks = 0;

View File

@ -1,28 +1,34 @@
import fs from "fs";
import POGOProtos from "pokemongo-protobuf";
import {
idToPkmnBundleName
} from "../../utils";
import CFG from "../../../cfg";
import ENUM from "../../enum";
import print from "../../print";
/**
* @class GameMaster
*/
export default class GameMaster {
/**
* @param {Object} decode
* @param {GameServer} instance
* @constructor
*/
constructor(decode) {
constructor(instance) {
this.instance = instance;
this.settings = this.buildSettings();
this.decode = decode;
this.decode = this.parse();
this.buffer = this.encode();
this.parse();
this.parseItemTemplates();
}
@ -36,6 +42,17 @@ export default class GameMaster {
}
parse() {
let master = null;
try {
let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master");
master = this.instance.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse");
} catch (e) {
print(e, 31);
}
return (master);
}
parseItemTemplates() {
let item = null;
let items = this.decode.item_templates;

View File

@ -70,7 +70,7 @@ export default class Avatar {
return (this._pants);
}
set pants(value) {
if (this.between(value, 0, 2)) {
if (this.between(value, 0, 5)) {
this._pants = value;
}
}
@ -125,6 +125,17 @@ export default class Avatar {
}
}
resetOutfit() {
this.skin = 0;
this.hair = 0;
this.shirt = 0;
this.pants = 0;
this.hat = 0;
this.shoes = 0;
this.eyes = 0;
this.backpack = 0;
}
/**
* @return {Object}
*/
@ -149,7 +160,7 @@ export default class Avatar {
let obj = JSON.parse(str);
for (let key in obj) {
if (this.hasOwnProperty("_" + key)) {
this[key] = obj[key];
this[key] = obj[key] << 0;
}
};
}

View File

@ -61,6 +61,8 @@ export default class Player extends MapObject {
this.remoteAddress = null;
this.currentEncounter = null;
this.bag = new Bag(this);
this.candyBag = new CandyBag(this);

View File

@ -6,6 +6,19 @@ import POGOProtos from "pokemongo-protobuf";
*/
export default function SetAvatar(msg) {
if (!msg.player_avatar.hasOwnProperty("gender")) {
msg.player_avatar.gender = "MALE";
}
let gender = msg.player_avatar.gender;
let genderChange = this.avatar.gender !== gender;
msg.player_avatar.gender = gender === "MALE" ? 0 : 1;
if (genderChange) {
this.avatar.resetOutfit();
}
for (let key in msg.player_avatar) {
this.avatar[key] = msg.player_avatar[key];
};

View File

@ -1,10 +1,19 @@
import s2 from "s2-geometry";
import Pokemon from "../index";
//import CaptureProbability from "CaptureProbability";
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;
/**
* @class WildPokemon
*/
class WildPokemon extends Pokemon {
export default class WildPokemon extends Pokemon {
/**
* @param {Object} obj
@ -15,14 +24,80 @@ class WildPokemon extends Pokemon {
super(obj);
this.encounterId = 0;
this.spawnPointId = 0;
this.latitude = 0;
this.longitude = 0;
this.creation = +new Date();
this.pokeball = 0;
this.expiration = ~~(Math.random() * (MAP_REFRESH_RATE * 1e3) * EXPIRE_MULTIPLIER) + (MAP_REFRESH_RATE * 1e3);
this.spawnPoint = 0;
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;
}
/**
* @return {Boolean}
*/
isExpired() {
return (
+new Date() >= this.creation + this.expiration
);
}
getEncounterId() {
return (getHashCodeFrom(this.cellId + "" + this.uid));
}
getPkmnId() {
return (
this.getPkmnName().toUpperCase()
);
}
/**
* @return {Object}
*/
serializeWild() {
return ({
encounter_id: this.getEncounterId(),
last_modified_timestamp_ms: +new Date(),
latitude: this.latitude,
longitude: this.longitude,
pokemon_data: {
pokemon_id: this.getPkmnId()
},
time_till_hidden_ms: this.expiration
});
}
/**
* @return {Object}
*/
serializeCatchable() {
return ({
encounter_id: this.getEncounterId(),
pokemon_id: this.getPkmnId(),
expiration_timestamp_ms: this.creation + this.expiration,
latitude: this.latitude,
longitude: this.longitude
});
}
/**
* @return {Object}
*/
serializeNearby() {
return ({
pokemon_id: this.getPkmnId(),
encounter_id: this.getEncounterId()
});
}
}

View File

@ -62,6 +62,8 @@ export default class Pokemon extends MapObject {
this.nickname = null;
this.pokeball = null;
this.isWild = false;
this.init(obj);
}
@ -84,8 +86,13 @@ export default class Pokemon extends MapObject {
this[key] = obj[key];
}
};
this.calcStats();
this.calcMoves();
if (obj.isWild) {
this.isWild = true;
}
else {
this.calcStats();
this.calcMoves();
}
}
/**

View File

@ -1,8 +1,11 @@
import s2 from "s2-geometry";
import rare from "pokerare";
import Gym from "../Fort/Gym";
import Pokestop from "../Fort/Pokestop";
import WildPokemon from "../../Pokemon/WildPokemon";
import MapObject from "../MapObject";
import Settings from "../../../modes";
import CFG from "../../../../cfg";
@ -25,14 +28,26 @@ export default class Cell extends MapObject {
super(obj);
this._uPkmnId = 0;
this.synced = false;
this.forts = [];
this.encounters = [];
this.type = obj.type;
}
get uPkmnId() {
return (this._uPkmnId);
}
set uPkmnId(value) {
if (this.uPkmnId < Number.MAX_SAFE_INTEGER) this._uPkmnId += value;
else this._uPkmnId = 0;
}
/**
* @param {Number} lat
* @param {Number} lng
@ -44,6 +59,63 @@ export default class Cell extends MapObject {
);
}
/**
* @param {Object} obj
* @return {Fort}
*/
addFort(obj) {
obj.world = this.world;
let fort = null;
fort = obj.type === "CHECKPOINT" ? new Pokestop(obj) : new Gym(obj);
this.forts.push(fort);
return (fort);
}
/**
* @return {WildPokemon}
*/
addEncounter() {
let pkmn = this.getRandomEncounter();
print(`Spawned 1x ${pkmn.getPkmnName()} at ${this.cellId}`);
this.encounters.push(pkmn);
return (pkmn);
}
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);
}
});
}
loadForts() {
return new Promise((resolve) => {
if (this.synced) {
@ -165,15 +237,21 @@ export default class Cell extends MapObject {
}
/**
* @param {Object} obj
* @return {Fort}
* @return {Object}
*/
addFort(obj) {
obj.world = this.world;
let fort = null;
fort = obj.type === "CHECKPOINT" ? new Pokestop(obj) : new Gym(obj);
this.forts.push(fort);
return (fort);
serialize() {
return ({
s2_cell_id: this.cellId,
current_timestamp_ms: +new Date(),
forts: this.forts.map((fort) => { return fort.serialize(); }),
spawn_points: [],
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(); })
});
}
}

View File

@ -46,7 +46,7 @@ export function getFortsByCells(cells, out, index) {
*/
export function getFortsByCellId(cellId) {
return new Promise((resolve) => {
if (!this.cellRegistered(cellId)) {
if (!this.cellAlreadyRegistered(cellId)) {
this.registerCell(cellId).then((cell) => {
resolve(cell);
});
@ -68,7 +68,7 @@ export function registerCell(cellId) {
world: this,
cellId: cellId
});
this.cells[cellId] = cell;
this.cells.push(cell);
return new Promise((resolve) => {
cell.loadForts().then(() => {
resolve(this.getCellById(cellId));

View File

@ -22,7 +22,7 @@ export default class World {
this.players = [];
this.cells = {};
this.cells = [];
}
@ -30,15 +30,27 @@ export default class World {
return (this.players.length);
}
/**
* @param {String} cellId
* @return {Cell}
*/
getCellByCellId(cellId) {
let ii = 0;
let length = this.cells.length;
for (; ii < length; ++ii) {
if (this.cells[ii].cellId === cellId) return (this.cells[ii]);
};
return (null);
}
/**
* @param {String} cellId
* @return {Boolean}
*/
cellRegistered(cellId) {
cellAlreadyRegistered(cellId) {
let cell = this.getCellByCellId(cellId);
return (
this.cells[cellId] !== null &&
this.cells[cellId] !== void 0 &&
this.cells[cellId]
cell !== null
);
}
@ -47,15 +59,24 @@ export default class World {
* @return {Cell}
*/
getCellById(cellId) {
return (this.cells[cellId]);
return (this.getCellByCellId(cellId));
}
addGym() {
}
spawnEncounters() {
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();
}
cell.refreshEncounters();
};
addWildPokemon() {
}
/**
@ -65,6 +86,11 @@ export default class World {
getPacket(type, msg) {
return new Promise((resolve) => {
switch (type) {
case "ENCOUNTER":
this.Encounter(msg).then((result) => {
resolve(result);
});
break;
case "FORT_SEARCH":
this.FortSearch(msg).then((result) => {
resolve(result);

View File

@ -0,0 +1,14 @@
import POGOProtos from "pokemongo-protobuf";
/**
* @param {Object} msg
*/
export default function Encounter(msg) {
let buffer = {};
return (
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.EncounterResponse")
);
}

View File

@ -30,18 +30,7 @@ export default function GetMapObjects(msg) {
else ids.push(id);
});
}
mapCells.push({
s2_cell_id: cell.cellId,
current_timestamp_ms: +new Date(),
forts: cell.forts.map((fort) => { return fort.serialize() }),
spawn_points: [],
deleted_objects: [],
fort_summaries: [],
decimated_spawn_points: [],
wild_pokemons: [],
catchable_pokemons: [],
nearby_pokemons: []
});
mapCells.push(cell.serialize());
});
resolve(
POGOProtos.serialize(buffer, "POGOProtos.Networking.Responses.GetMapObjectsResponse")

View File

@ -1,3 +1,4 @@
export Encounter from "./Encounter";
export FortSearch from "./FortSearch";
export FortDetails from "./FortDetails";
export GetMapObjects from "./GetMapObjects";

View File

@ -11,9 +11,9 @@ export default {
far_interaction_range_meters: 1000.0156862745098
},
map_settings: {
pokemon_visible_range: 70.00196078431372,
pokemon_visible_range: 999.00196078431372,
poke_nav_range_meters: 751.0156862745098,
encounter_range_meters: 50.25098039215686,
encounter_range_meters: 999.25098039215686,
get_map_objects_min_refresh_seconds: 16,
get_map_objects_max_refresh_seconds: 16,
get_map_objects_min_distance_meters: 10.007843017578125,
@ -30,6 +30,7 @@ export default {
},
PKMN_SETTINGS: {
MIN_IV: 1,
MAX_IV: 15
MAX_IV: 15,
EXPIRE_MULTIPLIER: 5
}
}

View File

@ -56,6 +56,7 @@ export function processResponse(player, req) {
return void 0;
break;
// Global
case "ENCOUNTER":
case "FORT_SEARCH":
case "FORT_DETAILS":
case "GET_MAP_OBJECTS":

View File

@ -39,8 +39,7 @@ export function setup() {
print(`Downloaded assets are valid! Proceeding..`);
let parsedMaster = this.parseGameMaster();
shared.GAME_MASTER = new GameMaster(parsedMaster);
shared.GAME_MASTER = new GameMaster(this);
this.setupDatabaseConnection().then(() => {
if (CFG.PORT < 1) {
@ -93,9 +92,8 @@ export function validateModels() {
return new Promise((resolve, reject) => {
const validate = (index) => {
let platform = pogo.platforms[index];
let name = platform.name;
let path = CFG.DUMP_ASSET_PATH + name + "/";
let platform = pogo.platforms[index].name;
let path = CFG.DUMP_ASSET_PATH + platform + "/";
// ups, validate asset_digest's too
if (!this.fileExists(path + "asset_digest")) {
@ -103,7 +101,7 @@ export function validateModels() {
}
else {
let buffer = fs.readFileSync(path + "asset_digest");
shared.GAME_ASSETS[name] = {
shared.GAME_ASSETS[platform] = {
buffer: buffer,
decode: this.parseProtobuf(buffer, "POGOProtos.Networking.Responses.GetAssetDigestResponse")
};
@ -130,17 +128,6 @@ export function validateModels() {
}
export function parseGameMaster() {
let master = null;
try {
let data = fs.readFileSync(CFG.DUMP_ASSET_PATH + "game_master");
master = this.parseProtobuf(data, "POGOProtos.Networking.Responses.DownloadItemTemplatesResponse");
} catch (e) {
print(e, 31);
}
return (master);
}
export function onFirstRun(resolve) {
print(`Attempt to login with ${_toCC(CFG.DOWNLOAD_PROVIDER)}..`, 33);
pogo.login({