From a5c40b66f5050ab51f198161ca63618615ef6108 Mon Sep 17 00:00:00 2001 From: Gent Date: Wed, 16 Sep 2020 22:27:21 -0400 Subject: [PATCH 1/6] Add basic Monkey Skyway functionality --- src/TableData.cpp | 22 +++++++++ src/TransportManager.cpp | 98 ++++++++++++++++++++++++++++++++++++---- src/TransportManager.hpp | 11 +++++ src/settings.cpp | 2 + src/settings.hpp | 1 + tdata | 2 +- 6 files changed, 126 insertions(+), 10 deletions(-) diff --git a/src/TableData.cpp b/src/TableData.cpp index d018692..6c9f34c 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -42,6 +42,28 @@ void TableData::init() { std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl; } + // load paths + try { + std::ifstream inFile(settings::PATHJSON); + nlohmann::json pathData; + + // read file into json + inFile >> pathData; + + nlohmann::json pathDataSkyway = pathData["skyway"]; + for (nlohmann::json::iterator skywayPath = pathDataSkyway.begin(); skywayPath != pathDataSkyway.end(); skywayPath++) { + std::vector points; + nlohmann::json pathPoints = skywayPath.value()["points"]; + for (nlohmann::json::iterator point = pathPoints.begin(); point != pathPoints.end(); point++) + points.push_back({point.value()["iX"], point.value()["iY"], point.value()["iZ"] }); + TransportManager::SkywayPaths[skywayPath.value()["iRouteID"]] = points; + } + + 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; + } // load everything else from xdttable std::cout << "[INFO] Parsing xdt.json..." << std::endl; std::ifstream infile(settings::XDTJSON); diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 07a16f4..77efe46 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -3,11 +3,16 @@ #include "PlayerManager.hpp" #include "TransportManager.hpp" +#include std::map TransportManager::Routes; std::map TransportManager::Locations; +std::map> TransportManager::SkywayPaths; +std::unordered_map> TransportManager::SkywayQueue; 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); } @@ -34,7 +39,7 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket } // update registration bitfield using bit shifting + bitwise or - plr->iWarpLocationFlag |= (1UL << (transport->iLocationID - 1)); + plr->iWarpLocationFlag |= plr->IsGM ? INT32_MAX : (1UL << (transport->iLocationID - 1)); } 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; @@ -52,7 +57,13 @@ 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. */ - plr->aSkywayLocationFlag[transport->iLocationID > 63 ? 1 : 0] |= (1ULL << (transport->iLocationID > 63 ? transport->iLocationID - 65 : transport->iLocationID - 1)); + if (plr->IsGM) { + plr->aSkywayLocationFlag[0] = INT64_MAX; + plr->aSkywayLocationFlag[1] = INT64_MAX; + } + else { + plr->aSkywayLocationFlag[transport->iLocationID > 63 ? 1 : 0] |= (1ULL << (transport->iLocationID > 63 ? transport->iLocationID - 65 : transport->iLocationID - 1)); + } } else { std::cout << "[WARN] Unknown mode of transport; eTT = " << transport->eTT << std::endl; INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL, failResp); @@ -92,8 +103,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 +114,30 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) } TransportRoute route = Routes[req->iTransporationID]; - plr->money -= route.cost; - TransportLocation target = Locations[route.end]; + TransportLocation target; + PlayerView& plrv = PlayerManager::players[sock]; + std::vector* points = nullptr; 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. + */ + plrv.viewable.clear(); + plrv.viewableNPCs.clear(); break; case 2: // Monkey Skyway - // TODO: implement + if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) // sanity check + points = &SkywayPaths[route.mssRouteNum]; + else + std::cout << "[WARN] MSS route " << route.mssRouteNum << " not pathed" << std::endl; break; default: std::cout << "[WARN] Unknown tranportation type " << route.type << std::endl; @@ -124,13 +145,13 @@ 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; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC)); /* * Not strictly necessary since there isn't a valid SCAMPER that puts you in the @@ -141,6 +162,65 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) PlayerManager::removePlayerFromChunks(plrv.currentChunks, sock); plrv.currentChunks.clear(); plrv.chunkPos = std::make_pair(0, 0); + if (points == nullptr) // no skyway path set + return; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC)); + // Interpolate + std::queue queue; + WarpLocation last = { plr->x, plr->y, plr->z }; // start pos + for (std::vector::iterator point = points->begin(); point != points->end(); point++) { + // avoiding pow here + int distanceBetween = sqrt((last.x - point->x) * (last.x - point->x) + (last.y - point->y) * (last.y - point->y) + (last.z - point->z) * (last.z - point->z)); + int lerps = distanceBetween / (int)LERP_GAP; // integer division to ensure a whole number + for (int i = 0; i < lerps; i++) { + WarpLocation lerp; + float frac = 1.0f / (lerps + 1); + lerp.x = (last.x * (1.0f - frac)) + (point->x * frac); + lerp.y = (last.y * (1.0f - frac)) + (point->y * frac); + lerp.z = (last.z * (1.0f - frac)) + (point->z * frac); + queue.push(lerp); // add lerp'd point to the queue + } + queue.push(*point); + last = { point->x, point->y, point->z }; // update start pos + } + SkywayQueue[sock] = queue; +} + +void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { + + std::cout << SkywayQueue.size(); + // using an unordered list so we can remove finished players in one iteration + std::unordered_map>::iterator it = SkywayQueue.begin(); + while (it != SkywayQueue.end()) { + + std::queue* queue = &it->second; + Player* plr = PlayerManager::getPlayer(it->first); + if (plr == nullptr) { + // sanity check + it = SkywayQueue.erase(it); // remove player from tracking map + update iterator + } + else if (queue->empty()) { + // send dismount packet + INITSTRUCT(sP_FE2CL_REP_PC_RIDING_SUCC, rideSucc); + rideSucc.iPC_ID = plr == nullptr ? 0 : plr->iID; + rideSucc.eRT = 0; + it->first->sendPacket((void*)&rideSucc, P_FE2CL_REP_PC_RIDING_SUCC, sizeof(sP_FE2CL_REP_PC_RIDING_SUCC)); + // TODO: send packet to nearby players? + it = SkywayQueue.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 == nullptr ? 0 : 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)); + // TODO: send packet to nearby players? + + it++; // go to next entry in map + } + } } diff --git a/src/TransportManager.hpp b/src/TransportManager.hpp index 7ad34b0..aad44e7 100644 --- a/src/TransportManager.hpp +++ b/src/TransportManager.hpp @@ -1,6 +1,13 @@ #pragma once #include "CNShardServer.hpp" +#include "NPCManager.hpp" + +#include + +#define LERP_GAP 5000 + +struct WarpLocation; struct TransportRoute { int type, start, end, cost, mssSpeed, mssRouteNum; @@ -13,9 +20,13 @@ struct TransportLocation { namespace TransportManager { extern std::map Routes; extern std::map Locations; + extern std::map> SkywayPaths; + extern std::unordered_map> SkywayQueue; void init(); void transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data); void transportWarpHandler(CNSocket* sock, CNPacketData* data); + + void tickSkywaySystem(CNServer* serv, time_t currTime); } diff --git a/src/settings.cpp b/src/settings.cpp index e7a83ce..2219643 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -23,6 +23,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; @@ -54,6 +55,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); } diff --git a/src/settings.hpp b/src/settings.hpp index 6700ab6..bba3288 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -18,6 +18,7 @@ namespace settings { extern std::string NPCJSON; extern std::string XDTJSON; extern std::string MOBJSON; + extern std::string PATHJSON; extern bool GM; void init(); diff --git a/tdata b/tdata index 19d9902..f9c5f6a 160000 --- a/tdata +++ b/tdata @@ -1 +1 @@ -Subproject commit 19d9902331c94174785323d39daccc2cd23c5872 +Subproject commit f9c5f6a245c36add9fd8663badeca96e19a5d7ed From 6a78a301c92d8031fddb73626ff7203f08565cc0 Mon Sep 17 00:00:00 2001 From: Gent Date: Wed, 16 Sep 2020 22:40:20 -0400 Subject: [PATCH 2/6] Skyway lerp tuning --- src/TransportManager.cpp | 4 ++-- src/TransportManager.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 77efe46..31db81d 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -174,7 +174,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) int lerps = distanceBetween / (int)LERP_GAP; // integer division to ensure a whole number for (int i = 0; i < lerps; i++) { WarpLocation lerp; - float frac = 1.0f / (lerps + 1); + float frac = (i + 1) * 1.0f / (lerps + 1); lerp.x = (last.x * (1.0f - frac)) + (point->x * frac); lerp.y = (last.y * (1.0f - frac)) + (point->y * frac); lerp.z = (last.z * (1.0f - frac)) + (point->z * frac); @@ -188,7 +188,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { - std::cout << SkywayQueue.size(); + //std::cout << SkywayQueue.size(); // using an unordered list so we can remove finished players in one iteration std::unordered_map>::iterator it = SkywayQueue.begin(); while (it != SkywayQueue.end()) { diff --git a/src/TransportManager.hpp b/src/TransportManager.hpp index aad44e7..ff30477 100644 --- a/src/TransportManager.hpp +++ b/src/TransportManager.hpp @@ -5,7 +5,7 @@ #include -#define LERP_GAP 5000 +#define LERP_GAP 3000 struct WarpLocation; From a5ffe26c4493e61c0da29798525939ebe2e2f8f2 Mon Sep 17 00:00:00 2001 From: Gent Date: Thu, 17 Sep 2020 05:04:00 -0400 Subject: [PATCH 3/6] Move lerp to td init, add variable gap size --- src/TableData.cpp | 32 +++++++++++++++++++++++++++----- src/TableData.hpp | 3 +++ src/TransportManager.cpp | 32 ++++---------------------------- src/TransportManager.hpp | 4 +--- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/TableData.cpp b/src/TableData.cpp index 6c9f34c..30b43df 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -52,11 +52,7 @@ void TableData::init() { nlohmann::json pathDataSkyway = pathData["skyway"]; for (nlohmann::json::iterator skywayPath = pathDataSkyway.begin(); skywayPath != pathDataSkyway.end(); skywayPath++) { - std::vector points; - nlohmann::json pathPoints = skywayPath.value()["points"]; - for (nlohmann::json::iterator point = pathPoints.begin(); point != pathPoints.end(); point++) - points.push_back({point.value()["iX"], point.value()["iY"], point.value()["iZ"] }); - TransportManager::SkywayPaths[skywayPath.value()["iRouteID"]] = points; + constructPath(skywayPath); } std::cout << "[INFO] Loaded " << TransportManager::SkywayPaths.size() << " skyway paths" << std::endl; @@ -235,3 +231,29 @@ int TableData::getItemType(int itemSet) { } return overriden; } + +void TableData::constructPath(nlohmann::json::iterator pathData) { + // Interpolate + nlohmann::json pathPoints = pathData.value()["points"]; + std::queue points; + nlohmann::json::iterator point = pathPoints.begin(); + WarpLocation last = { point.value()["iX"] , point.value()["iY"] , point.value()["iZ"] }; // start pos + // use some for loop trickery + for (point++; point != pathPoints.end(); point++) { + WarpLocation coords = { point.value()["iX"] , point.value()["iY"] , point.value()["iZ"] }; + // avoiding pow here + int distanceBetween = sqrt((last.x - coords.x) * (last.x - coords.x) + (last.y - coords.y) * (last.y - coords.y) + (last.z - coords.z) * (last.z - coords.z)); + int lerps = distanceBetween / (int)pathData.value()["iMaxGapSize"]; // integer division to ensure a whole number + for (int i = 0; i < lerps; i++) { + WarpLocation lerp; + 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); + last = coords; // update start pos + } + TransportManager::SkywayPaths[pathData.value()["iRouteID"]] = points; +} diff --git a/src/TableData.hpp b/src/TableData.hpp index bf89c68..065612b 100644 --- a/src/TableData.hpp +++ b/src/TableData.hpp @@ -1,9 +1,12 @@ #pragma once #include +#include "contrib/JSON.hpp" + namespace TableData { void init(); void cleanup(); int getItemType(int); + void constructPath(nlohmann::json::iterator); } diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 31db81d..16fb6d8 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -7,7 +7,7 @@ std::map TransportManager::Routes; std::map TransportManager::Locations; -std::map> TransportManager::SkywayPaths; +std::map> TransportManager::SkywayPaths; std::unordered_map> TransportManager::SkywayQueue; void TransportManager::init() { @@ -118,7 +118,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) TransportLocation target; PlayerView& plrv = PlayerManager::players[sock]; - std::vector* points = nullptr; + std::queue* points = nullptr; switch (route.type) { case 1: // S.C.A.M.P.E.R. @@ -153,37 +153,13 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) resp.iZ = plr->z; sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC)); - /* - * 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(0, 0); - if (points == nullptr) // no skyway path set - return; - - // Interpolate - std::queue queue; - WarpLocation last = { plr->x, plr->y, plr->z }; // start pos - for (std::vector::iterator point = points->begin(); point != points->end(); point++) { - // avoiding pow here - int distanceBetween = sqrt((last.x - point->x) * (last.x - point->x) + (last.y - point->y) * (last.y - point->y) + (last.z - point->z) * (last.z - point->z)); - int lerps = distanceBetween / (int)LERP_GAP; // integer division to ensure a whole number - for (int i = 0; i < lerps; i++) { - WarpLocation lerp; - float frac = (i + 1) * 1.0f / (lerps + 1); - lerp.x = (last.x * (1.0f - frac)) + (point->x * frac); - lerp.y = (last.y * (1.0f - frac)) + (point->y * frac); - lerp.z = (last.z * (1.0f - frac)) + (point->z * frac); - queue.push(lerp); // add lerp'd point to the queue - } - queue.push(*point); - last = { point->x, point->y, point->z }; // update start pos - } - SkywayQueue[sock] = queue; + if (points != nullptr) // check if skyway path set + SkywayQueue[sock] = *points; } void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { diff --git a/src/TransportManager.hpp b/src/TransportManager.hpp index ff30477..7ca3a25 100644 --- a/src/TransportManager.hpp +++ b/src/TransportManager.hpp @@ -5,8 +5,6 @@ #include -#define LERP_GAP 3000 - struct WarpLocation; struct TransportRoute { @@ -20,7 +18,7 @@ struct TransportLocation { namespace TransportManager { extern std::map Routes; extern std::map Locations; - extern std::map> SkywayPaths; + extern std::map> SkywayPaths; extern std::unordered_map> SkywayQueue; void init(); From cb984c029b9bc1003f4cfef4cea4a425786e53e0 Mon Sep 17 00:00:00 2001 From: Gent Date: Thu, 17 Sep 2020 05:18:50 -0400 Subject: [PATCH 4/6] Add paths JSON file --- config.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.ini b/config.ini index de43a05..37cbc34 100644 --- a/config.ini +++ b/config.ini @@ -33,6 +33,8 @@ npcdata=tdata/NPCs.json xdtdata=tdata/xdt.json # mob json mobdata=tdata/mobs.json +# path json +mobdata=tdata/paths.json # is everyone a GM? gm=true From 135424b8554ebbe3d10ba855d2a4d467d55e5b32 Mon Sep 17 00:00:00 2001 From: Gent Date: Thu, 17 Sep 2020 09:22:06 -0400 Subject: [PATCH 5/6] Update player visibility on skyway --- src/TransportManager.cpp | 47 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 16fb6d8..c54d7a5 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -24,7 +24,7 @@ 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; + //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; @@ -130,12 +130,13 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) * 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. */ - plrv.viewable.clear(); - plrv.viewableNPCs.clear(); + PlayerManager::removePlayerFromChunks(plrv.currentChunks, sock); + plrv.currentChunks.clear(); + plrv.chunkPos = std::make_pair(0, 0); break; case 2: // Monkey Skyway if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) // sanity check - points = &SkywayPaths[route.mssRouteNum]; + SkywayQueue[sock] = SkywayPaths[route.mssRouteNum]; else std::cout << "[WARN] MSS route " << route.mssRouteNum << " not pathed" << std::endl; break; @@ -152,14 +153,6 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) resp.iY = plr->y; resp.iZ = plr->z; sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC)); - - PlayerView& plrv = PlayerManager::players[sock]; - - PlayerManager::removePlayerFromChunks(plrv.currentChunks, sock); - plrv.currentChunks.clear(); - plrv.chunkPos = std::make_pair(0, 0); - if (points != nullptr) // check if skyway path set - SkywayQueue[sock] = *points; } void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { @@ -170,18 +163,26 @@ void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { while (it != SkywayQueue.end()) { std::queue* queue = &it->second; - Player* plr = PlayerManager::getPlayer(it->first); - if (plr == nullptr) { - // sanity check - it = SkywayQueue.erase(it); // remove player from tracking map + update iterator + PlayerView& plr = PlayerManager::players[it->first]; + + if (plr.plr == nullptr) { + // pluck out dead queue + update iterator + it = SkywayQueue.erase(it); + continue; } - else if (queue->empty()) { + + if (queue->empty()) { // send dismount packet INITSTRUCT(sP_FE2CL_REP_PC_RIDING_SUCC, rideSucc); - rideSucc.iPC_ID = plr == nullptr ? 0 : plr->iID; + INITSTRUCT(sP_FE2CL_PC_RIDING, rideBroadcast); + rideSucc.iPC_ID = plr.plr->iID; rideSucc.eRT = 0; + rideBroadcast.iPC_ID = plr.plr->iID; + rideBroadcast.eRT = 0; it->first->sendPacket((void*)&rideSucc, P_FE2CL_REP_PC_RIDING_SUCC, sizeof(sP_FE2CL_REP_PC_RIDING_SUCC)); - // TODO: send packet to nearby players? + // send packet to players in view (the client does NOT like this for some reason) + for (CNSocket* otherSock : plr.viewable) + otherSock->sendPacket((void*)&rideBroadcast, P_FE2CL_PC_RIDING, sizeof(sP_FE2CL_PC_RIDING)); it = SkywayQueue.erase(it); // remove player from tracking map + update iterator } else { @@ -189,12 +190,16 @@ void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { queue->pop(); // remove point from front of queue INITSTRUCT(sP_FE2CL_PC_BROOMSTICK_MOVE, bmstk); - bmstk.iPC_ID = plr == nullptr ? 0 : plr->iID; + bmstk.iPC_ID = plr.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)); - // TODO: send packet to nearby players? + // set player location to point to get better view + PlayerManager::updatePlayerPosition(it->first, point.x, point.y, point.z); + // send packet to players in view + for(CNSocket* otherSock : plr.viewable) + otherSock->sendPacket((void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE)); it++; // go to next entry in map } From 2c8243e1363d2c8a7b6325894223b253e367974e Mon Sep 17 00:00:00 2001 From: Gent Date: Mon, 21 Sep 2020 21:03:48 -0400 Subject: [PATCH 6/6] General MSS fixes + tweaks Add alert message for unpathed skyway routes Fix overflow during lerp + add Future routes Add documentation for MSS Fix potential MSS registration bug Minor tweaks + styling Update packet broadcast --- config.ini | 2 +- src/TableData.cpp | 74 ++++++++++++++++++------------ src/TableData.hpp | 1 + src/TransportManager.cpp | 99 +++++++++++++++++++++++++--------------- src/TransportManager.hpp | 4 +- 5 files changed, 110 insertions(+), 70 deletions(-) diff --git a/config.ini b/config.ini index 37cbc34..301e42c 100644 --- a/config.ini +++ b/config.ini @@ -34,7 +34,7 @@ xdtdata=tdata/xdt.json # mob json mobdata=tdata/mobs.json # path json -mobdata=tdata/paths.json +pathdata=tdata/paths.json # is everyone a GM? gm=true diff --git a/src/TableData.cpp b/src/TableData.cpp index 30b43df..9243eec 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -42,24 +42,8 @@ void TableData::init() { std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl; } - // load paths - try { - std::ifstream inFile(settings::PATHJSON); - nlohmann::json pathData; + loadPaths(); // load paths - // read file into json - inFile >> pathData; - - 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; - } // load everything else from xdttable std::cout << "[INFO] Parsing xdt.json..." << std::endl; std::ifstream infile(settings::XDTJSON); @@ -232,28 +216,60 @@ int TableData::getItemType(int itemSet) { return overriden; } -void TableData::constructPath(nlohmann::json::iterator pathData) { +/* + * 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.value()["points"]; + nlohmann::json pathPoints = pathData["points"]; std::queue points; - nlohmann::json::iterator point = pathPoints.begin(); - WarpLocation last = { point.value()["iX"] , point.value()["iY"] , point.value()["iZ"] }; // start pos - // use some for loop trickery - for (point++; point != pathPoints.end(); point++) { - WarpLocation coords = { point.value()["iX"] , point.value()["iY"] , point.value()["iZ"] }; - // avoiding pow here - int distanceBetween = sqrt((last.x - coords.x) * (last.x - coords.x) + (last.y - coords.y) * (last.y - coords.y) + (last.z - coords.z) * (last.z - coords.z)); - int lerps = distanceBetween / (int)pathData.value()["iMaxGapSize"]; // integer division to ensure a whole number + 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); + points.push(coords); // add keyframe to the queue last = coords; // update start pos } - TransportManager::SkywayPaths[pathData.value()["iRouteID"]] = points; + TransportManager::SkywayPaths[pathData["iRouteID"]] = points; } diff --git a/src/TableData.hpp b/src/TableData.hpp index 065612b..640e275 100644 --- a/src/TableData.hpp +++ b/src/TableData.hpp @@ -8,5 +8,6 @@ namespace TableData { void cleanup(); int getItemType(int); + void loadPaths(); void constructPath(nlohmann::json::iterator); } diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index c54d7a5..cced5a8 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -8,7 +8,7 @@ std::map TransportManager::Routes; std::map TransportManager::Locations; std::map> TransportManager::SkywayPaths; -std::unordered_map> TransportManager::SkywayQueue; +std::unordered_map> TransportManager::SkywayQueues; void TransportManager::init() { REGISTER_SHARD_TIMER(tickSkywaySystem, 1000); @@ -24,6 +24,7 @@ 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); + 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 @@ -38,8 +39,12 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket return; } - // update registration bitfield using bit shifting + bitwise or - plr->iWarpLocationFlag |= plr->IsGM ? INT32_MAX : (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; @@ -54,15 +59,19 @@ 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 */ if (plr->IsGM) { - plr->aSkywayLocationFlag[0] = INT64_MAX; - plr->aSkywayLocationFlag[1] = INT64_MAX; - } - else { - plr->aSkywayLocationFlag[transport->iLocationID > 63 ? 1 : 0] |= (1ULL << (transport->iLocationID > 63 ? transport->iLocationID - 65 : transport->iLocationID - 1)); + 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; @@ -76,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; @@ -118,9 +129,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) TransportLocation target; PlayerView& plrv = PlayerManager::players[sock]; - std::queue* points = nullptr; - switch (route.type) - { + switch (route.type) { case 1: // S.C.A.M.P.E.R. target = Locations[route.end]; plr->x = target.x; @@ -135,10 +144,20 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) plrv.chunkPos = std::make_pair(0, 0); break; case 2: // Monkey Skyway - if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) // sanity check - SkywayQueue[sock] = SkywayPaths[route.mssRouteNum]; - else - std::cout << "[WARN] MSS route " << route.mssRouteNum << " not pathed" << std::endl; + 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; @@ -155,19 +174,26 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) 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 list so we can remove finished players in one iteration - std::unordered_map>::iterator it = SkywayQueue.begin(); - while (it != SkywayQueue.end()) { + // using an unordered map so we can remove finished players in one iteration + std::unordered_map>::iterator it = SkywayQueues.begin(); + while (it != SkywayQueues.end()) { std::queue* queue = &it->second; - PlayerView& plr = PlayerManager::players[it->first]; - - if (plr.plr == nullptr) { - // pluck out dead queue + update iterator - it = SkywayQueue.erase(it); + + 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; } @@ -175,31 +201,28 @@ void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) { // send dismount packet INITSTRUCT(sP_FE2CL_REP_PC_RIDING_SUCC, rideSucc); INITSTRUCT(sP_FE2CL_PC_RIDING, rideBroadcast); - rideSucc.iPC_ID = plr.plr->iID; + rideSucc.iPC_ID = plr->iID; rideSucc.eRT = 0; - rideBroadcast.iPC_ID = plr.plr->iID; + 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 (the client does NOT like this for some reason) - for (CNSocket* otherSock : plr.viewable) - otherSock->sendPacket((void*)&rideBroadcast, P_FE2CL_PC_RIDING, sizeof(sP_FE2CL_PC_RIDING)); - it = SkywayQueue.erase(it); // remove player from tracking map + update iterator - } - else { + // 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.plr->iID; + 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 get better view + // set player location to point to update viewables PlayerManager::updatePlayerPosition(it->first, point.x, point.y, point.z); // send packet to players in view - for(CNSocket* otherSock : plr.viewable) - otherSock->sendPacket((void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE)); + PlayerManager::sendToViewable(it->first, (void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE)); it++; // go to next entry in map } diff --git a/src/TransportManager.hpp b/src/TransportManager.hpp index 7ca3a25..0f0f67d 100644 --- a/src/TransportManager.hpp +++ b/src/TransportManager.hpp @@ -18,8 +18,8 @@ struct TransportLocation { namespace TransportManager { extern std::map Routes; extern std::map Locations; - extern std::map> SkywayPaths; - extern std::unordered_map> SkywayQueue; + extern std::map> SkywayPaths; // predefined skyway paths with points + extern std::unordered_map> SkywayQueues; // player sockets with queued broomstick points void init();