diff --git a/src/ChatManager.cpp b/src/ChatManager.cpp index c22a575..faa6407 100644 --- a/src/ChatManager.cpp +++ b/src/ChatManager.cpp @@ -232,7 +232,7 @@ void summonWCommand(std::string full, std::vector& args, CNSocket* npc->appearanceData.iAngle = (plr->angle + 180) % 360; NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z, plr->instanceID, npc->appearanceData.iAngle); // if we're in a lair, we need to spawn the NPC in both the private instance and the template if (PLAYERID(plr->instanceID) != 0) { @@ -251,7 +251,7 @@ void summonWCommand(std::string full, std::vector& args, CNSocket* npc->appearanceData.iAngle = (plr->angle + 180) % 360; NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, plr->x, plr->y, plr->z, npc->instanceID, npc->appearanceData.iAngle); } ChatManager::sendServerMessage(sock, "/summonW: placed mob with type: " + std::to_string(type) + @@ -262,7 +262,8 @@ void summonWCommand(std::string full, std::vector& args, CNSocket* void unsummonWCommand(std::string full, std::vector& args, CNSocket* sock) { Player* plr = PlayerManager::getPlayer(sock); - BaseNPC* npc = NPCManager::getNearestNPC(*plr->currentChunks, plr->x, plr->y, plr->z); + std::vector chunks; // TODO + BaseNPC* npc = NPCManager::getNearestNPC(chunks, plr->x, plr->y, plr->z); if (npc == nullptr) { ChatManager::sendServerMessage(sock, "/unsummonW: No NPCs found nearby"); @@ -318,7 +319,8 @@ void toggleAiCommand(std::string full, std::vector& args, CNSocket* void npcRotateCommand(std::string full, std::vector& args, CNSocket* sock) { Player* plr = PlayerManager::getPlayer(sock); - BaseNPC* npc = NPCManager::getNearestNPC(*plr->currentChunks, plr->x, plr->y, plr->z); + std::vector chunks; // TODO + BaseNPC* npc = NPCManager::getNearestNPC(chunks, plr->x, plr->y, plr->z); if (npc == nullptr) { ChatManager::sendServerMessage(sock, "[NPCR] No NPCs found nearby"); @@ -326,11 +328,11 @@ void npcRotateCommand(std::string full, std::vector& args, CNSocket } int angle = (plr->angle + 180) % 360; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ, angle); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ, npc->instanceID, angle); // if it's a gruntwork NPC, rotate in-place if (TableData::RunningMobs.find(npc->appearanceData.iNPC_ID) != TableData::RunningMobs.end()) { - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ, angle); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ, npc->instanceID, angle); ChatManager::sendServerMessage(sock, "[NPCR] Successfully set angle to " + std::to_string(angle) + " for gruntwork NPC " + std::to_string(npc->appearanceData.iNPC_ID)); @@ -385,7 +387,8 @@ void npcInstanceCommand(std::string full, std::vector& args, CNSock return; } - BaseNPC* npc = NPCManager::getNearestNPC(*plr->currentChunks, plr->x, plr->y, plr->z); + std::vector chunks; // TODO + BaseNPC* npc = NPCManager::getNearestNPC(chunks, plr->x, plr->y, plr->z); if (npc == nullptr) { ChatManager::sendServerMessage(sock, "[NPCI] No NPCs found nearby"); @@ -402,7 +405,7 @@ void npcInstanceCommand(std::string full, std::vector& args, CNSock ChatManager::sendServerMessage(sock, "[NPCI] Moving NPC with ID " + std::to_string(npc->appearanceData.iNPC_ID) + " to instance " + std::to_string(instance)); TableData::RunningNPCMapNumbers[npc->appearanceData.iNPC_ID] = instance; - NPCManager::updateNPCInstance(npc->appearanceData.iNPC_ID, instance); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ, instance, npc->appearanceData.iAngle); } void minfoCommand(std::string full, std::vector& args, CNSocket* sock) { @@ -492,7 +495,7 @@ void eggCommand(std::string full, std::vector& args, CNSocket* sock Egg* egg = new Egg(plr->x + addX, plr->y + addY, plr->z, plr->instanceID, eggType, id, false); // change last arg to true after gruntwork NPCManager::NPCs[id] = egg; NPCManager::Eggs[id] = egg; - NPCManager::updateNPCPosition(id, plr->x + addX, plr->y + addY, plr->z, plr->instanceID); + NPCManager::updateNPCPosition(id, plr->x + addX, plr->y + addY, plr->z, plr->instanceID, plr->angle); // add to template TableData::RunningEggs[id] = egg; diff --git a/src/ChunkManager.cpp b/src/ChunkManager.cpp index c0957b3..79ce826 100644 --- a/src/ChunkManager.cpp +++ b/src/ChunkManager.cpp @@ -9,6 +9,11 @@ std::map ChunkManager::chunks; void ChunkManager::init() {} // stubbed void ChunkManager::newChunk(ChunkPos pos) { + if (chunkExists(pos)) { + std::cout << "[WARN] Tried to create a chunk that already exists\n"; + return; + } + Chunk *chunk = new Chunk(); chunk->players = std::set(); @@ -17,158 +22,340 @@ void ChunkManager::newChunk(ChunkPos pos) { chunks[pos] = chunk; } -void ChunkManager::populateNewChunk(Chunk* chunk, ChunkPos pos) {// add the new chunk to every player and mob that's near it - for (Chunk *c : grabChunks(pos)) { - if (c == chunk) - continue; +void ChunkManager::updatePlayerChunk(CNSocket* sock, ChunkPos from, ChunkPos to) { + Player* plr = PlayerManager::getPlayer(sock); - for (CNSocket *s : c->players) - PlayerManager::getPlayer(s)->currentChunks->push_back(chunk); + // if the new chunk doesn't exist, make it first + if (!ChunkManager::chunkExists(to)) + newChunk(to); - for (int32_t id : c->NPCs) - NPCManager::NPCs[id]->currentChunks.push_back(chunk); - } + // move to other chunk's player set + untrackPlayer(from, sock); // this will delete the chunk if it's empty + trackPlayer(to, sock); + + // calculate viewable chunks from both points + std::set oldViewables = getViewableChunks(from); + std::set newViewables = getViewableChunks(to); + std::set toExit, toEnter; + + /* + * Calculate diffs. This is done to prevent phasing on chunk borders. + * toExit will contain old viewables - new viewables, so the player will only be exited in chunks that are out of sight. + * toEnter contains the opposite: new viewables - old viewables, chunks where we previously weren't visible from before. + */ + std::set_difference(oldViewables.begin(), oldViewables.end(), newViewables.begin(), newViewables.end(), + std::inserter(toExit, toExit.end())); // chunks we must be EXITed from (old - new) + std::set_difference(newViewables.begin(), newViewables.end(), oldViewables.begin(), oldViewables.end(), + std::inserter(toEnter, toEnter.end())); // chunks we must be ENTERed into (new - old) + + // update views + removePlayerFromChunks(toExit, sock); + addPlayerToChunks(toEnter, sock); } -void ChunkManager::addNPC(int posX, int posY, uint64_t instanceID, int32_t id) { - ChunkPos pos = grabChunk(posX, posY, instanceID); +void ChunkManager::updateNPCChunk(int32_t id, ChunkPos from, ChunkPos to) { + BaseNPC* npc = NPCManager::NPCs[id]; - bool newChunkUsed = false; + // if the new chunk doesn't exist, make it first + if (!ChunkManager::chunkExists(to)) + newChunk(to); - // make chunk if it doesn't exist! - if (chunks.find(pos) == chunks.end()) { - newChunk(pos); - newChunkUsed = true; - } + // move to other chunk's player set + untrackNPC(from, id); // this will delete the chunk if it's empty + trackNPC(to, id); - Chunk* chunk = chunks[pos]; + // calculate viewable chunks from both points + std::set oldViewables = getViewableChunks(from); + std::set newViewables = getViewableChunks(to); + std::set toExit, toEnter; - if (newChunkUsed) - NPCManager::NPCs[id]->currentChunks.push_back(chunk); + /* + * Calculate diffs. This is done to prevent phasing on chunk borders. + * toExit will contain old viewables - new viewables, so the player will only be exited in chunks that are out of sight. + * toEnter contains the opposite: new viewables - old viewables, chunks where we previously weren't visible from before. + */ + std::set_difference(oldViewables.begin(), oldViewables.end(), newViewables.begin(), newViewables.end(), + std::inserter(toExit, toExit.end())); // chunks we must be EXITed from (old - new) + std::set_difference(newViewables.begin(), newViewables.end(), oldViewables.begin(), oldViewables.end(), + std::inserter(toEnter, toEnter.end())); // chunks we must be ENTERed into (new - old) - chunk->NPCs.insert(id); - - // we must update other players after the NPC is added to chunk - if (newChunkUsed) - populateNewChunk(chunk, pos); + // update views + removeNPCFromChunks(toExit, id); + addNPCToChunks(toEnter, id); } -void ChunkManager::addPlayer(int posX, int posY, uint64_t instanceID, CNSocket* sock) { - ChunkPos pos = grabChunk(posX, posY, instanceID); +void ChunkManager::trackPlayer(ChunkPos chunkPos, CNSocket* sock) { + if (!chunkExists(chunkPos)) + return; // shouldn't happen - bool newChunkUsed = false; - - // make chunk if it doesn't exist! - if (chunks.find(pos) == chunks.end()) { - newChunk(pos); - newChunkUsed = true; - } - - Chunk* chunk = chunks[pos]; - - if (newChunkUsed) - PlayerManager::getPlayer(sock)->currentChunks->push_back(chunk); - - chunk->players.insert(sock); - - // we must update other players after this player is added to chunk - if (newChunkUsed) - populateNewChunk(chunk, pos); + chunks[chunkPos]->players.insert(sock); } -bool ChunkManager::removePlayer(ChunkPos chunkPos, CNSocket* sock) { - if (!checkChunk(chunkPos)) - return false; // do nothing if chunk doesn't even exist +void ChunkManager::trackNPC(ChunkPos chunkPos, int32_t id) { + if (!chunkExists(chunkPos)) + return; // shouldn't happen + + chunks[chunkPos]->NPCs.insert(id); +} + +void ChunkManager::untrackPlayer(ChunkPos chunkPos, CNSocket* sock) { + if (!chunkExists(chunkPos)) + return; // do nothing if chunk doesn't even exist Chunk* chunk = chunks[chunkPos]; chunk->players.erase(sock); // gone - // if players and NPCs are empty, free chunk and remove it from surrounding views + // if chunk is empty, free it if (chunk->NPCs.size() == 0 && chunk->players.size() == 0) { - destroyChunk(chunkPos); - - // the chunk we left was destroyed - return true; + chunks.erase(chunkPos); // remove from map + delete chunk; // free from memory } - - // the chunk we left was not destroyed - return false; } -bool ChunkManager::removeNPC(ChunkPos chunkPos, int32_t id) { - if (!checkChunk(chunkPos)) - return false; // do nothing if chunk doesn't even exist +void ChunkManager::untrackNPC(ChunkPos chunkPos, int32_t id) { + if (!chunkExists(chunkPos)) + return; // do nothing if chunk doesn't even exist Chunk* chunk = chunks[chunkPos]; chunk->NPCs.erase(id); // gone - // if players and NPCs are empty, free chunk and remove it from surrounding views + // if chunk is empty, free it if (chunk->NPCs.size() == 0 && chunk->players.size() == 0) { - destroyChunk(chunkPos); - - // the chunk we left was destroyed - return true; + chunks.erase(chunkPos); // remove from map + delete chunk; // free from memory } - - // the chunk we left was not destroyed - return false; } -void ChunkManager::destroyChunk(ChunkPos chunkPos) { - if (!checkChunk(chunkPos)) +void ChunkManager::addPlayerToChunks(std::set chnks, CNSocket* sock) { + INITSTRUCT(sP_FE2CL_PC_NEW, newPlayer); + + for (Chunk* chunk : chnks) { + // add npcs + for (int32_t id : chunk->NPCs) { + BaseNPC* npc = NPCManager::NPCs[id]; + + if (npc->appearanceData.iHP <= 0) + continue; + + switch (npc->npcClass) { + case NPC_BUS: + INITSTRUCT(sP_FE2CL_TRANSPORTATION_ENTER, enterBusData); + enterBusData.AppearanceData = { 3, npc->appearanceData.iNPC_ID, npc->appearanceData.iNPCType, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ }; + sock->sendPacket((void*)&enterBusData, P_FE2CL_TRANSPORTATION_ENTER, sizeof(sP_FE2CL_TRANSPORTATION_ENTER)); + break; + case NPC_EGG: + INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData); + NPCManager::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); + sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER)); + break; + default: + INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData); + enterData.NPCAppearanceData = NPCManager::NPCs[id]->appearanceData; + sock->sendPacket((void*)&enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER)); + break; + } + } + + // add players + for (CNSocket* otherSock : chunk->players) { + if (sock == otherSock) + continue; // that's us :P + + Player* otherPlr = PlayerManager::getPlayer(otherSock); + Player* plr = PlayerManager::getPlayer(sock); + + newPlayer.PCAppearanceData.iID = plr->iID; + newPlayer.PCAppearanceData.iHP = plr->HP; + newPlayer.PCAppearanceData.iLv = plr->level; + newPlayer.PCAppearanceData.iX = plr->x; + newPlayer.PCAppearanceData.iY = plr->y; + newPlayer.PCAppearanceData.iZ = plr->z; + newPlayer.PCAppearanceData.iAngle = plr->angle; + newPlayer.PCAppearanceData.PCStyle = plr->PCStyle; + newPlayer.PCAppearanceData.Nano = plr->Nanos[plr->activeNano]; + newPlayer.PCAppearanceData.iPCState = plr->iPCState; + newPlayer.PCAppearanceData.iSpecialState = plr->iSpecialState; + memcpy(newPlayer.PCAppearanceData.ItemEquip, plr->Equip, sizeof(sItemBase) * AEQUIP_COUNT); + + otherSock->sendPacket((void*)&newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW)); + + newPlayer.PCAppearanceData.iID = otherPlr->iID; + newPlayer.PCAppearanceData.iHP = otherPlr->HP; + newPlayer.PCAppearanceData.iLv = otherPlr->level; + newPlayer.PCAppearanceData.iX = otherPlr->x; + newPlayer.PCAppearanceData.iY = otherPlr->y; + newPlayer.PCAppearanceData.iZ = otherPlr->z; + newPlayer.PCAppearanceData.iAngle = otherPlr->angle; + newPlayer.PCAppearanceData.PCStyle = otherPlr->PCStyle; + newPlayer.PCAppearanceData.Nano = otherPlr->Nanos[otherPlr->activeNano]; + newPlayer.PCAppearanceData.iPCState = otherPlr->iPCState; + newPlayer.PCAppearanceData.iSpecialState = otherPlr->iSpecialState; + memcpy(newPlayer.PCAppearanceData.ItemEquip, otherPlr->Equip, sizeof(sItemBase) * AEQUIP_COUNT); + + sock->sendPacket((void*)&newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW)); + } + } +} + +void ChunkManager::addNPCToChunks(std::set chnks, int32_t id) { + BaseNPC* npc = NPCManager::NPCs[id]; + + switch (npc->npcClass) { + case NPC_BUS: + INITSTRUCT(sP_FE2CL_TRANSPORTATION_ENTER, enterBusData); + enterBusData.AppearanceData = { 3, npc->appearanceData.iNPC_ID, npc->appearanceData.iNPCType, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ }; + + for (Chunk* chunk : chnks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&enterBusData, P_FE2CL_TRANSPORTATION_ENTER, sizeof(sP_FE2CL_TRANSPORTATION_ENTER)); + } + } + break; + case NPC_EGG: + INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData); + NPCManager::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); + + for (Chunk* chunk : chnks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER)); + } + } + break; + default: + // create struct + INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData); + enterData.NPCAppearanceData = npc->appearanceData; + + for (Chunk* chunk : chnks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER)); + } + } + break; + } +} + +void ChunkManager::removePlayerFromChunks(std::set chnks, CNSocket* sock) { + INITSTRUCT(sP_FE2CL_PC_EXIT, exitPlayer); + + // for chunks that need the player to be removed from + for (Chunk* chunk : chnks) { + + // remove NPCs from view + for (int32_t id : chunk->NPCs) { + BaseNPC* npc = NPCManager::NPCs[id]; + switch (npc->npcClass) { + case NPC_BUS: + INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, exitBusData); + exitBusData.eTT = 3; + exitBusData.iT_ID = id; + sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT)); + break; + case NPC_EGG: + INITSTRUCT(sP_FE2CL_SHINY_EXIT, exitEggData); + exitEggData.iShinyID = id; + sock->sendPacket((void*)&exitEggData, P_FE2CL_SHINY_EXIT, sizeof(sP_FE2CL_SHINY_EXIT)); + break; + default: + INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData); + exitData.iNPC_ID = id; + sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); + break; + } + } + + // remove players from eachother's views + for (CNSocket* otherSock : chunk->players) { + if (sock == otherSock) + continue; // that's us :P + exitPlayer.iID = PlayerManager::getPlayer(sock)->iID; + otherSock->sendPacket((void*)&exitPlayer, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT)); + exitPlayer.iID = PlayerManager::getPlayer(otherSock)->iID; + sock->sendPacket((void*)&exitPlayer, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT)); + } + } + +} + +void ChunkManager::removeNPCFromChunks(std::set chnks, int32_t id) { + BaseNPC* npc = NPCManager::NPCs[id]; + + switch (npc->npcClass) { + case NPC_BUS: + INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, exitBusData); + exitBusData.eTT = 3; + exitBusData.iT_ID = id; + + for (Chunk* chunk : chnks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT)); + } + } + break; + case NPC_EGG: + INITSTRUCT(sP_FE2CL_SHINY_EXIT, exitEggData); + exitEggData.iShinyID = id; + + for (Chunk* chunk : chnks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&exitEggData, P_FE2CL_SHINY_EXIT, sizeof(sP_FE2CL_SHINY_EXIT)); + } + } + break; + default: + // create struct + INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData); + exitData.iNPC_ID = id; + + // remove it from the clients + for (Chunk* chunk : chnks) { + for (CNSocket* sock : chunk->players) { + // send to socket + sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); + } + } + break; + } +} + +void ChunkManager::emptyChunk(ChunkPos chunkPos) { + if (!chunkExists(chunkPos)) { + std::cout << "[WARN] Tried to empty chunk that doesn't exist\n"; return; // chunk doesn't exist, we don't need to do anything + } Chunk* chunk = chunks[chunkPos]; + if (chunk->players.size() > 0) { + std::cout << "[WARN] Tried to empty chunk that still had players\n"; + return; // chunk doesn't exist, we don't need to do anything + } + // unspawn all of the mobs/npcs std::set npcIDs(chunk->NPCs); for (uint32_t id : npcIDs) { + // every call of this will check if the chunk is empty and delete it if so NPCManager::destroyNPC(id); } - - // we also need to remove it from all NPCs/Players views - for (Chunk* otherChunk : grabChunks(chunkPos)) { - if (otherChunk == chunk) - continue; - - // remove from NPCs - for (uint32_t id : otherChunk->NPCs) { - if (std::find(NPCManager::NPCs[id]->currentChunks.begin(), NPCManager::NPCs[id]->currentChunks.end(), chunk) != NPCManager::NPCs[id]->currentChunks.end()) { - NPCManager::NPCs[id]->currentChunks.erase(std::remove(NPCManager::NPCs[id]->currentChunks.begin(), NPCManager::NPCs[id]->currentChunks.end(), chunk), NPCManager::NPCs[id]->currentChunks.end()); - } - } - - // remove from players - for (CNSocket* sock : otherChunk->players) { - Player* plr = PlayerManager::getPlayer(sock); - if (std::find(plr->currentChunks->begin(), plr->currentChunks->end(), chunk) != plr->currentChunks->end()) { - plr->currentChunks->erase(std::remove(plr->currentChunks->begin(), plr->currentChunks->end(), chunk), plr->currentChunks->end()); - } - } - } - - - assert(chunk->players.size() == 0); - - // remove from the map - chunks.erase(chunkPos); - - delete chunk; } -bool ChunkManager::checkChunk(ChunkPos chunk) { +bool ChunkManager::chunkExists(ChunkPos chunk) { return chunks.find(chunk) != chunks.end(); } -ChunkPos ChunkManager::grabChunk(int posX, int posY, uint64_t instanceID) { +ChunkPos ChunkManager::chunkPosAt(int posX, int posY, uint64_t instanceID) { return std::make_tuple(posX / (settings::VIEWDISTANCE / 3), posY / (settings::VIEWDISTANCE / 3), instanceID); } -std::vector ChunkManager::grabChunks(ChunkPos chunk) { - std::vector chnks; - chnks.reserve(9); +std::set ChunkManager::getViewableChunks(ChunkPos chunk) { + std::set chnks; int x, y; uint64_t inst; @@ -179,38 +366,15 @@ std::vector ChunkManager::grabChunks(ChunkPos chunk) { for (int z = -1; z < 2; z++) { ChunkPos pos = std::make_tuple(x+i, y+z, inst); - // if chunk exists, add it to the vector - if (checkChunk(pos)) - chnks.push_back(chunks[pos]); + // if chunk exists, add it to the set + if (chunkExists(pos)) + chnks.insert(chunks[pos]); } } return chnks; } -// returns the chunks that aren't shared (only from from) -std::vector ChunkManager::getDeltaChunks(std::vector from, std::vector to) { - std::vector delta; - - for (Chunk* i : from) { - bool found = false; - - // search for it in the other array - for (Chunk* z : to) { - if (i == z) { - found = true; - break; - } - } - - // add it to the vector if we didn't find it! - if (!found) - delta.push_back(i); - } - - return delta; -} - /* * inefficient algorithm to get all chunks from a specific instance */ @@ -227,8 +391,7 @@ std::vector ChunkManager::getChunksInMap(uint64_t mapNum) { } bool ChunkManager::inPopulatedChunks(int posX, int posY, uint64_t instanceID) { - auto chunk = ChunkManager::grabChunk(posX, posY, instanceID); - auto nearbyChunks = ChunkManager::grabChunks(chunk); + auto nearbyChunks = ChunkManager::getViewableChunks(chunkPosAt(posX, posY, instanceID)); for (Chunk *c: nearbyChunks) { if (!c->players.empty()) @@ -258,7 +421,10 @@ void ChunkManager::createInstance(uint64_t instanceID) { instanceID, baseNPC->appearanceData.iNPCType, newID); NPCManager::NPCs[newID] = newNPC; } - NPCManager::updateNPCInstance(newID, instanceID); // make sure the npc state gets updated + NPCManager::updateNPCPosition(newID, baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, + instanceID, baseNPC->appearanceData.iAngle); + // force chunk update + updateNPCChunk(newID, {0, 0, 0}, chunkPosAt(baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, instanceID)); } } } else { @@ -271,7 +437,7 @@ void ChunkManager::destroyInstance(uint64_t instanceID) { std::vector instanceChunks = ChunkManager::getChunksInMap(instanceID); std::cout << "Deleting instance " << instanceID << " (" << instanceChunks.size() << " chunks)" << std::endl; for (ChunkPos& coords : instanceChunks) { - destroyChunk(coords); + emptyChunk(coords); } } diff --git a/src/ChunkManager.hpp b/src/ChunkManager.hpp index ba6815b..49b93ba 100644 --- a/src/ChunkManager.hpp +++ b/src/ChunkManager.hpp @@ -4,10 +4,10 @@ #include "CNStructs.hpp" #include -#include #include #include #include +#include class Chunk { public: @@ -28,19 +28,27 @@ namespace ChunkManager { extern std::map chunks; void newChunk(ChunkPos pos); - void populateNewChunk(Chunk* chunk, ChunkPos pos); - void addNPC(int posX, int posY, uint64_t instanceID, int32_t id); - void addPlayer(int posX, int posY, uint64_t instanceID, CNSocket* sock); - bool removePlayer(ChunkPos chunkPos, CNSocket* sock); - bool removeNPC(ChunkPos chunkPos, int32_t id); - bool checkChunk(ChunkPos chunk); - void destroyChunk(ChunkPos chunkPos); - ChunkPos grabChunk(int posX, int posY, uint64_t instanceID); - std::vector grabChunks(ChunkPos chunkPos); - std::vector getDeltaChunks(std::vector from, std::vector to); + + void updatePlayerChunk(CNSocket* sock, ChunkPos from, ChunkPos to); + void updateNPCChunk(int32_t id, ChunkPos from, ChunkPos to); + + void trackPlayer(ChunkPos chunkPos, CNSocket* sock); + void trackNPC(ChunkPos chunkPos, int32_t id); + void untrackPlayer(ChunkPos chunkPos, CNSocket* sock); + void untrackNPC(ChunkPos chunkPos, int32_t id); + + void addPlayerToChunks(std::set chnks, CNSocket* sock); + void addNPCToChunks(std::set chnks, int32_t id); + void removePlayerFromChunks(std::set chnks, CNSocket* sock); + void removeNPCFromChunks(std::set chnks, int32_t id); + + bool chunkExists(ChunkPos chunk); + void emptyChunk(ChunkPos chunkPos); + ChunkPos chunkPosAt(int posX, int posY, uint64_t instanceID); + std::set getViewableChunks(ChunkPos chunkPos); + std::vector getChunksInMap(uint64_t mapNum); bool inPopulatedChunks(int posX, int posY, uint64_t instanceID); - void createInstance(uint64_t); void destroyInstance(uint64_t); void destroyInstanceIfEmpty(uint64_t); diff --git a/src/MobManager.cpp b/src/MobManager.cpp index 623481f..dab4a3c 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -554,7 +554,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) { auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->target->plr->x, mob->target->plr->y,std::min(distance-(int)mob->data["m_iAtkRange"]+1, speed*2/5)); - NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ); + NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ, mob->instanceID, mob->appearanceData.iAngle); INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt); @@ -1115,7 +1115,8 @@ bool MobManager::aggroCheck(Mob *mob, time_t currTime) { CNSocket *closest = nullptr; int closestDistance = INT_MAX; - for (Chunk *chunk : mob->currentChunks) { + std::set chunks = ChunkManager::getViewableChunks(ChunkManager::chunkPosAt(mob->appearanceData.iX, mob->appearanceData.iY, mob->instanceID)); + for (Chunk *chunk : chunks) { for (CNSocket *s : chunk->players) { Player *plr = s->plr; diff --git a/src/NPC.hpp b/src/NPC.hpp index 8b7400b..b73a215 100644 --- a/src/NPC.hpp +++ b/src/NPC.hpp @@ -24,8 +24,6 @@ public: appearanceData.iNPC_ID = id; instanceID = iID; - - chunkPos = std::make_tuple(0, 0, instanceID); }; BaseNPC(int x, int y, int z, int angle, uint64_t iID, int type, int id, NPCClass classType) : BaseNPC(x, y, z, angle, iID, type, id) { npcClass = classType; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 7698645..6fe202e 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -54,90 +54,6 @@ void NPCManager::init() { REGISTER_SHARD_TIMER(eggStep, 1000); } -void NPCManager::removeNPC(std::vector viewableChunks, int32_t id) { - BaseNPC* npc = NPCs[id]; - - switch (npc->npcClass) { - case NPC_BUS: - INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, exitBusData); - exitBusData.eTT = 3; - exitBusData.iT_ID = id; - - for (Chunk* chunk : viewableChunks) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT)); - } - } - break; - case NPC_EGG: - INITSTRUCT(sP_FE2CL_SHINY_EXIT, exitEggData); - exitEggData.iShinyID = id; - - for (Chunk* chunk : viewableChunks) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&exitEggData, P_FE2CL_SHINY_EXIT, sizeof(sP_FE2CL_SHINY_EXIT)); - } - } - break; - default: - // create struct - INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData); - exitData.iNPC_ID = id; - - // remove it from the clients - for (Chunk* chunk : viewableChunks) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); - } - } - break; - } -} - -void NPCManager::addNPC(std::vector viewableChunks, int32_t id) { - BaseNPC* npc = NPCs[id]; - - switch (npc->npcClass) { - case NPC_BUS: - INITSTRUCT(sP_FE2CL_TRANSPORTATION_ENTER, enterBusData); - enterBusData.AppearanceData = { 3, npc->appearanceData.iNPC_ID, npc->appearanceData.iNPCType, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ }; - - for (Chunk* chunk : viewableChunks) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&enterBusData, P_FE2CL_TRANSPORTATION_ENTER, sizeof(sP_FE2CL_TRANSPORTATION_ENTER)); - } - } - break; - case NPC_EGG: - INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData); - npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); - - for (Chunk* chunk : viewableChunks) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER)); - } - } - break; - default: - // create struct - INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData); - enterData.NPCAppearanceData = npc->appearanceData; - - for (Chunk* chunk : viewableChunks) { - for (CNSocket* sock : chunk->players) { - // send to socket - sock->sendPacket((void*)&enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER)); - } - } - break; - } -} - void NPCManager::destroyNPC(int32_t id) { // sanity check if (NPCs.find(id) == NPCs.end()) { @@ -146,19 +62,19 @@ void NPCManager::destroyNPC(int32_t id) { } BaseNPC* entity = NPCs[id]; + ChunkPos chunkPos = ChunkManager::chunkPosAt(entity->appearanceData.iX, entity->appearanceData.iY, entity->instanceID); // sanity check - if (ChunkManager::chunks.find(entity->chunkPos) == ChunkManager::chunks.end()) { + if (!ChunkManager::chunkExists(chunkPos)) { std::cout << "chunk not found!" << std::endl; return; } // remove NPC from the chunk - Chunk* chunk = ChunkManager::chunks[entity->chunkPos]; - chunk->NPCs.erase(id); + ChunkManager::untrackNPC(chunkPos, id); // remove from viewable chunks - removeNPC(entity->currentChunks, id); + ChunkManager::removeNPCFromChunks(ChunkManager::getViewableChunks(chunkPos), id); // remove from mob manager if (MobManager::Mobs.find(id) != MobManager::Mobs.end()) @@ -173,58 +89,23 @@ void NPCManager::destroyNPC(int32_t id) { delete entity; } -void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z, int angle) { - NPCs[id]->appearanceData.iAngle = angle; - updateNPCPosition(id, X, Y, Z); -} - -void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z) { +void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z, uint64_t I, int angle) { BaseNPC* npc = NPCs[id]; - + npc->appearanceData.iAngle = angle; + ChunkPos oldChunk = ChunkManager::chunkPosAt(npc->appearanceData.iX, npc->appearanceData.iY, npc->instanceID); + ChunkPos newChunk = ChunkManager::chunkPosAt(X, Y, I); npc->appearanceData.iX = X; npc->appearanceData.iY = Y; npc->appearanceData.iZ = Z; - - ChunkPos newPos = ChunkManager::grabChunk(X, Y, npc->instanceID); - - // nothing to be done (but we should also update currentChunks to add/remove stale chunks) - if (newPos == npc->chunkPos) { - npc->currentChunks = ChunkManager::grabChunks(newPos); - return; - } - - ChunkManager::addNPC(X, Y, npc->instanceID, id); - std::vector allChunks = ChunkManager::grabChunks(newPos); - - // send npc exit to stale chunks - removeNPC(ChunkManager::getDeltaChunks(npc->currentChunks, allChunks), id); - - // send npc enter to new chunks - addNPC(ChunkManager::getDeltaChunks(allChunks, npc->currentChunks), id); - - Chunk *chunk = nullptr; - if (ChunkManager::checkChunk(npc->chunkPos)) - chunk = ChunkManager::chunks[npc->chunkPos]; - - if (ChunkManager::removeNPC(npc->chunkPos, id)) { - // if the old chunk was deallocated, remove it - allChunks.erase(std::remove(allChunks.begin(), allChunks.end(), chunk), allChunks.end()); - } - - - - npc->chunkPos = newPos; - npc->currentChunks = allChunks; -} - -void NPCManager::updateNPCInstance(int32_t npcID, uint64_t instanceID) { - BaseNPC* npc = NPCs[npcID]; - npc->instanceID = instanceID; - updateNPCPosition(npcID, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ); + npc->instanceID = I; + if (oldChunk == newChunk) + return; // didn't change chunks + ChunkManager::updateNPCChunk(id, oldChunk, newChunk); } void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t size) { - for (Chunk *chunk : npc->currentChunks) { + std::set chunks = ChunkManager::getViewableChunks(ChunkManager::chunkPosAt(npc->appearanceData.iX, npc->appearanceData.iY, npc->instanceID)); + for (Chunk *chunk : chunks) { for (CNSocket *s : chunk->players) { s->sendPacket(buf, type, size); } @@ -602,7 +483,9 @@ void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) { } else NPCs[id] = new BaseNPC(plr->x, plr->y, plr->z, 0, plr->instanceID, req->iNPCType, id); - updateNPCPosition(id, plr->x, plr->y, plr->z); + updateNPCPosition(id, plr->x, plr->y, plr->z, plr->instanceID, 0); + // force chunk update + ChunkManager::updateNPCChunk(id, { 0, 0, 0 }, ChunkManager::chunkPosAt(plr->x, plr->y, plr->instanceID)); } } @@ -655,16 +538,15 @@ void NPCManager::handleWarp(CNSocket* sock, int32_t warpId) { } else { - INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp); //Can only be used for exiting instances because it sets the instance flag to false + INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp); // Can only be used for exiting instances because it sets the instance flag to false resp.iX = Warps[warpId].x; resp.iY = Warps[warpId].y; resp.iZ = Warps[warpId].z; resp.iCandy = plr->money; resp.eIL = 4; // do not take away any items - PlayerManager::removePlayerFromChunks(*plr->currentChunks, sock); - plr->currentChunks->clear(); plr->instanceID = INSTANCE_OVERWORLD; sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_NPC_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC)); + PlayerManager::updatePlayerPosition(sock, resp.iX, resp.iY, resp.iZ, INSTANCE_OVERWORLD, plr->angle); } } @@ -844,7 +726,9 @@ void NPCManager::eggStep(CNServer* serv, time_t currTime) { egg.second->dead = false; egg.second->deadUntil = 0; egg.second->appearanceData.iHP = 400; - addNPC(egg.second->currentChunks, egg.first); + + ChunkManager::addNPCToChunks(ChunkManager::getViewableChunks(ChunkManager::chunkPosAt(egg.second->appearanceData.iX, egg.second->appearanceData.iY, egg.second->instanceID)), + egg.first); } } @@ -1012,7 +896,8 @@ void NPCManager::eggPickup(CNSocket* sock, CNPacketData* data) { if (egg->summoned) destroyNPC(eggId); else { - removeNPC(egg->currentChunks, eggId); + ChunkManager::removeNPCFromChunks(ChunkManager::getViewableChunks(ChunkManager::chunkPosAt(egg->appearanceData.iX, egg->appearanceData.iY, egg->instanceID)), + eggId); egg->dead = true; egg->deadUntil = getTime() + (time_t)type->regen * 1000; egg->appearanceData.iHP = 0; diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index ebe738e..521cf58 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -47,12 +47,8 @@ namespace NPCManager { extern int32_t nextId; void init(); - void addNPC(std::vector viewableChunks, int32_t id); - void removeNPC(std::vector viewableChunks, int32_t id); void destroyNPC(int32_t); - void updateNPCPosition(int32_t, int X, int Y, int Z, int angle); - void updateNPCPosition(int32_t, int X, int Y, int Z); - void updateNPCInstance(int32_t, uint64_t instanceID); + void updateNPCPosition(int32_t, int X, int Y, int Z, uint64_t I, int angle); void sendToViewable(BaseNPC* npc, void* buf, uint32_t type, size_t size); diff --git a/src/Player.hpp b/src/Player.hpp index 2755723..0060347 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -77,7 +77,7 @@ struct Player { bool buddiesSynced; int64_t buddyIDs[50]; - ChunkPos chunkPos; - std::vector* currentChunks; + //ChunkPos chunkPos; + std::set* currentChunks; time_t lastHeartbeat; }; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index ccc2f11..45076cf 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -53,8 +53,8 @@ void PlayerManager::addPlayer(CNSocket* key, Player plr) { memcpy(p, &plr, sizeof(Player)); players[key] = p; - p->chunkPos = std::make_tuple(0, 0, 0); - p->currentChunks = new std::vector(); + //p->chunkPos = std::make_tuple(0, 0, 0); + p->currentChunks = new std::set(); p->lastHeartbeat = 0; key->plr = p; @@ -75,12 +75,11 @@ void PlayerManager::removePlayer(CNSocket* key) { // save player to DB Database::updatePlayer(plr); - // remove players from all chunks - removePlayerFromChunks(*plr->currentChunks, key); + ChunkPos chunkPos = ChunkManager::chunkPosAt(plr->x, plr->y, plr->instanceID); - // remove from chunk - if (ChunkManager::chunks.find(plr->chunkPos) != ChunkManager::chunks.end()) - ChunkManager::chunks[plr->chunkPos]->players.erase(key); + // remove player visually and untrack + ChunkManager::removePlayerFromChunks(ChunkManager::getViewableChunks(chunkPos), key); + ChunkManager::untrackPlayer(chunkPos, key); std::cout << getPlayerName(key->plr) << " has left!" << std::endl; @@ -104,167 +103,24 @@ void PlayerManager::removePlayer(CNSocket* key) { std::cout << players.size() << " players" << std::endl; } -bool PlayerManager::removePlayerFromChunks(std::vector chunks, CNSocket* sock) { - INITSTRUCT(sP_FE2CL_PC_EXIT, exitPlayer); - - // for chunks that need the player to be removed from - for (Chunk* chunk : chunks) { - - // remove NPCs - for (int32_t id : chunk->NPCs) { - BaseNPC* npc = NPCManager::NPCs[id]; - switch (npc->npcClass) { - case NPC_BUS: - INITSTRUCT(sP_FE2CL_TRANSPORTATION_EXIT, exitBusData); - exitBusData.eTT = 3; - exitBusData.iT_ID = id; - sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT)); - break; - case NPC_EGG: - INITSTRUCT(sP_FE2CL_SHINY_EXIT, exitEggData); - exitEggData.iShinyID = id; - sock->sendPacket((void*)&exitEggData, P_FE2CL_SHINY_EXIT, sizeof(sP_FE2CL_SHINY_EXIT)); - break; - default: - INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData); - exitData.iNPC_ID = id; - sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT)); - break; - } - } - - // remove players from eachother - for (CNSocket* otherSock : chunk->players) { - exitPlayer.iID = getPlayer(sock)->iID; - otherSock->sendPacket((void*)&exitPlayer, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT)); - exitPlayer.iID = getPlayer(otherSock)->iID; - sock->sendPacket((void*)&exitPlayer, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT)); - } - } - - // remove us from that old stinky chunk - return ChunkManager::removePlayer(getPlayer(sock)->chunkPos, sock); -} - -void PlayerManager::addPlayerToChunks(std::vector chunks, CNSocket* sock) { - INITSTRUCT(sP_FE2CL_PC_NEW, newPlayer); - - for (Chunk* chunk : chunks) { - // add npcs - for (int32_t id : chunk->NPCs) { - BaseNPC* npc = NPCManager::NPCs[id]; - - if (npc->appearanceData.iHP <= 0) - continue; - - switch (npc->npcClass) { - case NPC_BUS: - INITSTRUCT(sP_FE2CL_TRANSPORTATION_ENTER, enterBusData); - enterBusData.AppearanceData = { 3, npc->appearanceData.iNPC_ID, npc->appearanceData.iNPCType, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ }; - sock->sendPacket((void*)&enterBusData, P_FE2CL_TRANSPORTATION_ENTER, sizeof(sP_FE2CL_TRANSPORTATION_ENTER)); - break; - case NPC_EGG: - INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData); - NPCManager::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData); - sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER)); - break; - default: - INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData); - enterData.NPCAppearanceData = NPCManager::NPCs[id]->appearanceData; - sock->sendPacket((void*)&enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER)); - break; - } - } - - // add players - for (CNSocket* otherSock : chunk->players) { - if (sock == otherSock) - continue; - - Player *otherPlr = getPlayer(otherSock); - Player *plr = getPlayer(sock); - - newPlayer.PCAppearanceData.iID = plr->iID; - newPlayer.PCAppearanceData.iHP = plr->HP; - newPlayer.PCAppearanceData.iLv = plr->level; - newPlayer.PCAppearanceData.iX = plr->x; - newPlayer.PCAppearanceData.iY = plr->y; - newPlayer.PCAppearanceData.iZ = plr->z; - newPlayer.PCAppearanceData.iAngle = plr->angle; - newPlayer.PCAppearanceData.PCStyle = plr->PCStyle; - newPlayer.PCAppearanceData.Nano = plr->Nanos[plr->activeNano]; - newPlayer.PCAppearanceData.iPCState = plr->iPCState; - newPlayer.PCAppearanceData.iSpecialState = plr->iSpecialState; - memcpy(newPlayer.PCAppearanceData.ItemEquip, plr->Equip, sizeof(sItemBase) * AEQUIP_COUNT); - - otherSock->sendPacket((void*)&newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW)); - - newPlayer.PCAppearanceData.iID = otherPlr->iID; - newPlayer.PCAppearanceData.iHP = otherPlr->HP; - newPlayer.PCAppearanceData.iLv = otherPlr->level; - newPlayer.PCAppearanceData.iX = otherPlr->x; - newPlayer.PCAppearanceData.iY = otherPlr->y; - newPlayer.PCAppearanceData.iZ = otherPlr->z; - newPlayer.PCAppearanceData.iAngle = otherPlr->angle; - newPlayer.PCAppearanceData.PCStyle = otherPlr->PCStyle; - newPlayer.PCAppearanceData.Nano = otherPlr->Nanos[otherPlr->activeNano]; - newPlayer.PCAppearanceData.iPCState = otherPlr->iPCState; - newPlayer.PCAppearanceData.iSpecialState = otherPlr->iSpecialState; - memcpy(newPlayer.PCAppearanceData.ItemEquip, otherPlr->Equip, sizeof(sItemBase) * AEQUIP_COUNT); - - sock->sendPacket((void*)&newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW)); - } - } -} - -void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, int angle) { - getPlayer(sock)->angle = angle; - updatePlayerPosition(sock, X, Y, Z); -} - -void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z) { +void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, uint64_t I, int angle) { Player* plr = getPlayer(sock); + plr->angle = angle; + ChunkPos oldChunk = ChunkManager::chunkPosAt(plr->x, plr->y, plr->instanceID); + ChunkPos newChunk = ChunkManager::chunkPosAt(X, Y, I); plr->x = X; plr->y = Y; plr->z = Z; - updatePlayerChunk(sock, X, Y, plr->instanceID); -} - -void PlayerManager::updatePlayerChunk(CNSocket* sock, int X, int Y, uint64_t instanceID) { - Player* plr = getPlayer(sock); - ChunkPos newPos = ChunkManager::grabChunk(X, Y, plr->instanceID); - - // nothing to be done - if (newPos == plr->chunkPos) - return; - - // add player to chunk - ChunkManager::addPlayer(X, Y, plr->instanceID, sock); - std::vector allChunks = ChunkManager::grabChunks(newPos); - - Chunk *chunk = nullptr; - if (ChunkManager::checkChunk(plr->chunkPos)) - chunk = ChunkManager::chunks[plr->chunkPos]; - - // first, remove all the old npcs & players from the old chunks - if (removePlayerFromChunks(ChunkManager::getDeltaChunks(*plr->currentChunks, allChunks), sock)) { - allChunks.erase(std::remove(allChunks.begin(), allChunks.end(), chunk), allChunks.end()); - } - - // now, add all the new npcs & players! - addPlayerToChunks(ChunkManager::getDeltaChunks(allChunks, *plr->currentChunks), sock); - - plr->chunkPos = newPos; - plr->currentChunks->clear(); - for (Chunk* c : allChunks) { - plr->currentChunks->push_back(c); - } + plr->instanceID = I; + if (oldChunk == newChunk) + return; // didn't change chunks + ChunkManager::updatePlayerChunk(sock, oldChunk, newChunk); } void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I) { Player* plr = getPlayer(sock); - if (plr->instanceID == 0) { + if (plr->instanceID == INSTANCE_OVERWORLD) { // save last uninstanced coords plr->lastX = plr->x; plr->lastY = plr->y; @@ -276,13 +132,16 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I uint64_t fromInstance = plr->instanceID; // pre-warp instance, saved for post-warp - plr->instanceID = I; if (I != INSTANCE_OVERWORLD) { INITSTRUCT(sP_FE2CL_INSTANCE_MAP_INFO, pkt); pkt.iEP_ID = PLAYERID(I) == 0; // iEP_ID has to be positive for the map to be enabled pkt.iInstanceMapNum = (int32_t)MAPNUM(I); // lower 32 bits are mapnum sock->sendPacket((void*)&pkt, P_FE2CL_INSTANCE_MAP_INFO, sizeof(sP_FE2CL_INSTANCE_MAP_INFO)); - sendPlayerTo(sock, X, Y, Z); + INITSTRUCT(sP_FE2CL_REP_PC_GOTO_SUCC, pkt2); + pkt2.iX = X; + pkt2.iY = Y; + pkt2.iZ = Z; + sock->sendPacket((void*)&pkt2, P_FE2CL_REP_PC_GOTO_SUCC, sizeof(sP_FE2CL_REP_PC_GOTO_SUCC)); } else { // annoying but necessary to set the flag back INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp); @@ -291,29 +150,18 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I resp.iZ = Z; resp.iCandy = plr->money; resp.eIL = 4; // do not take away any items - PlayerManager::removePlayerFromChunks(*plr->currentChunks, sock); - plr->currentChunks->clear(); sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_NPC_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC)); - updatePlayerPosition(sock, X, Y, Z); } + updatePlayerPosition(sock, X, Y, Z, I, plr->angle); + // post-warp: check if the source instance has no more players in it and delete it if so ChunkManager::destroyInstanceIfEmpty(fromInstance); + } void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z) { - PlayerManager::updatePlayerPosition(sock, X, Y, Z); - INITSTRUCT(sP_FE2CL_REP_PC_GOTO_SUCC, pkt); - pkt.iX = X; - pkt.iY = Y; - pkt.iZ = Z; - - // force player & NPC reload - Player* plr = getPlayer(sock); - PlayerManager::removePlayerFromChunks(*plr->currentChunks, sock); - plr->currentChunks->clear(); - plr->chunkPos = std::make_tuple(0, 0, plr->instanceID); - sock->sendPacket((void*)&pkt, P_FE2CL_REP_PC_GOTO_SUCC, sizeof(sP_FE2CL_REP_PC_GOTO_SUCC)); + sendPlayerTo(sock, X, Y, Z, getPlayer(sock)->instanceID); } void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { @@ -445,7 +293,9 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { } void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) { - for (Chunk* chunk : *getPlayer(sock)->currentChunks) { + Player* plr = getPlayer(sock); + std::set chunks = ChunkManager::getViewableChunks(ChunkManager::chunkPosAt(plr->x, plr->y, plr->instanceID)); + for (Chunk* chunk : chunks) { for (CNSocket* otherSock : chunk->players) { if (otherSock == sock) continue; @@ -471,7 +321,8 @@ void PlayerManager::loadPlayer(CNSocket* sock, CNPacketData* data) { response.iPC_ID = complete->iPC_ID; // reload players & NPCs - updatePlayerPosition(sock, plr->x, plr->y, plr->z, plr->angle); + updatePlayerPosition(sock, plr->x, plr->y, plr->z, plr->instanceID, plr->angle); + ChunkManager::updatePlayerChunk(sock, { 0, 0, 0 }, ChunkManager::chunkPosAt(plr->x, plr->y, plr->instanceID)); sock->sendPacket((void*)&response, P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC, sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC)); } @@ -480,12 +331,11 @@ void PlayerManager::movePlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_MOVE)) return; // ignore the malformed packet - sP_CL2FE_REQ_PC_MOVE* moveData = (sP_CL2FE_REQ_PC_MOVE*)data->buf; - updatePlayerPosition(sock, moveData->iX, moveData->iY, moveData->iZ, moveData->iAngle); - Player* plr = getPlayer(sock); - plr->angle = moveData->iAngle; + sP_CL2FE_REQ_PC_MOVE* moveData = (sP_CL2FE_REQ_PC_MOVE*)data->buf; + updatePlayerPosition(sock, moveData->iX, moveData->iY, moveData->iZ, plr->instanceID, moveData->iAngle); + uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_MOVE, moveResponse); @@ -512,14 +362,16 @@ void PlayerManager::stopPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_STOP)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_STOP* stopData = (sP_CL2FE_REQ_PC_STOP*)data->buf; - updatePlayerPosition(sock, stopData->iX, stopData->iY, stopData->iZ); + updatePlayerPosition(sock, stopData->iX, stopData->iY, stopData->iZ, plr->instanceID, plr->angle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_STOP, stopResponse); - stopResponse.iID = getPlayer(sock)->iID; + stopResponse.iID = plr->iID; stopResponse.iX = stopData->iX; stopResponse.iY = stopData->iY; @@ -535,14 +387,16 @@ void PlayerManager::jumpPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_JUMP)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_JUMP* jumpData = (sP_CL2FE_REQ_PC_JUMP*)data->buf; - updatePlayerPosition(sock, jumpData->iX, jumpData->iY, jumpData->iZ, jumpData->iAngle); + updatePlayerPosition(sock, jumpData->iX, jumpData->iY, jumpData->iZ, plr->instanceID, jumpData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_JUMP, jumpResponse); - jumpResponse.iID = getPlayer(sock)->iID; + jumpResponse.iID = plr->iID; jumpResponse.cKeyValue = jumpData->cKeyValue; jumpResponse.iX = jumpData->iX; @@ -564,14 +418,16 @@ void PlayerManager::jumppadPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_JUMPPAD)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_JUMPPAD* jumppadData = (sP_CL2FE_REQ_PC_JUMPPAD*)data->buf; - updatePlayerPosition(sock, jumppadData->iX, jumppadData->iY, jumppadData->iZ, jumppadData->iAngle); + updatePlayerPosition(sock, jumppadData->iX, jumppadData->iY, jumppadData->iZ, plr->instanceID, jumppadData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_JUMPPAD, jumppadResponse); - jumppadResponse.iPC_ID = getPlayer(sock)->iID; + jumppadResponse.iPC_ID = plr->iID; jumppadResponse.cKeyValue = jumppadData->cKeyValue; jumppadResponse.iX = jumppadData->iX; @@ -591,14 +447,16 @@ void PlayerManager::launchPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_LAUNCHER)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_LAUNCHER* launchData = (sP_CL2FE_REQ_PC_LAUNCHER*)data->buf; - updatePlayerPosition(sock, launchData->iX, launchData->iY, launchData->iZ, launchData->iAngle); + updatePlayerPosition(sock, launchData->iX, launchData->iY, launchData->iZ, plr->instanceID, launchData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_LAUNCHER, launchResponse); - launchResponse.iPC_ID = getPlayer(sock)->iID; + launchResponse.iPC_ID = plr->iID; launchResponse.iX = launchData->iX; launchResponse.iY = launchData->iY; @@ -619,14 +477,16 @@ void PlayerManager::ziplinePlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_ZIPLINE)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_ZIPLINE* ziplineData = (sP_CL2FE_REQ_PC_ZIPLINE*)data->buf; - updatePlayerPosition(sock, ziplineData->iX, ziplineData->iY, ziplineData->iZ, ziplineData->iAngle); + updatePlayerPosition(sock, ziplineData->iX, ziplineData->iY, ziplineData->iZ, plr->instanceID, ziplineData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_ZIPLINE, ziplineResponse); - ziplineResponse.iPC_ID = getPlayer(sock)->iID; + ziplineResponse.iPC_ID = plr->iID; ziplineResponse.iCliTime = ziplineData->iCliTime; ziplineResponse.iSvrTime = tm; ziplineResponse.iX = ziplineData->iX; @@ -654,14 +514,16 @@ void PlayerManager::movePlatformPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_MOVEPLATFORM)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_MOVEPLATFORM* platformData = (sP_CL2FE_REQ_PC_MOVEPLATFORM*)data->buf; - updatePlayerPosition(sock, platformData->iX, platformData->iY, platformData->iZ, platformData->iAngle); + updatePlayerPosition(sock, platformData->iX, platformData->iY, platformData->iZ, plr->instanceID, platformData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_MOVEPLATFORM, platResponse); - platResponse.iPC_ID = getPlayer(sock)->iID; + platResponse.iPC_ID = plr->iID; platResponse.iCliTime = platformData->iCliTime; platResponse.iSvrTime = tm; platResponse.iX = platformData->iX; @@ -686,14 +548,16 @@ void PlayerManager::moveSliderPlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_MOVETRANSPORTATION)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_MOVETRANSPORTATION* sliderData = (sP_CL2FE_REQ_PC_MOVETRANSPORTATION*)data->buf; - updatePlayerPosition(sock, sliderData->iX, sliderData->iY, sliderData->iZ, sliderData->iAngle); + updatePlayerPosition(sock, sliderData->iX, sliderData->iY, sliderData->iZ, plr->instanceID, sliderData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_MOVETRANSPORTATION, sliderResponse); - sliderResponse.iPC_ID = getPlayer(sock)->iID; + sliderResponse.iPC_ID = plr->iID; sliderResponse.iCliTime = sliderData->iCliTime; sliderResponse.iSvrTime = tm; sliderResponse.iX = sliderData->iX; @@ -717,14 +581,16 @@ void PlayerManager::moveSlopePlayer(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_SLOPE)) return; // ignore the malformed packet + Player* plr = getPlayer(sock); + sP_CL2FE_REQ_PC_SLOPE* slopeData = (sP_CL2FE_REQ_PC_SLOPE*)data->buf; - updatePlayerPosition(sock, slopeData->iX, slopeData->iY, slopeData->iZ,slopeData->iAngle); + updatePlayerPosition(sock, slopeData->iX, slopeData->iY, slopeData->iZ, plr->instanceID, slopeData->iAngle); uint64_t tm = getTime(); INITSTRUCT(sP_FE2CL_PC_SLOPE, slopeResponse); - slopeResponse.iPC_ID = getPlayer(sock)->iID; + slopeResponse.iPC_ID = plr->iID; slopeResponse.iCliTime = slopeData->iCliTime; slopeResponse.iSvrTime = tm; slopeResponse.iX = slopeData->iX; @@ -755,7 +621,7 @@ void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) { std::cout << "\tZ: " << gotoData->iToZ << std::endl; ) - sendPlayerTo(sock, gotoData->iToX, gotoData->iToY, gotoData->iToZ, 0); + sendPlayerTo(sock, gotoData->iToX, gotoData->iToY, gotoData->iToZ, INSTANCE_OVERWORLD); } void PlayerManager::setSpecialPlayer(CNSocket* sock, CNPacketData* data) { @@ -851,7 +717,7 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) { NanoManager::nanoUnbuff(sock, CSB_BIT_PHOENIX, ECSB_PHOENIX, 0, false); plr->HP = PC_MAXHEALTH(plr->level); } else { - updatePlayerPosition(sock, target.x, target.y, target.z); + updatePlayerPosition(sock, target.x, target.y, target.z, plr->instanceID, plr->angle); if (reviveData->iRegenType != 5) plr->HP = PC_MAXHEALTH(plr->level); diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 886f92c..e99ac65 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -19,12 +19,7 @@ namespace PlayerManager { void addPlayer(CNSocket* key, Player plr); void removePlayer(CNSocket* key); - bool removePlayerFromChunks(std::vector chunks, CNSocket* sock); - void addPlayerToChunks(std::vector chunks, CNSocket* sock); - - void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z); - void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, int angle); - void updatePlayerChunk(CNSocket* sock, int X, int Y, uint64_t instanceID); + void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, uint64_t I, int angle); void sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I); void sendPlayerTo(CNSocket* sock, int X, int Y, int Z); diff --git a/src/TableData.cpp b/src/TableData.cpp index 59b811e..b8550c5 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -43,7 +43,8 @@ void TableData::init() { BaseNPC *tmp = new BaseNPC(npc["x"], npc["y"], npc["z"], npc["angle"], instanceID, npc["id"], nextId); NPCManager::NPCs[nextId] = tmp; - NPCManager::updateNPCPosition(nextId, npc["x"], npc["y"], npc["z"]); + NPCManager::updateNPCPosition(nextId, npc["x"], npc["y"], npc["z"], instanceID, npc["angle"]); + ChunkManager::updateNPCChunk(nextId, {0, 0, 0}, ChunkManager::chunkPosAt(npc["x"], npc["y"], instanceID)); nextId++; if (npc["id"] == 641 || npc["id"] == 642) @@ -210,7 +211,8 @@ void TableData::init() { NPCManager::NPCs[nextId] = tmp; MobManager::Mobs[nextId] = (Mob*)NPCManager::NPCs[nextId]; - NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"]); + NPCManager::updateNPCPosition(nextId, npc["iX"], npc["iY"], npc["iZ"], instanceID, npc["iAngle"]); + ChunkManager::updateNPCChunk(nextId, { 0, 0, 0 }, ChunkManager::chunkPosAt(npc["iX"], npc["iY"], instanceID)); nextId++; } @@ -282,7 +284,7 @@ void TableData::loadPaths(int* nextId) { // spawn a slider BaseNPC* slider = new BaseNPC(sliderPoint["iX"], sliderPoint["iY"], sliderPoint["iZ"], 0, INSTANCE_OVERWORLD, 1, (*nextId)++, NPC_BUS); NPCManager::NPCs[slider->appearanceData.iNPC_ID] = slider; - NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->appearanceData.iX, slider->appearanceData.iY, slider->appearanceData.iZ); + NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->appearanceData.iX, slider->appearanceData.iY, slider->appearanceData.iZ, INSTANCE_OVERWORLD, 0); // set slider path to a rotation of the circuit constructPathSlider(pathDataSlider, pos, slider->appearanceData.iNPC_ID); @@ -466,7 +468,7 @@ void TableData::loadEggs(int32_t* nextId) { Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); NPCManager::NPCs[id] = addEgg; NPCManager::Eggs[id] = addEgg; - NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID); + NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); } std::cout << "[INFO] Loaded " <appearanceData.iNPC_ID, instanceID); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->appearanceData.iX, npc->appearanceData.iY, + npc->appearanceData.iZ, instanceID, npc->appearanceData.iAngle); RunningNPCMapNumbers[npcID] = instanceID; } @@ -626,7 +629,7 @@ void TableData::loadGruntwork(int32_t *nextId) { NPCManager::NPCs[npc->appearanceData.iNPC_ID] = npc; TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, mob["iX"], mob["iY"], mob["iZ"]); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iAngle"]); } auto eggs = gruntwork["eggs"]; @@ -638,7 +641,7 @@ void TableData::loadGruntwork(int32_t *nextId) { Egg* addEgg = new Egg((int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, (int)egg["iType"], id, false); NPCManager::NPCs[id] = addEgg; NPCManager::Eggs[id] = addEgg; - NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID); + NPCManager::updateNPCPosition(id, (int)egg["iX"], (int)egg["iY"], (int)egg["iZ"], instanceID, 0); TableData::RunningEggs[id] = addEgg; } diff --git a/src/TransportManager.cpp b/src/TransportManager.cpp index 5aa6a67..ee13bbb 100644 --- a/src/TransportManager.cpp +++ b/src/TransportManager.cpp @@ -142,16 +142,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data) 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(*plr->currentChunks, sock); - plr->currentChunks->clear(); - plr->chunkPos = std::make_tuple(0, 0, plr->instanceID); + PlayerManager::updatePlayerPosition(sock, target.x, target.y, target.z, INSTANCE_OVERWORLD, plr->angle); break; case 2: // Monkey Skyway if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) { // check if route exists @@ -254,7 +245,7 @@ void TransportManager::stepSkywaySystem() { 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::updatePlayerChunk(it->first, point.x, point.y, plr->instanceID); + PlayerManager::updatePlayerPosition(it->first, point.x, point.y, point.z, plr->instanceID, plr->angle); // send packet to players in view PlayerManager::sendToViewable(it->first, (void*)&bmstk, P_FE2CL_PC_BROOMSTICK_MOVE, sizeof(sP_FE2CL_PC_BROOMSTICK_MOVE)); @@ -301,7 +292,7 @@ void TransportManager::stepNPCPathing() { int distanceBetween = hypot(dXY, point.z - npc->appearanceData.iZ); // total distance // update NPC location to update viewables - NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, point.x, point.y, point.z); + NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, point.x, point.y, point.z, npc->instanceID, npc->appearanceData.iAngle); switch (npc->npcClass) { case NPC_BUS: