- Improve live map object editing
- Added fort table
- Restructured world, forts and cells
- Todo: Make sure forts always have unique uid, likely breaks when add
and delete multiple forts
This commit is contained in:
Felix 2016-08-30 18:56:57 +02:00
parent 7f65c4fc1b
commit 19b3e3b5f2
16 changed files with 480 additions and 309 deletions

View File

@ -41,6 +41,7 @@ export default {
MYSQL_USERNAME: "root",
MYSQL_PASSWORD: "",
MYSQL_USERS_TABLE: "users",
MYSQL_FORT_TABLE: "forts",
MYSQL_OWNED_PKMN_TABLE: "owned_pkmn",
// Used for asset download session

View File

@ -3,6 +3,8 @@ import url from "url";
import s2 from "s2-geometry";
import prompt from "prompt";
import Cell from "./models/World/Cell";
import print from "./print";
import CFG from "../cfg";
@ -167,7 +169,7 @@ export function api_spawnPkmnToPlayer(data) {
export function api_addFortToPosition(data) {
return new Promise((resolve) => {
this.world.insertFort(data).then((fort) => {
this.world.insertFortIntoDatabase(data).then((fort) => {
resolve({
success: true
});
@ -176,23 +178,45 @@ export function api_addFortToPosition(data) {
}
export function api_deleteFortById(data) {
let cellId = data.cell_id;
let uid = data.cell_uid;
let uid = data.uid;
let cellId = Cell.getIdByPosition(data.latitude, data.longitude, data.zoom);
return new Promise((resolve) => {
this.world.getFortById(cellId, uid).then((fort) => {
fort.deleted = true;
this.world.processDeletedFort(fort);
this.world.deleteFort(cellId, uid).then(() => {
resolve({
success: true
});
});
});
}
export function getNeighbors(lvl, lat, lng) {
export function api_getFortsByPosition(data) {
return new Promise((resolve) => {
let lat = data.latitude;
let lng = data.longitude;
let zoom = data.zoom;
this.getNeighboredForts(this.getNeighbors(lat, lng, zoom), [], 0).then((forts) => {
let result = [];
for (let fort of forts) {
let fortData = fort.serialize();
fortData.name = fort.name;
fortData.uid = fort.uid;
result.push(fortData);
};
resolve({
forts: result,
success: true
});
});
});
}
export function getNeighbors(lat, lng, lvl) {
let origin = S2Geo.latLngToKey(lat, lng, lvl);
let walk = [S2Geo.keyToId(origin)];
let next = S2Geo.nextKey(origin);
let prev = S2Geo.prevKey(origin);
let ii = 0;
let length = 20;
let length = 30;
for (; ii < length; ++ii) {
walk.push(S2Geo.toId(prev));
walk.push(S2Geo.toId(next));
@ -205,23 +229,10 @@ export function getNeighbors(lvl, lat, lng) {
export function getNeighboredForts(cells, out, index) {
return new Promise((resolve) => {
let id = cells[index];
this.world.getFortsByCell(id).then((res) => {
out = out.concat(res.forts);
this.world.getFortsByCellId(id).then((cell) => {
out = out.concat(cell.forts);
if (++index >= cells.length) resolve(out);
else resolve(this.getNeighboredForts(cells, out, index));
});
});
}
export function api_getFortsByPosition(data) {
return new Promise((resolve) => {
let lat = data.lat;
let lng = data.lng;
this.getNeighboredForts(this.getNeighbors(15, lat, lng), [], 0).then((res) => {
resolve({
forts: res,
success: true
});
});
});
}

View File

@ -24,7 +24,9 @@ export function createTables() {
return new Promise((resolve) => {
this.createTable(CFG.MYSQL_USERS_TABLE).then(() => {
this.createTable(CFG.MYSQL_OWNED_PKMN_TABLE).then(() => {
resolve();
this.createTable(CFG.MYSQL_FORT_TABLE).then(() => {
resolve();
});
});
});
});

10
src/db/tables/forts.table Normal file
View File

@ -0,0 +1,10 @@
id int(11) NOT NULL AUTO_INCREMENT,
cell_id varchar(64) NOT NULL,
cell_uid int(11) NOT NULL,
latitude double NOT NULL,
longitude double NOT NULL,
name varchar(64) NOT NULL,
description varchar(128) NOT NULL,
image_url varchar(128) NOT NULL,
rewards varchar(64) NOT NULL
PRIMARY KEY (id)

View File

@ -74,7 +74,7 @@ export default class GameServer {
fetchVersioningUrl() {
return new Promise((resolve) => {
let url = "";
let branch = "dev";
let branch = "master";
let base = "https://raw.githubusercontent.com";
url = require("../package.json").repository.url;
url = url.replace("git://", "");

View File

@ -0,0 +1,134 @@
import s2 from "s2-geometry";
import Fort from "../Fort";
import MapObject from "../MapObject";
import Settings from "../../../modes";
import CFG from "../../../../cfg";
import print from "../../../print";
const S2Geo = s2.S2;
const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
/**
* @class Cell
*/
export default class Cell extends MapObject {
/**
* @param {Object} obj
* @constructor
*/
constructor(obj) {
super(obj);
this.synced = false;
this.forts = [];
}
/**
* @param {Number} lat
* @param {Number} lng
* @return {String}
*/
static getIdByPosition(lat, lng, zoom) {
return (
S2Geo.keyToId(S2Geo.latLngToKey(lat, lng, zoom || 15))
);
}
loadForts() {
return new Promise((resolve) => {
if (this.synced) {
this.forts.map((fort) => {
this.processDeletedFort(fort);
});
resolve(this.forts);
}
else {
this.getFortsFromDatabase().then((forts) => {
forts.map((fort) => {
this.processDeletedFort(this.addFort(fort));
});
this.synced = true;
resolve(this.forts);
});
}
});
}
getFortsFromDatabase() {
return new Promise((resolve) => {
this.world.instance.getQueryByColumnFromTable("cell_id", this.cellId, CFG.MYSQL_FORT_TABLE).then((forts) => {
resolve(forts || []);
});
});
}
/**
* Dirty hack to display disappearing forts
* seems like game engine doesnt support dat
* @param {Fort} fort
*/
processDeletedFort(fort) {
if (fort.deleted) {
fort.latitude = 0;
fort.longitude = 0;
fort.enabled = false;
// wait for the next map refresh
setTimeout(() => {
this.deleteFortById(fort.uid);
}, (MAP_REFRESH_RATE * 1e3) * 3);
}
}
/**
* @param {Number} id
* @return {Number}
*/
getFortIndexById(id) {
let index = 0;
for (let fort of this.forts) {
if (fort.uid === id) return (index);
++index;
};
return (-1);
}
/**
* @param {Number} id
* @return {Fort}
*/
getFortById(id) {
let index = this.getFortIndexById(id);
if (index < 0) return (null);
return (this.forts[index]);
}
/**
* @param {Number} id
* @return {Fort}
*/
deleteFortById(id) {
let index = this.getFortIndexById(id);
if (index < 0) return (null);
return (this.forts.splice(index, 1));
}
/**
* @param {Object} obj
* @return {Fort}
*/
addFort(obj) {
obj.world = this.world;
let fort = new Fort(obj);
fort.parent = this;
fort.cellId = fort.parent.cellId;
this.forts.push(fort);
return (fort);
}
}

View File

@ -20,6 +20,8 @@ export default class Fort extends MapObject {
super(obj);
this.parent = null;
this.enabled = true;
this.deleted = false;
@ -90,6 +92,10 @@ export default class Fort extends MapObject {
);
}
delete() {
this.deleted = true;
}
/**
* @return {Object}
*/

View File

@ -0,0 +1,46 @@
import MapObject from "../MapObject";
import print from "../../../print";
import CFG from "../../../../cfg";
import {
validName
} from "../../../utils";
/**
* @class Gym
*/
export default class Gym extends MapObject {
/**
* @param {Object} obj
* @constructor
*/
constructor(obj) {
super(obj);
this.init(obj);
}
/**
* @param {Object} obj
*/
init(obj) {
obj = obj || {};
for (let key in obj) {
if (this.hasOwnProperty(key)) {
this[key] = obj[key];
}
};
}
/**
* @return {Object}
*/
serialize() {
return ({});
}
}

View File

@ -13,6 +13,8 @@ export default class MapObject {
this.cellId = null;
this.world = null;
this.altitude = 0;
this.latitude = 0;
this.longitude = 0;

114
src/models/World/forts.js Normal file
View File

@ -0,0 +1,114 @@
import Cell from "./Cell";
import CFG from "../../../cfg";
import print from "../../print";
/**
* @param {Array} cells
* @param {Array} out
* @param {Number} index
*/
export function getFortsByCells(cells, out, index) {
return new Promise((resolve) => {
this.getFortsByCellId(cells[index]).then((result) => {
out.push(result);
if (++index >= cells.length) resolve(out);
else resolve(this.getFortsByCells(cells, out, index));
});
});
}
/**
* @param {String} cellId
*/
export function getFortsByCellId(cellId) {
return new Promise((resolve) => {
if (!this.cellRegistered(cellId)) {
this.registerCell(cellId).then((cell) => {
resolve(cell);
});
}
else {
let cell = this.getCellById(cellId);
cell.loadForts().then(() => {
resolve(cell);
});
}
});
}
/**
* @param {String} cellId
*/
export function registerCell(cellId) {
let cell = new Cell({
world: this,
cellId: cellId
});
this.cells[cellId] = cell;
return new Promise((resolve) => {
cell.loadForts().then(() => {
resolve(this.getCellById(cellId));
});
});
}
/**
* @param {Object} obj
*/
export function addFort(obj) {
let cellId = Cell.getIdByPosition(obj.latitude, obj.longitude, obj.zoom);
return new Promise((resolve) => {
this.getFortsByCellId(cellId).then((cell) => {
let fort = cell.addFort(obj);
resolve(fort);
});
});
}
export function deleteFort(cellId, uid) {
let cell = this.getCellById(cellId);
let fort = cell.getFortById(uid);
return new Promise((resolve) => {
this.deleteFortFromDatabase(fort).then(() => {
fort.delete();
resolve();
});
});
}
export function insertFortIntoDatabase(obj) {
return new Promise((resolve) => {
this.addFort(obj).then((fort) => {
let query = `INSERT INTO ${CFG.MYSQL_FORT_TABLE} SET cell_id=?, cell_uid=?, latitude=?, longitude=?, name=?, description=?, image_url=?, rewards=?`;
let id = fort.parent.cellId;
let lat = fort.latitude;
let lng = fort.longitude;
let name = fort.name;
let desc = fort.description;
let img = "http://thecatapi.com/api/images/get?format=src&type=png&d=" + +new Date();
this.instance.db.query(query, [id, 0, lat, lng, name, desc, img, ""], (e, res) => {
let insertId = res.insertId;
this.instance.db.query(`SELECT * from ${CFG.MYSQL_FORT_TABLE} WHERE cell_id=? ORDER BY cell_uid DESC`, [id], (e, res) => {
let index = res instanceof Array ? res[0].cell_uid + 1 : 0;
this.instance.db.query(`UPDATE ${CFG.MYSQL_FORT_TABLE} SET cell_uid=? WHERE id=?`, [index, insertId], (e, res) => {
fort.uid = index;
resolve(fort);
});
});
});
});
});
}
/**
* @param {Fort} fort
*/
export function deleteFortFromDatabase(fort) {
return new Promise((resolve) => {
this.instance.db.query(`DELETE FROM ${CFG.MYSQL_FORT_TABLE} WHERE cell_id=? AND cell_uid=? LIMIT 1`, [fort.cellId, fort.uid], (e, res) => {
resolve();
});
});
}

View File

@ -1,21 +1,13 @@
import s2 from "s2-geometry";
import Fort from "./Fort";
import Player from "../Player";
import Settings from "../../modes";
import CFG from "../../../cfg";
import print from "../../print";
import * as _forts from "./forts";
import * as _players from "./players";
import * as _packets from "./packets";
import { inherit } from "../../utils";
const S2Geo = s2.S2;
const MAP_REFRESH_RATE = Settings.GAME_SETTINGS.map_settings.get_map_objects_max_refresh_seconds;
/**
* @class World
*/
@ -39,290 +31,33 @@ export default class World {
}
/**
* @param {String} cellId
* @return {Boolean}
*/
isFull() {
cellRegistered(cellId) {
return (
this.connectedPlayers >= CFG.MAX_CONNECTIONS
this.cells[cellId] !== null &&
this.cells[cellId] !== void 0 &&
this.cells[cellId]
);
}
/**
* @param {Request} req
* @param {Response} res
* @param {String} cellId
* @return {Cell}
*/
getPlayerByRequest(req, res) {
let player = null;
if (this.playerAlreadyConnected(req)) {
player = this.getPlayerByIP(req.headers.host);
return (player);
}
else {
player = this.addPlayer(req, res);
return (player);
}
getCellById(cellId) {
return (this.cells[cellId]);
}
/**
* @param {Request} req
* @return {Boolean}
*/
playerAlreadyConnected(req) {
let ii = 0;
let length = this.connectedPlayers;
let remoteAddress = req.headers.host;
for (; ii < length; ++ii) {
if (this.players[ii].remoteAddress === remoteAddress) {
return (true);
}
};
return (false);
}
/**
* @param {String} ip
*/
getPlayerByIP(ip) {
let players = this.players;
let ii = 0;
let length = this.connectedPlayers;
for (; ii < length; ++ii) {
if (players[ii].remoteAddress === ip) {
return (players[ii]);
}
};
return (null);
}
/**
* @param {Request} req
* @param {Response} res
* @return {Player}
*/
addPlayer(req, res) {
let player = new Player({
world: this,
request: req,
response: res
});
player.remoteAddress = req.headers.host;
this.players.push(player);
return (player);
}
/**
* @param {Player} player
*/
removePlayer(player) {
console.log("Remove:", player.email);
}
spawnFort() {
}
spawnGym() {
addGym() {
}
spawnEncounter() {
addWildPokemon() {
}
/**
* @param {String} id
* @return {Object}
*/
getCellById(id) {
if (!this.cells[id]) {
this.cells[id] = {
id: id,
forts: [],
synced: false
};
}
return (this.cells[id]);
}
getFortsByCells(cells, out, index) {
return new Promise((resolve) => {
this.getFortsByCell(cells[index]).then((result) => {
out.push(result);
if (++index >= cells.length) {
print(`Syncing forts with database!`, 33);
resolve(out);
}
else resolve(this.getFortsByCells(cells, out, index));
});
});
}
/**
* Dirty hack to display disappearing forts
* @param {Fort} fort
*/
processDeletedFort(fort) {
if (fort.deleted) {
fort.latitude = 0;
fort.longitude = 0;
this.instance.db.query("DELETE FROM forts WHERE cell_id=? AND cell_uid=? LIMIT 1", [fort.cellId, fort.uid], (e, res) => {
setTimeout(() => {
this.deleteFortById(fort.cellId, fort.uid);
}, MAP_REFRESH_RATE + 2e3);
});
}
}
/**
* @param {String} id
* @param {Number} uid
* @return {Number}
*/
getFortIndexById(id, uid) {
let cell = this.getCellById(id);
let index = 0;
if (!cell) return (-1);
for (let fort of cell.forts) {
if (fort.uid === uid) return (index);
++index;
};
return (-1);
}
/**
* @param {String} id
* @param {Number} uid
* @return {Fort}
*/
getFortById(id, uid) {
let cell = this.getCellById(id);
let index = this.getFortIndexById(id, uid);
return new Promise((resolve) => {
if (!cell.forts[index]) {
this.getFortsByCell(id).then((forts) => {
resolve(this.getFortById(id, uid));
});
}
else {
resolve(cell.forts[index]);
}
});
}
/**
* @param {String} id
* @param {Number} uid
* @return {Fort}
*/
deleteFortById(id, uid) {
let cell = this.getCellById(id);
let index = this.getFortIndexById(id, uid);
if (index < 0) return (null);
return (cell.forts.splice(index, 1));
}
/**
* @param {Object} obj
*/
insertFort(obj) {
let lat = obj.latitude;
let lng = obj.longitude;
let id = S2Geo.keyToId(S2Geo.latLngToKey(lat, lng, obj.zoom));
let name = obj.name || "";
let desc = obj.description || "";
let img = "http://thecatapi.com/api/images/get?format=src&type=png&d=" + +new Date();
let query = "INSERT INTO forts SET cell_id=?, cell_uid=?, latitude=?, longitude=?, name=?, description=?, image_url=?, rewards=?";
obj.cell_id = id;
return new Promise((resolve) => {
this.instance.db.query(query, [id, 0, lat, lng, name, desc, img, ""], (e, res) => {
let insertId = res.insertId;
this.instance.db.query("SELECT * from forts WHERE cell_id=? ORDER BY cell_uid DESC", [id], (e, res) => {
let index = res instanceof Array ? res[0].cell_uid + 1 : 0;
this.instance.db.query("UPDATE forts SET cell_uid=? WHERE id=?", [index, insertId], (e, res) => {
obj.cell_uid = index;
resolve(this.addFort(obj));
});
});
});
});
}
/**
* @param {Object} obj
* @return {Fort}
*/
addFort(obj) {
let cell = this.getCellById(obj.cell_id);
let fort = new Fort(obj);
cell.forts.push(fort);
return (fort);
}
/**
* @param {String} cell
* @return {Array}
*/
getFortsByCell(cellId) {
return new Promise((resolve) => {
let cell = this.getCellById(cellId);
// cached
if (cell && cell.synced) {
cell.forts.map((fort) => {
this.processDeletedFort(fort);
});
resolve(cell);
}
// load from db
else this.loadFortsFromDbByCell(cellId).then((forts) => {
let result = this.getCellById(cellId);
forts.map((fort) => {
let index = result.forts.push(new Fort(fort)) - 1;
this.processDeletedFort(result.forts[index]);
});
cell.synced = true;
resolve(result);
});
});
}
/**
* @param {String} cell
*/
loadFortsFromDbByCell(cell) {
return new Promise((resolve) => {
this.instance.getQueryByColumnFromTable("cell_id", cell, "forts").then((forts) => {
forts = forts || [];
resolve(forts);
});
});
}
/**
* @param {String} type
* @param {Object} msg
@ -363,4 +98,6 @@ export default class World {
}
inherit(World, _forts);
inherit(World, _players);
inherit(World, _packets);

View File

@ -11,7 +11,7 @@ export default function FortDetails(msg) {
let id = msg.fort_id.split(".");
return new Promise((resolve) => {
this.instance.db.query("SELECT * from forts WHERE cell_id=? AND cell_uid=?", [id[0], id[1]], (e, forts) => {
this.instance.db.query(`SELECT * from ${CFG.MYSQL_FORT_TABLE} WHERE cell_id=? AND cell_uid=?`, [id[0], id[1]], (e, forts) => {
let fort = forts[0];
if (!fort) return void 0;
let buffer = {

View File

@ -11,7 +11,7 @@ export default function FortSearch(msg) {
let id = msg.fort_id.split(".");
return new Promise((resolve) => {
this.instance.db.query("SELECT * from forts WHERE cell_id=? AND cell_uid=?", [id[0], id[1]], (e, forts) => {
this.instance.db.query(`SELECT * from ${CFG.MYSQL_FORT_TABLE} WHERE cell_id=? AND cell_uid=?`, [id[0], id[1]], (e, forts) => {
let fort = forts[0];
if (!fort) return void 0;
let buffer = ({
@ -21,7 +21,7 @@ export default function FortSearch(msg) {
{ item_id: 4 }
],
experience_awarded: 1337,
cooldown_complete_timestamp_ms: +new Date() + 5e3,
cooldown_complete_timestamp_ms: +new Date() + fort.cooldown,
chain_hack_sequence_number: 2
});
resolve(

View File

@ -29,8 +29,14 @@ export default function GetMapObjects(msg) {
return new Promise((resolve) => {
this.getFortsByCells(msg.cell_id, [], 0).then((cells) => {
cells.map((cell) => {
if (cell.forts.length) {
console.log("###" + cell.cellId + "###");
cell.forts.map((fort) => {
console.log(fort.uid, fort.latitude, fort.longitude);
});
}
mapCells.push({
s2_cell_id: cell.id,
s2_cell_id: cell.cellId,
current_timestamp_ms: +new Date(),
forts: cell.forts.map((fort) => { return fort.serialize() }),
spawn_points: [],

102
src/models/World/players.js Normal file
View File

@ -0,0 +1,102 @@
import Player from "../Player";
import CFG from "../../../cfg";
import print from "../../print";
/**
* @return {Boolean}
*/
export function isFull() {
return (
this.connectedPlayers >= CFG.MAX_CONNECTIONS
);
}
/**
* @param {Request} req
* @param {Response} res
*/
export function getPlayerByRequest(req, res) {
let player = null;
if (this.playerAlreadyConnected(req)) {
player = this.getPlayerByIP(req.headers.host);
return (player);
}
else {
player = this.addPlayer(req, res);
return (player);
}
}
/**
* @param {Request} req
* @return {Boolean}
*/
export function playerAlreadyConnected(req) {
let ii = 0;
let length = this.connectedPlayers;
let remoteAddress = req.headers.host;
for (; ii < length; ++ii) {
if (this.players[ii].remoteAddress === remoteAddress) {
return (true);
}
};
return (false);
}
/**
* @param {String} ip
*/
export function getPlayerByIP(ip) {
let players = this.players;
let ii = 0;
let length = this.connectedPlayers;
for (; ii < length; ++ii) {
if (players[ii].remoteAddress === ip) {
return (players[ii]);
}
};
return (null);
}
/**
* @param {Request} req
* @param {Response} res
* @return {Player}
*/
export function addPlayer(req, res) {
let player = new Player({
world: this,
request: req,
response: res
});
player.remoteAddress = req.headers.host;
this.players.push(player);
return (player);
}
/**
* @param {Player} player
*/
export function removePlayer(player) {
console.log("Remove:", player.email);
}

View File

@ -14,8 +14,8 @@ export default {
pokemon_visible_range: 70.00196078431372,
poke_nav_range_meters: 751.0156862745098,
encounter_range_meters: 50.25098039215686,
get_map_objects_min_refresh_seconds: 0,
get_map_objects_max_refresh_seconds: 0,
get_map_objects_min_refresh_seconds: 8,
get_map_objects_max_refresh_seconds: 8,
get_map_objects_min_distance_meters: 10.007843017578125,
google_maps_api_key: CFG.GMAPS_KEY
},