Dump models and asset digest on first start

- Now dumps models and asset_digest on first start into /data
- Reworked the route request routine
- Added local ip4 display (is this ok?)
- Model download urls are now routed to the server itself (need to get
this more efficient)
- Updated version to 0.3.4
This commit is contained in:
Felix 2016-08-19 16:53:09 +02:00
parent 31a230caf7
commit 8b61647a6d
12 changed files with 166 additions and 140 deletions

10
cfg.js
View File

@ -4,10 +4,8 @@ export default {
MAX_CONNECTIONS: 64,
PORT: 3000,
GAME_MODE: 0,
TICK_INTERVAL: 1,
SAVE_INTERVAL: 60000,
PLAYER_CONNECTION_TIMEOUT: 1800000,
BOOT_TIMEOUT: 10000,
MINIMUM_CLIENT_VERSION: "0.33.0",
DEFAULT_CONSOLE_COLOR: 32,
TRANSFER_ACCOUNTS: false,
@ -40,6 +38,12 @@ export default {
// Server debug options
DEBUG_DUMP_PATH: "./logs/",
DEBUG_DUMP_TRAFFIC: true,
DEBUG_LOG_REQUESTS: true
DEBUG_LOG_REQUESTS: true,
// Better dont touch these
TICK_INTERVAL: 1,
BOOT_TIMEOUT: 10000,
// Currently supported pokemon
MAX_POKEMON_NATIONAL_ID: 151
}

View File

@ -1,6 +1,6 @@
{
"name": "POGOServer",
"version": "0.3.2",
"version": "0.3.4",
"description": "",
"repository": {
"type": "git",
@ -9,7 +9,7 @@
"scripts": {
"test": "echo \"Error: no test specified\"",
"babel-node": "babel-node --presets=es2015",
"start": "nodemon --ignore tmp/ --exec npm run babel-node -- ./src/index.js"
"start": "nodemon --ignore tmp/ --ignore data/ --ignore logs/ --exec npm run babel-node -- ./src/index.js"
},
"engines": {
"node": ">= 6.x",

View File

@ -43,6 +43,7 @@ class GameServer {
collections: {}
};
this.asset = null;
this.socket = null;
this.cycleInstance = null;
@ -57,6 +58,9 @@ class GameServer {
this.clients = [];
this.greet();
this.print(`Booting Server v${require("../package.json").version}...`, 33);
this.setup();
}
@ -77,40 +81,6 @@ class GameServer {
}
createAssetDownloadSession() {
return new Promise((resolve) => {
pogodown.login({
provider: String(CFG.DOWNLOAD_PROVIDER).toLowerCase(),
username: CFG.DOWNLOAD_USERNAME,
password: CFG.DOWNLOAD_PASSWORD,
downloadModels: false
}).then((asset) => {
if (asset && asset.digest && asset.digest.length) {
this.print("Created asset download session");
resolve(asset);
}
else {
this.print("Failed to download asset digest!", 31);
return void 0;
}
});
});
}
/**
* @param {Array} assets
*/
generateDownloadUrlByAssetId(assets) {
return new Promise((resolve) => {
pogodown.getAssetByAssetId(assets).then((response) => {
// maybe cache and provide own local download link?
resolve(response);
});
});
}
/**
* @return {HTTP}
*/
@ -134,7 +104,7 @@ class GameServer {
let buffer = Buffer.concat(chunks);
req.body = buffer;
player.updateResponse(res);
this.routeRequest(req);
this.routeRequest(req, res);
});
});
server.listen(CFG.PORT);

View File

@ -14,7 +14,7 @@ export default function FortSearch(obj) {
items_awarded: [
new proto.Inventory.Item.ItemAward({
item_id: proto.Inventory.Item.ItemId.ITEM_MASTER_BALL,
item_count: 10
item_count: 3
}),
new proto.Inventory.Item.ItemAward({
item_id: proto.Inventory.Item.ItemId.ITEM_ULTRA_BALL,

View File

@ -7,30 +7,8 @@ import CFG from "../../cfg";
* @param {Request} req
* @return {Object}
*/
export default function GetAssetDigest(asset, req) {
export default function GetAssetDigest(req) {
let data = proto.Networking.Requests.Messages.GetAssetDigestMessage.decode(req.request_message.toBuffer());
let digests = [];
asset.digest.map((item) => {
digests.push(
new proto.Data.AssetDigestEntry({
asset_id: item.getAssetId(),
bundle_name: item.getBundleName(),
version: item.getVersion(),
checksum: item.getChecksum(),
size: item.getSize(),
key: item.getKey()
})
);
});
return (
new proto.Networking.Responses.GetAssetDigestResponse({
timestamp_ms: new Date().getTime() * 1e3,
digest: digests
}).encode()
);
return (fs.readFileSync("data/asset_digest"));
}

View File

@ -2,6 +2,8 @@ import proto from "../proto";
import DownloadUrlEntry from "./Data.DownloadUrlEntry";
import CFG from "../../cfg";
import {
getDownloadUrlByAssetId
} from "../utils.js";
@ -9,7 +11,7 @@ import {
/**
* @return {Object}
*/
export default function GetDownloadUrls(asset, req, download) {
export default function GetDownloadUrls(asset, ip, req) {
let data = proto.Networking.Requests.Messages.GetDownloadUrlsMessage.decode(req.request_message.toBuffer());
@ -26,25 +28,20 @@ export default function GetDownloadUrls(asset, req, download) {
};
return new Promise((resolve) => {
download(key).then((asset) => {
console.log(node);
download_urls.push(
new proto.Data.DownloadUrlEntry({
url: asset[0].asset,
asset_id: key,
size: parseInt(node.size),
checksum: parseInt(node.checksum)
})
);
let output = (
new proto.Networking.Responses.GetDownloadUrlsResponse({
download_urls: download_urls
}).encode()
);
resolve(output);
});
download_urls.push(
new proto.Data.DownloadUrlEntry({
asset_id: key,
url: `http://${ip}:${CFG.PORT}/model/${node.bundle_name}`,
size: node.size,
checksum: node.checksum
})
);
let output = (
new proto.Networking.Responses.GetDownloadUrlsResponse({
download_urls: download_urls
}).encode()
);
resolve(output);
});
}

View File

@ -11,9 +11,11 @@ import {
*/
export default function GetMapObjects(player, request) {
var cells = proto.Networking.Requests.Messages.GetMapObjectsMessage.decode(request.request_message.toBuffer()).cell_id;
let cells = proto.Networking.Requests.Messages.GetMapObjectsMessage.decode(request.request_message.toBuffer()).cell_id;
var cellsRes = [];
let cellsRes = [];
let pokemon_id = 121;
cells.forEach((cell) => {
cellsRes.push(new proto.Map.MapCell({
@ -59,7 +61,7 @@ export default function GetMapObjects(player, request) {
longitude: longitude,
spawn_point_id: "87bdd289c69",
pokemon_data: new proto.Data.PokemonData({
pokemon_id: 145,
pokemon_id: pokemon_id,
cp: 277,
stamina: 41,
stamina_max: 41,
@ -80,7 +82,7 @@ export default function GetMapObjects(player, request) {
new proto.Map.Pokemon.MapPokemon({
spawn_point_id: "87bdd289c69",
encounter_id: 11810991820755313517,
pokemon_id: 145,
pokemon_id: pokemon_id,
latitude: latitude,
longitude: longitude,
expiration_timestamp_ms: (new Date().getTime() + 1e6) * 1e3
@ -90,7 +92,7 @@ export default function GetMapObjects(player, request) {
cell.nearby_pokemons = [
new proto.Map.Pokemon.NearbyPokemon({
distance_in_meters: 200.0,
pokemon_id: 145
pokemon_id: pokemon_id
})
];

View File

@ -53,7 +53,7 @@ class Player {
this.gender = 0;
this.backpack = 0;
this.tutorial_state = [32, 1, 3, 4, 7];
this.tutorial_state = [];
this.badges = null;
this.pokedex = null;

View File

@ -1,4 +1,3 @@
import pogo from "node-pogo-protos";
import path from "path";
export default pogo;

View File

@ -1,3 +1,5 @@
import fs from "fs";
import url from "url";
import proto from "./proto";
import CFG from "../cfg";
@ -9,6 +11,45 @@ import {
const REQUEST = proto.Networking.Requests.RequestType;
/**
* @param {Request} req
* @param {Response} res
*/
export function routeRequest(req, res) {
let player = this.getPlayerByRequest(req);
let parsed = url.parse(req.url).pathname;
let route = parsed.split("/");
let host = req.headers.host;
switch (route[1]) {
case "plfe":
case "custom":
if (route[2] === "rpc") this.onRequest(req);
break;
case "model":
// make sure no random dudes can access download
if (!player.authenticated || !player.email_verified) return void 0;
let name = route[2];
if (name && name.length > 1) {
fs.readFile("data/" + name, (error, data) => {
if (error) {
this.print(`Error file resolving model ${name}:` + error, 31);
return void 0;
}
this.print(`Sent ${name} to ${player.email}`, 36);
res.end(data);
});
}
break;
default:
console.log(`Unknown request url: https://${req.headers.host}${req.url}`);
break;
};
}
/**
* @param {Request} req
* @return {String}
@ -118,26 +159,6 @@ export function processRequests(player, requests) {
}
/**
* @param {Request} req
*/
export function routeRequest(req) {
let url = String(req.url);
let route = url.substring(url.lastIndexOf("/") + 1);
let host = req.headers.host;
switch (route) {
case "rpc":
this.onRequest(req);
break;
default:
console.log(`Unknown request url: https://${req.headers.host}${req.url}`);
break;
};
}
/**
* @param {Request} req
* @return {Boolean}

View File

@ -67,7 +67,7 @@ export function processResponse(player, req) {
buffer = DownloadRemoteConfigVersion(req);
break;
case REQUEST.GET_ASSET_DIGEST:
buffer = GetAssetDigest(this.asset, req);
buffer = GetAssetDigest(req);
break;
case REQUEST.GET_PLAYER_PROFILE:
buffer = GetPlayerProfile();
@ -81,7 +81,7 @@ export function processResponse(player, req) {
return void 0;
break;
case REQUEST.GET_DOWNLOAD_URLS:
GetDownloadUrls(this.asset, req, this.generateDownloadUrlByAssetId).then((res) => {
GetDownloadUrls(this.asset, this.getLocalIPv4(), req).then((res) => {
resolve(res);
});
return void 0;

View File

@ -1,36 +1,91 @@
import fs from "fs";
import fse from "fs-extra";
import pogo from "pogo-asset-downloader";
import proto from "./proto";
import CFG from "../cfg";
export function setup() {
this.print(`Booting Server v${require("../package.json").version}...`, 33);
let isFirstRun = !!fse.ensureDirSync("data/");
let assetSessionLoaded = false;
if (isFirstRun) {
this.print("Preparing to dump required assets..", 36);
setTimeout(() => {
this.onFirstRun(() => {
this.setup();
});
}, 1e3);
return void 0;
}
setTimeout(() => {
if (!assetSessionLoaded) {
this.print("Boot timeout, please check your login details!", 31);
this.asset = this.parseAssetDigest();
this.setupDatabaseConnection().then(() => {
if (CFG.PORT < 1) {
this.print("Invalid port!", 31);
return void 0;
}
}, CFG.BOOT_TIMEOUT);
this.createAssetDownloadSession().then((asset) => {
assetSessionLoaded = true;
this.asset = asset;
this.setupDatabaseConnection().then(() => {
if (CFG.PORT < 1) {
this.print("Invalid port!", 31);
return void 0;
}
this.socket = this.createHTTPServer();
setTimeout(this::this.cycle, 1);
let localIPv4 = this.getLocalIPv4();
this.print(`Server running at ${localIPv4}:${CFG.PORT}`);
});
this.socket = this.createHTTPServer();
setTimeout(this::this.cycle, 1);
let localIPv4 = this.getLocalIPv4();
this.print(`Server running at ${localIPv4}:${CFG.PORT}`);
});
}
export function parseAssetDigest() {
let asset = null;
try {
asset = proto.Networking.Responses.GetAssetDigestResponse.decode(fs.readFileSync("data/asset_digest"));
} catch (e) {
this.print(e, 31);
}
return (asset);
}
export function onFirstRun(resolve) {
pogo.login({
provider: CFG.DOWNLOAD_PROVIDER, // google or ptc
username: CFG.DOWNLOAD_USERNAME,
password: CFG.DOWNLOAD_PASSWORD
}).then((asset) => {
this.print(`Dumping asset digest..`, 35);
fs.writeFileSync("data/asset_digest", asset.toBuffer());
this.dumpPkmnModels(() => {
resolve();
});
});
}
export function dumpPkmnModels(resolve) {
let limit = CFG.MAX_POKEMON_NATIONAL_ID;
const dump = (index) => {
let ids = [];
if (++index <= limit) ids.push(index);
if (++index <= limit) ids.push(index);
if (++index <= limit) ids.push(index);
pogo.getAssetByPokemonId(ids).then((downloads) => {
downloads.map((item) => {
this.print(`Dumping model ${item.name}..`, 35);
try {
fs.writeFileSync("data/" + item.name, item.body);
}
catch (e) {
this.print(`Error while dumping model ${item.name}:` + e, 31);
}
});
if (index >= limit) {
this.print(`Dumped ${limit} assets successfully!`);
resolve();
return void 0;
}
setTimeout(() => dump(index), 2e3);
});
};
dump(0);
}