diff --git a/src/Chunking.cpp b/src/Chunking.cpp index 65231fe..4861ad5 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -1,5 +1,6 @@ #include "Chunking.hpp" +#include "Player.hpp" #include "MobAI.hpp" #include "NPCManager.hpp" @@ -11,6 +12,11 @@ using namespace Chunking; * The initial chunkPos value before a player is placed into the world. */ const ChunkPos Chunking::INVALID_CHUNK = {}; +constexpr size_t MAX_PC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sPCAppearanceData); +constexpr size_t MAX_NPC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sNPCAppearanceData); +constexpr size_t MAX_SHINY_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sShinyAppearanceData); +constexpr size_t MAX_TRANSPORTATION_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sTransportationAppearanceData); +constexpr size_t MAX_IDS_PER_AROUND_DEL = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(int32_t); std::map Chunking::chunks; @@ -75,11 +81,83 @@ void Chunking::untrackEntity(ChunkPos chunkPos, const EntityRef ref) { deleteChunk(chunkPos); } +template +static void sendAroundPacket(const EntityRef recipient, std::vector>& slices, size_t maxCnt, uint32_t packetId) { + assert(recipient.kind == EntityKind::PLAYER); + + uint8_t pktBuf[CN_PACKET_BUFFER_SIZE]; + for (const auto& slice : slices) { + memset(pktBuf, 0, CN_PACKET_BUFFER_SIZE); + int count = slice.size(); + assert(count <= maxCnt); + *((int32_t*)pktBuf) = count; + T* data = (T*)(pktBuf + sizeof(int32_t)); + for (size_t i = 0; i < count; i++) { + data[i] = slice[i]; + } + recipient.sock->sendPacket(pktBuf, packetId, 4 + (count * sizeof(T))); + } +} + +template +static void sendAroundDelPacket(const EntityRef recipient, std::vector>& slices, bool isTransportation, uint32_t packetId) { + assert(recipient.kind == EntityKind::PLAYER); + + size_t maxCnt = MAX_IDS_PER_AROUND_DEL; + if (isTransportation) + maxCnt -= 1; // account for eTT. sad. + + uint8_t pktBuf[CN_PACKET_BUFFER_SIZE]; + for (const auto& slice : slices) { + memset(pktBuf, 0, CN_PACKET_BUFFER_SIZE); + int count = slice.size(); + assert(count <= maxCnt); + + T* data; + if (isTransportation) { + *((int32_t*)pktBuf) = 3; // eTT + *((int32_t*)pktBuf + sizeof(int32_t)) = count; + data = (T*)(pktBuf + sizeof(int32_t) + sizeof(int32_t)); + } else { + *((int32_t*)pktBuf) = count; + data = (T*)(pktBuf + sizeof(int32_t)); + } + + for (size_t i = 0; i < count; i++) { + data[i] = slice[i]; + } + recipient.sock->sendPacket(pktBuf, packetId, isTransportation ? 8 : 4 + (count * sizeof(T))); + } +} + +template +static void bufferAppearanceData(std::vector>& slices, const T& data, size_t maxCnt) { + if (slices.empty()) + slices.push_back(std::vector()); + std::vector& slice = slices[slices.size() - 1]; + slice.push_back(data); + if (slice.size() == maxCnt) + slices.push_back(std::vector()); +} + +template +static void bufferIdForDisappearance(std::vector>& slices, int32_t id, size_t maxCnt) { + if (slices.empty()) + slices.push_back(std::vector()); + std::vector& slice = slices[slices.size() - 1]; + slice.push_back(id); + if (slice.size() == maxCnt) + slices.push_back(std::vector()); +} + void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { Entity *ent = ref.getEntity(); bool alive = ent->isExtant(); - // TODO: maybe optimize this, potentially using AROUND packets? + std::vector> pcAppearances; + std::vector> npcAppearances; + std::vector> shinyAppearances; + std::vector> transportationAppearances; for (Chunk *chunk : chnks) { for (const EntityRef otherRef : chunk->entities) { // skip oneself @@ -95,7 +173,33 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { // notify this *player* of the existence of all visible Entities if (ref.kind == EntityKind::PLAYER && other->isExtant()) { - other->enterIntoViewOf(ref.sock); + switch(otherRef.kind) + { + case EntityKind::PLAYER: + sPCAppearanceData pcData = dynamic_cast(other)->getAppearanceData(); + bufferAppearanceData(pcAppearances, pcData, MAX_PC_PER_AROUND); + break; + case EntityKind::SIMPLE_NPC: + sNPCAppearanceData npcData = dynamic_cast(other)->getAppearanceData(); + bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + break; + case EntityKind::COMBAT_NPC: + sNPCAppearanceData combatNpcData = dynamic_cast(other)->getAppearanceData(); + bufferAppearanceData(npcAppearances, combatNpcData, MAX_NPC_PER_AROUND); + break; + case EntityKind::MOB: + sNPCAppearanceData mobData = dynamic_cast(other)->getAppearanceData(); + bufferAppearanceData(npcAppearances, mobData, MAX_NPC_PER_AROUND); + break; + case EntityKind::EGG: + sShinyAppearanceData shinyData = dynamic_cast(other)->getShinyAppearanceData(); + bufferAppearanceData(shinyAppearances, shinyData, MAX_SHINY_PER_AROUND); + break; + case EntityKind::BUS: + sTransportationAppearanceData transportationData = dynamic_cast(other)->getTransportationAppearanceData(); + bufferAppearanceData(transportationAppearances, transportationData, MAX_TRANSPORTATION_PER_AROUND); + break; + } } // for mobs, increment playersInView @@ -105,13 +209,28 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { ((Mob*)other)->playersInView++; } } + + if (ref.kind != EntityKind::PLAYER) + return; // nothing to send + + if (!pcAppearances.empty()) + sendAroundPacket(ref, pcAppearances, MAX_PC_PER_AROUND, P_FE2CL_PC_AROUND); + if (!npcAppearances.empty()) + sendAroundPacket(ref, npcAppearances, MAX_NPC_PER_AROUND, P_FE2CL_NPC_AROUND); + if (!shinyAppearances.empty()) + sendAroundPacket(ref, shinyAppearances, MAX_SHINY_PER_AROUND, P_FE2CL_SHINY_AROUND); + if (!transportationAppearances.empty()) + sendAroundPacket(ref, transportationAppearances, MAX_TRANSPORTATION_PER_AROUND, P_FE2CL_TRANSPORTATION_AROUND); } void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef ref) { Entity *ent = ref.getEntity(); bool alive = ent->isExtant(); - // TODO: same as above + std::vector> pcDisappearances; + std::vector> npcDisappearances; + std::vector> shinyDisappearances; + std::vector> transportationDisappearances; for (Chunk *chunk : chnks) { for (const EntityRef otherRef : chunk->entities) { // skip oneself @@ -127,7 +246,28 @@ void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef re // notify this *player* of the departure of all visible Entities if (ref.kind == EntityKind::PLAYER && other->isExtant()) { - other->disappearFromViewOf(ref.sock); + int32_t id; + switch(otherRef.kind) + { + case EntityKind::PLAYER: + id = dynamic_cast(other)->iID; + bufferIdForDisappearance(pcDisappearances, id, MAX_IDS_PER_AROUND_DEL); + break; + case EntityKind::SIMPLE_NPC: + case EntityKind::COMBAT_NPC: + case EntityKind::MOB: + id = dynamic_cast(other)->id; + bufferIdForDisappearance(npcDisappearances, id, MAX_IDS_PER_AROUND_DEL); + break; + case EntityKind::EGG: + id = dynamic_cast(other)->id; + bufferIdForDisappearance(shinyDisappearances, id, MAX_IDS_PER_AROUND_DEL); + break; + case EntityKind::BUS: + id = dynamic_cast(other)->id; + bufferIdForDisappearance(transportationDisappearances, id, MAX_IDS_PER_AROUND_DEL - 1); + break; + } } // for mobs, decrement playersInView @@ -137,6 +277,18 @@ void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef re ((Mob*)other)->playersInView--; } } + + if (ref.kind != EntityKind::PLAYER) + return; // nothing to send + + if (!pcDisappearances.empty()) + sendAroundDelPacket(ref, pcDisappearances, false, P_FE2CL_AROUND_DEL_PC); + if (!npcDisappearances.empty()) + sendAroundDelPacket(ref, npcDisappearances, false, P_FE2CL_AROUND_DEL_NPC); + if (!shinyDisappearances.empty()) + sendAroundDelPacket(ref, shinyDisappearances, false, P_FE2CL_AROUND_DEL_SHINY); + if (!transportationDisappearances.empty()) + sendAroundDelPacket(ref, transportationDisappearances, true, P_FE2CL_AROUND_DEL_TRANSPORTATION); } static void emptyChunk(ChunkPos chunkPos) { diff --git a/src/Eggs.cpp b/src/Eggs.cpp index 38cd97e..fe5cf73 100644 --- a/src/Eggs.cpp +++ b/src/Eggs.cpp @@ -126,15 +126,6 @@ static void eggStep(CNServer* serv, time_t currTime) { } -void Eggs::npcDataToEggData(int x, int y, int z, sNPCAppearanceData* npc, sShinyAppearanceData* egg) { - egg->iX = x; - egg->iY = y; - egg->iZ = z; - // client doesn't care about egg->iMapNum - egg->iShinyType = npc->iNPCType; - egg->iShiny_ID = npc->iNPC_ID; -} - static void eggPickup(CNSocket* sock, CNPacketData* data) { auto pickup = (sP_CL2FE_REQ_SHINY_PICKUP*)data->buf; Player* plr = PlayerManager::getPlayer(sock); diff --git a/src/Eggs.hpp b/src/Eggs.hpp index 9c34ec9..8bb43a8 100644 --- a/src/Eggs.hpp +++ b/src/Eggs.hpp @@ -15,5 +15,4 @@ namespace Eggs { void init(); void eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration); - void npcDataToEggData(int x, int y, int z, sNPCAppearanceData* npc, sShinyAppearanceData* egg); } diff --git a/src/Entities.cpp b/src/Entities.cpp index 63a4a64..c3532de 100644 --- a/src/Entities.cpp +++ b/src/Entities.cpp @@ -70,11 +70,7 @@ void Bus::enterIntoViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_TRANSPORTATION_ENTER, pkt); // TODO: Potentially decouple this from BaseNPC? - pkt.AppearanceData = { - 3, id, type, - x, y, z - }; - + pkt.AppearanceData = getTransportationAppearanceData(); sock->sendPacket(pkt, P_FE2CL_TRANSPORTATION_ENTER); } @@ -82,12 +78,22 @@ void Egg::enterIntoViewOf(CNSocket *sock) { INITSTRUCT(sP_FE2CL_SHINY_ENTER, pkt); // TODO: Potentially decouple this from BaseNPC? - pkt.ShinyAppearanceData = { + pkt.ShinyAppearanceData = getShinyAppearanceData(); + sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER); +} + +sTransportationAppearanceData Bus::getTransportationAppearanceData() { + return sTransportationAppearanceData { + 3, id, type, + x, y, z + }; +} + +sShinyAppearanceData Egg::getShinyAppearanceData() { + return sShinyAppearanceData { id, type, 0, // client doesn't care about map num x, y, z }; - - sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER); } sNano* Player::getActiveNano() { diff --git a/src/Entities.hpp b/src/Entities.hpp index 22eb50a..5d55408 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -161,6 +161,8 @@ struct Egg : public BaseNPC { virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; + + sShinyAppearanceData getShinyAppearanceData(); }; struct Bus : public BaseNPC { @@ -172,4 +174,6 @@ struct Bus : public BaseNPC { virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; + + sTransportationAppearanceData getTransportationAppearanceData(); }; diff --git a/src/core/Packets.cpp b/src/core/Packets.cpp index 735c7ad..df9f919 100644 --- a/src/core/Packets.cpp +++ b/src/core/Packets.cpp @@ -235,7 +235,7 @@ std::map Packets::packets = { PACKET(P_FE2CL_REP_PC_EXIT_FAIL), PACKET(P_FE2CL_REP_PC_EXIT_SUCC), PACKET(P_FE2CL_PC_EXIT), - PACKET(P_FE2CL_PC_AROUND), + VAR_PACKET(P_FE2CL_PC_AROUND, iPCCnt, sPCAppearanceData), PACKET(P_FE2CL_PC_MOVE), PACKET(P_FE2CL_PC_STOP), PACKET(P_FE2CL_PC_JUMP), @@ -243,9 +243,9 @@ std::map Packets::packets = { PACKET(P_FE2CL_NPC_EXIT), PACKET(P_FE2CL_NPC_MOVE), PACKET(P_FE2CL_NPC_NEW), - PACKET(P_FE2CL_NPC_AROUND), - PACKET(P_FE2CL_AROUND_DEL_PC), - PACKET(P_FE2CL_AROUND_DEL_NPC), + VAR_PACKET(P_FE2CL_NPC_AROUND, iNPCCnt, sNPCAppearanceData), + VAR_PACKET(P_FE2CL_AROUND_DEL_PC, iPCCnt, int32_t), + VAR_PACKET(P_FE2CL_AROUND_DEL_NPC, iNPCCnt, int32_t), PACKET(P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC), PACKET(P_FE2CL_REP_SEND_FREECHAT_MESSAGE_FAIL), VAR_PACKET(P_FE2CL_PC_ATTACK_NPCs_SUCC, iNPCCnt, sAttackResult), @@ -387,8 +387,8 @@ std::map Packets::packets = { PACKET(P_FE2CL_TRANSPORTATION_EXIT), PACKET(P_FE2CL_TRANSPORTATION_MOVE), PACKET(P_FE2CL_TRANSPORTATION_NEW), - PACKET(P_FE2CL_TRANSPORTATION_AROUND), - PACKET(P_FE2CL_AROUND_DEL_TRANSPORTATION), + VAR_PACKET(P_FE2CL_TRANSPORTATION_AROUND, iCnt, sTransportationAppearanceData), + VAR_PACKET(P_FE2CL_AROUND_DEL_TRANSPORTATION, iCnt, int32_t), PACKET(P_FE2CL_REP_EP_RANK_LIST), PACKET(P_FE2CL_REP_EP_RANK_DETAIL), PACKET(P_FE2CL_REP_EP_RANK_PC_INFO), @@ -404,8 +404,8 @@ std::map Packets::packets = { PACKET(P_FE2CL_SHINY_ENTER), PACKET(P_FE2CL_SHINY_EXIT), PACKET(P_FE2CL_SHINY_NEW), - PACKET(P_FE2CL_SHINY_AROUND), - PACKET(P_FE2CL_AROUND_DEL_SHINY), + VAR_PACKET(P_FE2CL_SHINY_AROUND, iShinyCnt, sShinyAppearanceData), + VAR_PACKET(P_FE2CL_AROUND_DEL_SHINY, iShinyCnt, int32_t), PACKET(P_FE2CL_REP_SHINY_PICKUP_FAIL), PACKET(P_FE2CL_REP_SHINY_PICKUP_SUCC), PACKET(P_FE2CL_PC_MOVETRANSPORTATION),