Merge pull request #95 from gsemaj/monkeys

Paths framework + Monkey Skyway System framework
This commit is contained in:
dongresource 2020-09-22 03:54:01 +02:00 committed by GitHub
commit 5d8bb7f8a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 189 additions and 27 deletions

View File

@ -32,6 +32,8 @@ npcdata=tdata/NPCs.json
xdtdata=tdata/xdt.json
# mob json
mobdata=tdata/mobs.json
# path json
pathdata=tdata/paths.json
# is everyone a GM?
gm=true

View File

@ -42,6 +42,8 @@ void TableData::init() {
std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
}
loadPaths(); // load paths
// load everything else from xdttable
std::cout << "[INFO] Parsing xdt.json..." << std::endl;
std::ifstream infile(settings::XDTJSON);
@ -213,3 +215,61 @@ int TableData::getItemType(int itemSet) {
}
return overriden;
}
/*
* Load paths from paths JSON.
*/
void TableData::loadPaths() {
try {
std::ifstream inFile(settings::PATHJSON);
nlohmann::json pathData;
// read file into json
inFile >> pathData;
// skyway paths
nlohmann::json pathDataSkyway = pathData["skyway"];
for (nlohmann::json::iterator skywayPath = pathDataSkyway.begin(); skywayPath != pathDataSkyway.end(); skywayPath++) {
constructPath(skywayPath);
}
std::cout << "[INFO] Loaded " << TransportManager::SkywayPaths.size() << " skyway paths" << std::endl;
}
catch (const std::exception& err) {
std::cerr << "[WARN] Malformed paths.json file! Reason:" << err.what() << std::endl;
}
}
/*
* Create a full and properly-paced Skyway System path by interpolating between keyframes.
*/
void TableData::constructPath(nlohmann::json::iterator _pathData) {
auto pathData = _pathData.value();
// Interpolate
nlohmann::json pathPoints = pathData["points"];
std::queue<WarpLocation> points;
nlohmann::json::iterator _point = pathPoints.begin();
auto point = _point.value();
WarpLocation last = { point["iX"] , point["iY"] , point["iZ"] }; // start pos
// use some for loop trickery; start position should not be a point
for (_point++; _point != pathPoints.end(); _point++) {
point = _point.value();
WarpLocation coords = { point["iX"] , point["iY"] , point["iZ"] };
// Calculate distance between this point and the last
int dXY = hypot(last.x - coords.x, last.y - coords.y); // XY plane distance
int distanceBetween = hypot(dXY, last.z - coords.z); // total distance
int lerps = distanceBetween / (int)pathData["iMonkeySpeed"]; // integer division to ensure a whole number of in-between points
for (int i = 0; i < lerps; i++) {
WarpLocation lerp;
// lerp math
float frac = (i + 1) * 1.0f / (lerps + 1);
lerp.x = (last.x * (1.0f - frac)) + (coords.x * frac);
lerp.y = (last.y * (1.0f - frac)) + (coords.y * frac);
lerp.z = (last.z * (1.0f - frac)) + (coords.z * frac);
points.push(lerp); // add lerp'd point to the queue
}
points.push(coords); // add keyframe to the queue
last = coords; // update start pos
}
TransportManager::SkywayPaths[pathData["iRouteID"]] = points;
}

View File

@ -1,9 +1,13 @@
#pragma once
#include <map>
#include "contrib/JSON.hpp"
namespace TableData {
void init();
void cleanup();
int getItemType(int);
void loadPaths();
void constructPath(nlohmann::json::iterator);
}

View File

@ -3,11 +3,16 @@
#include "PlayerManager.hpp"
#include "TransportManager.hpp"
#include <unordered_map>
std::map<int32_t, TransportRoute> TransportManager::Routes;
std::map<int32_t, TransportLocation> TransportManager::Locations;
std::map<int32_t, std::queue<WarpLocation>> TransportManager::SkywayPaths;
std::unordered_map<CNSocket*, std::queue<WarpLocation>> TransportManager::SkywayQueues;
void TransportManager::init() {
REGISTER_SHARD_TIMER(tickSkywaySystem, 1000);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION, transportRegisterLocationHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION, transportWarpHandler);
}
@ -19,7 +24,8 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket
sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION* transport = (sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION*)data->buf;
Player* plr = PlayerManager::getPlayer(sock);
std::cout << "request to register transport, eTT " << transport->eTT << ", locID " << transport->iLocationID << ", npc " << transport->iNPC_ID << std::endl;
bool newReg = false; // this is a new registration
//std::cout << "request to register transport, eTT " << transport->eTT << ", locID " << transport->iLocationID << ", npc " << transport->iNPC_ID << std::endl;
if (transport->eTT == 1) { // S.C.A.M.P.E.R.
if (transport->iLocationID < 1 || transport->iLocationID > 31) { // sanity check
std::cout << "[WARN] S.C.A.M.P.E.R. location ID " << transport->iLocationID << " is out of bounds" << std::endl;
@ -33,8 +39,12 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket
return;
}
// update registration bitfield using bit shifting + bitwise or
plr->iWarpLocationFlag |= (1UL << (transport->iLocationID - 1));
// update registration bitfield using bitmask
uint32_t newScamperFlag = plr->iWarpLocationFlag | (plr->IsGM ? UINT32_MAX : (1UL << (transport->iLocationID - 1)));
if (newScamperFlag != plr->iWarpLocationFlag) {
plr->iWarpLocationFlag = newScamperFlag;
newReg = true;
}
} else if (transport->eTT == 2) { // Monkey Skyway System
if (transport->iLocationID < 1 || transport->iLocationID > 127) { // sanity check
std::cout << "[WARN] Skyway location ID " << transport->iLocationID << " is out of bounds" << std::endl;
@ -49,10 +59,20 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket
}
/*
* assuming the two bitfields are just stuck together to make a longer one... do a similar operation, but on the respective integer
* this approach seems to work with initial testing, but we have yet to see a monkey ID greater than 63.
* assuming the two bitfields are just stuck together to make a longer one, do a similar operation
*/
plr->aSkywayLocationFlag[transport->iLocationID > 63 ? 1 : 0] |= (1ULL << (transport->iLocationID > 63 ? transport->iLocationID - 65 : transport->iLocationID - 1));
if (plr->IsGM) {
plr->aSkywayLocationFlag[0] = UINT64_MAX;
plr->aSkywayLocationFlag[1] = UINT64_MAX;
newReg = true;
} else {
int index = transport->iLocationID > 64 ? 1 : 0;
uint64_t newMonkeyFlag = plr->aSkywayLocationFlag[index] | (1ULL << (index ? transport->iLocationID - 65 : transport->iLocationID - 1));
if (newMonkeyFlag != plr->aSkywayLocationFlag[index]) {
plr->aSkywayLocationFlag[index] = newMonkeyFlag;
newReg = true;
}
}
} else {
std::cout << "[WARN] Unknown mode of transport; eTT = " << transport->eTT << std::endl;
INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, failResp);
@ -65,8 +85,10 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket
return;
}
INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC, resp);
if (!newReg)
return; // don't send new registration message
INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC, resp);
// response parameters
resp.eTT = transport->eTT;
resp.iLocationID = transport->iLocationID;
@ -92,8 +114,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data)
* iSlotNum -- inventory slot number
*/
if (Routes.find(req->iTransporationID) == Routes.end() || Locations.find(Routes[req->iTransporationID].end) == Locations.end()
|| Routes[req->iTransporationID].cost > plr->money) { // sanity check
if (Routes.find(req->iTransporationID) == Routes.end() || Routes[req->iTransporationID].cost > plr->money) { // sanity check
INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL, failResp);
failResp.iErrorCode = 0; // TODO: error code
@ -104,19 +125,39 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data)
}
TransportRoute route = Routes[req->iTransporationID];
plr->money -= route.cost;
TransportLocation target = Locations[route.end];
switch (route.type)
{
TransportLocation target;
PlayerView& plrv = PlayerManager::players[sock];
switch (route.type) {
case 1: // S.C.A.M.P.E.R.
target = Locations[route.end];
plr->x = target.x;
plr->y = target.y;
plr->z = target.z;
/*
* Not strictly necessary since there isn't a valid SCAMPER that puts you in the
* same map tile you were already in, but we might as well force an NPC reload.
*/
PlayerManager::removePlayerFromChunks(plrv.currentChunks, sock);
plrv.currentChunks.clear();
plrv.chunkPos = std::make_pair<int, int>(0, 0);
break;
case 2: // Monkey Skyway
// TODO: implement
if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) { // check if route exists
SkywayQueues[sock] = SkywayPaths[route.mssRouteNum]; // set socket point queue to route
break;
}
// refund and send alert packet
plr->money += route.cost;
INITSTRUCT(sP_FE2CL_ANNOUNCE_MSG, alert);
alert.iAnnounceType = 0; // don't think this lets us make a confirm dialog
alert.iDuringTime = 3;
U8toU16("Skyway route " + std::to_string(route.mssRouteNum) + " isn't pathed yet. You will not be charged any taros.", (char16_t*)alert.szAnnounceMsg);
sock->sendPacket((void*)&alert, P_FE2CL_ANNOUNCE_MSG, sizeof(sP_FE2CL_ANNOUNCE_MSG));
std::cout << "[WARN] MSS route " << route.mssRouteNum << " not pathed" << std::endl;
break;
default:
std::cout << "[WARN] Unknown tranportation type " << route.type << std::endl;
@ -124,23 +165,66 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data)
}
INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, resp);
// response parameters
resp.eTT = route.type;
resp.iCandy = plr->money;
resp.iX = plr->x;
resp.iY = plr->y;
resp.iZ = plr->z;
/*
* Not strictly necessary since there isn't a valid SCAMPER that puts you in the
* same map tile you were already in, but we might as well force an NPC reload.
*/
PlayerView& plrv = PlayerManager::players[sock];
PlayerManager::removePlayerFromChunks(plrv.currentChunks, sock);
plrv.currentChunks.clear();
plrv.chunkPos = std::make_pair<int, int>(0, 0);
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC));
}
/*
* Go through every socket that has broomstick points queued up, and advance to the next point.
* If the player has disconnected or finished the route, clean up and remove them from the queue.
*/
void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) {
//std::cout << SkywayQueue.size();
// using an unordered map so we can remove finished players in one iteration
std::unordered_map<CNSocket*, std::queue<WarpLocation>>::iterator it = SkywayQueues.begin();
while (it != SkywayQueues.end()) {
std::queue<WarpLocation>* queue = &it->second;
Player* plr = nullptr;
if(PlayerManager::players.find(it->first) != PlayerManager::players.end()) // check if socket still has a player
plr = PlayerManager::getPlayer(it->first);
if (plr == nullptr) {
// pluck out dead socket + update iterator
it = SkywayQueues.erase(it);
continue;
}
if (queue->empty()) {
// send dismount packet
INITSTRUCT(sP_FE2CL_REP_PC_RIDING_SUCC, rideSucc);
INITSTRUCT(sP_FE2CL_PC_RIDING, rideBroadcast);
rideSucc.iPC_ID = plr->iID;
rideSucc.eRT = 0;
rideBroadcast.iPC_ID = plr->iID;
rideBroadcast.eRT = 0;
it->first->sendPacket((void*)&rideSucc, P_FE2CL_REP_PC_RIDING_SUCC, sizeof(sP_FE2CL_REP_PC_RIDING_SUCC));
// send packet to players in view
PlayerManager::sendToViewable(it->first, (void*)&rideBroadcast, P_FE2CL_PC_RIDING, sizeof(sP_FE2CL_PC_RIDING));
it = SkywayQueues.erase(it); // remove player from tracking map + update iterator
} else {
WarpLocation point = queue->front(); // get point
queue->pop(); // remove point from front of queue
INITSTRUCT(sP_FE2CL_PC_BROOMSTICK_MOVE, bmstk);
bmstk.iPC_ID = plr->iID;
bmstk.iToX = point.x;
bmstk.iToY = point.y;
bmstk.iToZ = point.z;
it->first->sendPacket((void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE));
// set player location to point to update viewables
PlayerManager::updatePlayerPosition(it->first, point.x, point.y, point.z);
// send packet to players in view
PlayerManager::sendToViewable(it->first, (void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE));
it++; // go to next entry in map
}
}
}

View File

@ -1,6 +1,11 @@
#pragma once
#include "CNShardServer.hpp"
#include "NPCManager.hpp"
#include <unordered_map>
struct WarpLocation;
struct TransportRoute {
int type, start, end, cost, mssSpeed, mssRouteNum;
@ -13,9 +18,13 @@ struct TransportLocation {
namespace TransportManager {
extern std::map<int32_t, TransportRoute> Routes;
extern std::map<int32_t, TransportLocation> Locations;
extern std::map<int32_t, std::queue<WarpLocation>> SkywayPaths; // predefined skyway paths with points
extern std::unordered_map<CNSocket*, std::queue<WarpLocation>> SkywayQueues; // player sockets with queued broomstick points
void init();
void transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data);
void transportWarpHandler(CNSocket* sock, CNPacketData* data);
void tickSkywaySystem(CNServer* serv, time_t currTime);
}

View File

@ -22,6 +22,7 @@ int settings::SPAWN_ANGLE = 130;
std::string settings::NPCJSON = "tdata/NPCs.json";
std::string settings::XDTJSON = "tdata/xdt.json";
std::string settings::MOBJSON = "tdata/mobs.json";
std::string settings::PATHJSON = "tdata/paths.json";
std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
bool settings::GM = true;
@ -52,6 +53,7 @@ void settings::init() {
NPCJSON = reader.Get("shard", "npcdata", NPCJSON);
XDTJSON = reader.Get("shard", "xdtdata", XDTJSON);
MOBJSON = reader.Get("shard", "mobdata", MOBJSON);
PATHJSON = reader.Get("shard", "pathdata", PATHJSON);
MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING);
GM = reader.GetBoolean("shard", "gm", GM);
}

View File

@ -17,6 +17,7 @@ namespace settings {
extern std::string NPCJSON;
extern std::string XDTJSON;
extern std::string MOBJSON;
extern std::string PATHJSON;
extern bool GM;
void init();

2
tdata

@ -1 +1 @@
Subproject commit 19d9902331c94174785323d39daccc2cd23c5872
Subproject commit f9c5f6a245c36add9fd8663badeca96e19a5d7ed