mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-05 06:50:04 +00:00
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
This commit is contained in:
parent
135424b855
commit
2c8243e136
@ -34,7 +34,7 @@ xdtdata=tdata/xdt.json
|
|||||||
# mob json
|
# mob json
|
||||||
mobdata=tdata/mobs.json
|
mobdata=tdata/mobs.json
|
||||||
# path json
|
# path json
|
||||||
mobdata=tdata/paths.json
|
pathdata=tdata/paths.json
|
||||||
# is everyone a GM?
|
# is everyone a GM?
|
||||||
gm=true
|
gm=true
|
||||||
|
|
||||||
|
@ -42,24 +42,8 @@ void TableData::init() {
|
|||||||
std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
|
std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// load paths
|
loadPaths(); // 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++) {
|
|
||||||
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
|
// load everything else from xdttable
|
||||||
std::cout << "[INFO] Parsing xdt.json..." << std::endl;
|
std::cout << "[INFO] Parsing xdt.json..." << std::endl;
|
||||||
std::ifstream infile(settings::XDTJSON);
|
std::ifstream infile(settings::XDTJSON);
|
||||||
@ -232,28 +216,60 @@ int TableData::getItemType(int itemSet) {
|
|||||||
return overriden;
|
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
|
// Interpolate
|
||||||
nlohmann::json pathPoints = pathData.value()["points"];
|
nlohmann::json pathPoints = pathData["points"];
|
||||||
std::queue<WarpLocation> points;
|
std::queue<WarpLocation> points;
|
||||||
nlohmann::json::iterator point = pathPoints.begin();
|
nlohmann::json::iterator _point = pathPoints.begin();
|
||||||
WarpLocation last = { point.value()["iX"] , point.value()["iY"] , point.value()["iZ"] }; // start pos
|
auto point = _point.value();
|
||||||
// use some for loop trickery
|
WarpLocation last = { point["iX"] , point["iY"] , point["iZ"] }; // start pos
|
||||||
for (point++; point != pathPoints.end(); point++) {
|
// use some for loop trickery; start position should not be a point
|
||||||
WarpLocation coords = { point.value()["iX"] , point.value()["iY"] , point.value()["iZ"] };
|
for (_point++; _point != pathPoints.end(); _point++) {
|
||||||
// avoiding pow here
|
point = _point.value();
|
||||||
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));
|
WarpLocation coords = { point["iX"] , point["iY"] , point["iZ"] };
|
||||||
int lerps = distanceBetween / (int)pathData.value()["iMaxGapSize"]; // integer division to ensure a whole number
|
// 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++) {
|
for (int i = 0; i < lerps; i++) {
|
||||||
WarpLocation lerp;
|
WarpLocation lerp;
|
||||||
|
// lerp math
|
||||||
float frac = (i + 1) * 1.0f / (lerps + 1);
|
float frac = (i + 1) * 1.0f / (lerps + 1);
|
||||||
lerp.x = (last.x * (1.0f - frac)) + (coords.x * frac);
|
lerp.x = (last.x * (1.0f - frac)) + (coords.x * frac);
|
||||||
lerp.y = (last.y * (1.0f - frac)) + (coords.y * frac);
|
lerp.y = (last.y * (1.0f - frac)) + (coords.y * frac);
|
||||||
lerp.z = (last.z * (1.0f - frac)) + (coords.z * frac);
|
lerp.z = (last.z * (1.0f - frac)) + (coords.z * frac);
|
||||||
points.push(lerp); // add lerp'd point to the queue
|
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
|
last = coords; // update start pos
|
||||||
}
|
}
|
||||||
TransportManager::SkywayPaths[pathData.value()["iRouteID"]] = points;
|
TransportManager::SkywayPaths[pathData["iRouteID"]] = points;
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,6 @@ namespace TableData {
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
int getItemType(int);
|
int getItemType(int);
|
||||||
|
void loadPaths();
|
||||||
void constructPath(nlohmann::json::iterator);
|
void constructPath(nlohmann::json::iterator);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
std::map<int32_t, TransportRoute> TransportManager::Routes;
|
std::map<int32_t, TransportRoute> TransportManager::Routes;
|
||||||
std::map<int32_t, TransportLocation> TransportManager::Locations;
|
std::map<int32_t, TransportLocation> TransportManager::Locations;
|
||||||
std::map<int32_t, std::queue<WarpLocation>> TransportManager::SkywayPaths;
|
std::map<int32_t, std::queue<WarpLocation>> TransportManager::SkywayPaths;
|
||||||
std::unordered_map<CNSocket*, std::queue<WarpLocation>> TransportManager::SkywayQueue;
|
std::unordered_map<CNSocket*, std::queue<WarpLocation>> TransportManager::SkywayQueues;
|
||||||
|
|
||||||
void TransportManager::init() {
|
void TransportManager::init() {
|
||||||
REGISTER_SHARD_TIMER(tickSkywaySystem, 1000);
|
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;
|
sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION* transport = (sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION*)data->buf;
|
||||||
Player* plr = PlayerManager::getPlayer(sock);
|
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;
|
//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->eTT == 1) { // S.C.A.M.P.E.R.
|
||||||
if (transport->iLocationID < 1 || transport->iLocationID > 31) { // sanity check
|
if (transport->iLocationID < 1 || transport->iLocationID > 31) { // sanity check
|
||||||
@ -38,8 +39,12 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update registration bitfield using bit shifting + bitwise or
|
// update registration bitfield using bitmask
|
||||||
plr->iWarpLocationFlag |= plr->IsGM ? INT32_MAX : (1UL << (transport->iLocationID - 1));
|
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
|
} else if (transport->eTT == 2) { // Monkey Skyway System
|
||||||
if (transport->iLocationID < 1 || transport->iLocationID > 127) { // sanity check
|
if (transport->iLocationID < 1 || transport->iLocationID > 127) { // sanity check
|
||||||
std::cout << "[WARN] Skyway location ID " << transport->iLocationID << " is out of bounds" << std::endl;
|
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
|
* assuming the two bitfields are just stuck together to make a longer one, do a similar operation
|
||||||
* this approach seems to work with initial testing, but we have yet to see a monkey ID greater than 63.
|
|
||||||
*/
|
*/
|
||||||
if (plr->IsGM) {
|
if (plr->IsGM) {
|
||||||
plr->aSkywayLocationFlag[0] = INT64_MAX;
|
plr->aSkywayLocationFlag[0] = UINT64_MAX;
|
||||||
plr->aSkywayLocationFlag[1] = INT64_MAX;
|
plr->aSkywayLocationFlag[1] = UINT64_MAX;
|
||||||
}
|
newReg = true;
|
||||||
else {
|
} else {
|
||||||
plr->aSkywayLocationFlag[transport->iLocationID > 63 ? 1 : 0] |= (1ULL << (transport->iLocationID > 63 ? transport->iLocationID - 65 : transport->iLocationID - 1));
|
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 {
|
} else {
|
||||||
std::cout << "[WARN] Unknown mode of transport; eTT = " << transport->eTT << std::endl;
|
std::cout << "[WARN] Unknown mode of transport; eTT = " << transport->eTT << std::endl;
|
||||||
@ -76,8 +85,10 @@ void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacket
|
|||||||
return;
|
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
|
// response parameters
|
||||||
resp.eTT = transport->eTT;
|
resp.eTT = transport->eTT;
|
||||||
resp.iLocationID = transport->iLocationID;
|
resp.iLocationID = transport->iLocationID;
|
||||||
@ -118,9 +129,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data)
|
|||||||
|
|
||||||
TransportLocation target;
|
TransportLocation target;
|
||||||
PlayerView& plrv = PlayerManager::players[sock];
|
PlayerView& plrv = PlayerManager::players[sock];
|
||||||
std::queue<WarpLocation>* points = nullptr;
|
switch (route.type) {
|
||||||
switch (route.type)
|
|
||||||
{
|
|
||||||
case 1: // S.C.A.M.P.E.R.
|
case 1: // S.C.A.M.P.E.R.
|
||||||
target = Locations[route.end];
|
target = Locations[route.end];
|
||||||
plr->x = target.x;
|
plr->x = target.x;
|
||||||
@ -135,10 +144,20 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data)
|
|||||||
plrv.chunkPos = std::make_pair<int, int>(0, 0);
|
plrv.chunkPos = std::make_pair<int, int>(0, 0);
|
||||||
break;
|
break;
|
||||||
case 2: // Monkey Skyway
|
case 2: // Monkey Skyway
|
||||||
if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) // sanity check
|
if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) { // check if route exists
|
||||||
SkywayQueue[sock] = SkywayPaths[route.mssRouteNum];
|
SkywayQueues[sock] = SkywayPaths[route.mssRouteNum]; // set socket point queue to route
|
||||||
else
|
break;
|
||||||
std::cout << "[WARN] MSS route " << route.mssRouteNum << " not pathed" << std::endl;
|
}
|
||||||
|
|
||||||
|
// 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;
|
break;
|
||||||
default:
|
default:
|
||||||
std::cout << "[WARN] Unknown tranportation type " << route.type << std::endl;
|
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));
|
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) {
|
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
|
// using an unordered map so we can remove finished players in one iteration
|
||||||
std::unordered_map<CNSocket*, std::queue<WarpLocation>>::iterator it = SkywayQueue.begin();
|
std::unordered_map<CNSocket*, std::queue<WarpLocation>>::iterator it = SkywayQueues.begin();
|
||||||
while (it != SkywayQueue.end()) {
|
while (it != SkywayQueues.end()) {
|
||||||
|
|
||||||
std::queue<WarpLocation>* queue = &it->second;
|
std::queue<WarpLocation>* queue = &it->second;
|
||||||
PlayerView& plr = PlayerManager::players[it->first];
|
|
||||||
|
Player* plr = nullptr;
|
||||||
if (plr.plr == nullptr) {
|
if(PlayerManager::players.find(it->first) != PlayerManager::players.end()) // check if socket still has a player
|
||||||
// pluck out dead queue + update iterator
|
plr = PlayerManager::getPlayer(it->first);
|
||||||
it = SkywayQueue.erase(it);
|
|
||||||
|
if (plr == nullptr) {
|
||||||
|
// pluck out dead socket + update iterator
|
||||||
|
it = SkywayQueues.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,31 +201,28 @@ void TransportManager::tickSkywaySystem(CNServer* serv, time_t currTime) {
|
|||||||
// send dismount packet
|
// send dismount packet
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_RIDING_SUCC, rideSucc);
|
INITSTRUCT(sP_FE2CL_REP_PC_RIDING_SUCC, rideSucc);
|
||||||
INITSTRUCT(sP_FE2CL_PC_RIDING, rideBroadcast);
|
INITSTRUCT(sP_FE2CL_PC_RIDING, rideBroadcast);
|
||||||
rideSucc.iPC_ID = plr.plr->iID;
|
rideSucc.iPC_ID = plr->iID;
|
||||||
rideSucc.eRT = 0;
|
rideSucc.eRT = 0;
|
||||||
rideBroadcast.iPC_ID = plr.plr->iID;
|
rideBroadcast.iPC_ID = plr->iID;
|
||||||
rideBroadcast.eRT = 0;
|
rideBroadcast.eRT = 0;
|
||||||
it->first->sendPacket((void*)&rideSucc, P_FE2CL_REP_PC_RIDING_SUCC, sizeof(sP_FE2CL_REP_PC_RIDING_SUCC));
|
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)
|
// send packet to players in view
|
||||||
for (CNSocket* otherSock : plr.viewable)
|
PlayerManager::sendToViewable(it->first, (void*)&rideBroadcast, P_FE2CL_PC_RIDING, sizeof(sP_FE2CL_PC_RIDING));
|
||||||
otherSock->sendPacket((void*)&rideBroadcast, P_FE2CL_PC_RIDING, sizeof(sP_FE2CL_PC_RIDING));
|
it = SkywayQueues.erase(it); // remove player from tracking map + update iterator
|
||||||
it = SkywayQueue.erase(it); // remove player from tracking map + update iterator
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
WarpLocation point = queue->front(); // get point
|
WarpLocation point = queue->front(); // get point
|
||||||
queue->pop(); // remove point from front of queue
|
queue->pop(); // remove point from front of queue
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_PC_BROOMSTICK_MOVE, bmstk);
|
INITSTRUCT(sP_FE2CL_PC_BROOMSTICK_MOVE, bmstk);
|
||||||
bmstk.iPC_ID = plr.plr->iID;
|
bmstk.iPC_ID = plr->iID;
|
||||||
bmstk.iToX = point.x;
|
bmstk.iToX = point.x;
|
||||||
bmstk.iToY = point.y;
|
bmstk.iToY = point.y;
|
||||||
bmstk.iToZ = point.z;
|
bmstk.iToZ = point.z;
|
||||||
it->first->sendPacket((void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE));
|
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);
|
PlayerManager::updatePlayerPosition(it->first, point.x, point.y, point.z);
|
||||||
// send packet to players in view
|
// send packet to players in view
|
||||||
for(CNSocket* otherSock : plr.viewable)
|
PlayerManager::sendToViewable(it->first, (void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE));
|
||||||
otherSock->sendPacket((void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE));
|
|
||||||
|
|
||||||
it++; // go to next entry in map
|
it++; // go to next entry in map
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ struct TransportLocation {
|
|||||||
namespace TransportManager {
|
namespace TransportManager {
|
||||||
extern std::map<int32_t, TransportRoute> Routes;
|
extern std::map<int32_t, TransportRoute> Routes;
|
||||||
extern std::map<int32_t, TransportLocation> Locations;
|
extern std::map<int32_t, TransportLocation> Locations;
|
||||||
extern std::map<int32_t, std::queue<WarpLocation>> SkywayPaths;
|
extern std::map<int32_t, std::queue<WarpLocation>> SkywayPaths; // predefined skyway paths with points
|
||||||
extern std::unordered_map<CNSocket*, std::queue<WarpLocation>> SkywayQueue;
|
extern std::unordered_map<CNSocket*, std::queue<WarpLocation>> SkywayQueues; // player sockets with queued broomstick points
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user