From 83ba121f5fffc1b91a0d90689a6fda1fe3680023 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Sun, 27 Oct 2024 00:33:14 -0700 Subject: [PATCH 1/6] Use FE2CL_..._AROUND, _AROUND_DEL packets --- src/Chunking.cpp | 160 +++++++++++++++++++++++++++++++++++++++++-- src/Eggs.cpp | 9 --- src/Eggs.hpp | 1 - src/Entities.cpp | 22 +++--- src/Entities.hpp | 4 ++ src/core/Packets.cpp | 16 ++--- 6 files changed, 182 insertions(+), 30 deletions(-) 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 73f22f6..8cb0912 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), From 95181b105865fecadd99979ad99e89dee89cbb69 Mon Sep 17 00:00:00 2001 From: gsemaj Date: Sun, 27 Oct 2024 00:39:49 -0700 Subject: [PATCH 2/6] Fix build --- src/Chunking.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Chunking.cpp b/src/Chunking.cpp index 4861ad5..61fc883 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -173,31 +173,37 @@ 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()) { + sPCAppearanceData pcData; + sNPCAppearanceData npcData; + sShinyAppearanceData eggData; + sTransportationAppearanceData busData; switch(otherRef.kind) { case EntityKind::PLAYER: - sPCAppearanceData pcData = dynamic_cast(other)->getAppearanceData(); + pcData = dynamic_cast(other)->getAppearanceData(); bufferAppearanceData(pcAppearances, pcData, MAX_PC_PER_AROUND); break; case EntityKind::SIMPLE_NPC: - sNPCAppearanceData npcData = dynamic_cast(other)->getAppearanceData(); + 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); + npcData = dynamic_cast(other)->getAppearanceData(); + bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); break; case EntityKind::MOB: - sNPCAppearanceData mobData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, mobData, MAX_NPC_PER_AROUND); + npcData = dynamic_cast(other)->getAppearanceData(); + bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); break; case EntityKind::EGG: - sShinyAppearanceData shinyData = dynamic_cast(other)->getShinyAppearanceData(); - bufferAppearanceData(shinyAppearances, shinyData, MAX_SHINY_PER_AROUND); + eggData = dynamic_cast(other)->getShinyAppearanceData(); + bufferAppearanceData(shinyAppearances, eggData, MAX_SHINY_PER_AROUND); break; case EntityKind::BUS: - sTransportationAppearanceData transportationData = dynamic_cast(other)->getTransportationAppearanceData(); - bufferAppearanceData(transportationAppearances, transportationData, MAX_TRANSPORTATION_PER_AROUND); + busData = dynamic_cast(other)->getTransportationAppearanceData(); + bufferAppearanceData(transportationAppearances, busData, MAX_TRANSPORTATION_PER_AROUND); + break; + default: break; } } @@ -267,6 +273,8 @@ void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef re id = dynamic_cast(other)->id; bufferIdForDisappearance(transportationDisappearances, id, MAX_IDS_PER_AROUND_DEL - 1); break; + default: + break; } } From bfa3a8c04ee0ae85b1b5aa7d519f5d0025b78cf0 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Sun, 27 Oct 2024 01:25:34 -0700 Subject: [PATCH 3/6] Use increased buffer size for 728 and 1013 protocols --- src/core/Defines.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/Defines.hpp b/src/core/Defines.hpp index 7939d14..cb14b29 100644 --- a/src/core/Defines.hpp +++ b/src/core/Defines.hpp @@ -1,6 +1,8 @@ /* enum definitions from the client */ #pragma once +#include "core/CNStructs.hpp" + // floats const float VALUE_BATTERY_EMPTY_PENALTY = 0.5f; const float CN_EP_RANK_1 = 0.8f; @@ -410,7 +412,13 @@ enum { SEND_ANYCAST_NEW = 3, SEND_BROADCAST = 4, +#if PROTOCOL_VERSION == 728 + CN_PACKET_BUFFER_SIZE = 8192, +#elif PROTOCOL_VERSION == 1013 + CN_PACKET_BUFFER_SIZE = 8192, +#else CN_PACKET_BUFFER_SIZE = 4096, +#endif P_CL2LS_REQ_LOGIN = 0x12000001, // 301989889 P_CL2LS_REQ_CHECK_CHAR_NAME = 0x12000002, // 301989890 From 02a563214791e388f778939a881c61ccc51c4cfe Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Sun, 27 Oct 2024 01:50:45 -0700 Subject: [PATCH 4/6] Get rid of unnecessary templating --- src/Chunking.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Chunking.cpp b/src/Chunking.cpp index 61fc883..afdb298 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -140,14 +140,13 @@ static void bufferAppearanceData(std::vector>& slices, const T& d slices.push_back(std::vector()); } -template -static void bufferIdForDisappearance(std::vector>& slices, int32_t id, size_t maxCnt) { +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]; + 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()); + slices.push_back(std::vector()); } void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { From 91589b18c119af65b6995a8437d61c5dbb1bdb77 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Sun, 27 Oct 2024 11:55:06 -0700 Subject: [PATCH 5/6] More cleanup --- src/Chunking.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Chunking.cpp b/src/Chunking.cpp index afdb298..06ba53f 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -95,12 +95,11 @@ static void sendAroundPacket(const EntityRef recipient, std::vectorsendPacket(pktBuf, packetId, 4 + (count * sizeof(T))); + recipient.sock->sendPacket(pktBuf, packetId, sizeof(int32_t) + (count * sizeof(T))); } } -template -static void sendAroundDelPacket(const EntityRef recipient, std::vector>& slices, bool isTransportation, uint32_t packetId) { +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; @@ -113,20 +112,22 @@ static void sendAroundDelPacket(const EntityRef recipient, std::vectoreTT = 3; + pkt->iCnt = count; + baseSize = sizeof(sP_FE2CL_AROUND_DEL_TRANSPORTATION); } else { *((int32_t*)pktBuf) = count; - data = (T*)(pktBuf + sizeof(int32_t)); + baseSize = sizeof(int32_t); } + int32_t* ids = (int32_t*)(pktBuf + baseSize); for (size_t i = 0; i < count; i++) { - data[i] = slice[i]; + ids[i] = slice[i]; } - recipient.sock->sendPacket(pktBuf, packetId, isTransportation ? 8 : 4 + (count * sizeof(T))); + recipient.sock->sendPacket(pktBuf, packetId, baseSize + (count * sizeof(int32_t))); } } From 73aab0b5ba2c7aa198161fe195ee6ad00c7b86c9 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Mon, 28 Oct 2024 12:56:20 -0700 Subject: [PATCH 6/6] Optimize performance Introduces a generic, fixed-size `Bucket` type in place of the inner vector. This does add size generics but I think it's a very good tradeoff considering how performance-sensitive this codepath is. --- Makefile | 1 + src/Bucket.hpp | 39 ++++++++++++++++ src/Chunking.cpp | 114 +++++++++++++++++++++++------------------------ 3 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 src/Bucket.hpp diff --git a/Makefile b/Makefile index f94e9af..b6541dc 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,7 @@ CXXHDR=\ src/settings.hpp\ src/Transport.hpp\ src/TableData.hpp\ + src/Bucket.hpp\ src/Chunking.hpp\ src/Buddies.hpp\ src/Groups.hpp\ diff --git a/src/Bucket.hpp b/src/Bucket.hpp new file mode 100644 index 0000000..fc3f52b --- /dev/null +++ b/src/Bucket.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +template +class Bucket { + std::array buf; + size_t sz; +public: + Bucket() { + sz = 0; + } + + void add(const T& item) { + if (sz < N) { + buf[sz++] = item; + } + } + + std::optional get(size_t idx) const { + if (idx < sz) { + return buf[idx]; + } + return std::nullopt; + } + + size_t size() const { + return sz; + } + + bool isFull() const { + return sz == N; + } + + void clear() { + sz = 0; + } +}; diff --git a/src/Chunking.cpp b/src/Chunking.cpp index 06ba53f..fec1cdc 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -3,6 +3,7 @@ #include "Player.hpp" #include "MobAI.hpp" #include "NPCManager.hpp" +#include "Bucket.hpp" #include @@ -12,11 +13,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); +constexpr size_t MAX_PC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sPCAppearanceData); +constexpr size_t MAX_NPC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sNPCAppearanceData); +constexpr size_t MAX_SHINY_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sShinyAppearanceData); +constexpr size_t MAX_TRANSPORTATION_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sTransportationAppearanceData); +constexpr size_t MAX_IDS_PER_AROUND_DEL = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(int32_t); std::map Chunking::chunks; @@ -81,36 +82,32 @@ 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) { +template +static void sendAroundPacket(const EntityRef recipient, std::vector>& buckets, uint32_t packetId) { assert(recipient.kind == EntityKind::PLAYER); uint8_t pktBuf[CN_PACKET_BUFFER_SIZE]; - for (const auto& slice : slices) { + for (const auto& bucket : buckets) { memset(pktBuf, 0, CN_PACKET_BUFFER_SIZE); - int count = slice.size(); - assert(count <= maxCnt); + int count = bucket.size(); *((int32_t*)pktBuf) = count; T* data = (T*)(pktBuf + sizeof(int32_t)); for (size_t i = 0; i < count; i++) { - data[i] = slice[i]; + data[i] = bucket.get(i).value(); } recipient.sock->sendPacket(pktBuf, packetId, sizeof(int32_t) + (count * sizeof(T))); } } -static void sendAroundDelPacket(const EntityRef recipient, std::vector>& slices, bool isTransportation, uint32_t packetId) { +template +static void sendAroundDelPacket(const EntityRef recipient, std::vector>& buckets, 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) { + for (const auto& bucket : buckets) { memset(pktBuf, 0, CN_PACKET_BUFFER_SIZE); - int count = slice.size(); - assert(count <= maxCnt); + int count = bucket.size(); + assert(count <= N); size_t baseSize; if (isTransportation) { @@ -125,39 +122,42 @@ static void sendAroundDelPacket(const EntityRef recipient, std::vectorsendPacket(pktBuf, packetId, baseSize + (count * sizeof(int32_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 bufferAppearanceData(std::vector>& buckets, const T& data) { + if (buckets.empty()) + buckets.push_back(Bucket()); + Bucket& bucket = buckets[buckets.size() - 1]; + assert(!bucket.isFull()); + bucket.add(data); + if (bucket.isFull()) + buckets.push_back(Bucket()); } -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()); +template +static void bufferIdForDisappearance(std::vector>& buckets, int32_t id) { + if (buckets.empty()) + buckets.push_back(Bucket()); + Bucket& bucket = buckets[buckets.size() - 1]; + assert(!bucket.isFull()); + bucket.add(id); + if (bucket.isFull()) + buckets.push_back(Bucket()); } void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { Entity *ent = ref.getEntity(); bool alive = ent->isExtant(); - std::vector> pcAppearances; - std::vector> npcAppearances; - std::vector> shinyAppearances; - std::vector> transportationAppearances; + std::vector> pcAppearances; + std::vector> npcAppearances; + std::vector> shinyAppearances; + std::vector> transportationAppearances; for (Chunk *chunk : chnks) { for (const EntityRef otherRef : chunk->entities) { // skip oneself @@ -181,27 +181,27 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { { case EntityKind::PLAYER: pcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(pcAppearances, pcData, MAX_PC_PER_AROUND); + bufferAppearanceData(pcAppearances, pcData); break; case EntityKind::SIMPLE_NPC: npcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + bufferAppearanceData(npcAppearances, npcData); break; case EntityKind::COMBAT_NPC: npcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + bufferAppearanceData(npcAppearances, npcData); break; case EntityKind::MOB: npcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + bufferAppearanceData(npcAppearances, npcData); break; case EntityKind::EGG: eggData = dynamic_cast(other)->getShinyAppearanceData(); - bufferAppearanceData(shinyAppearances, eggData, MAX_SHINY_PER_AROUND); + bufferAppearanceData(shinyAppearances, eggData); break; case EntityKind::BUS: busData = dynamic_cast(other)->getTransportationAppearanceData(); - bufferAppearanceData(transportationAppearances, busData, MAX_TRANSPORTATION_PER_AROUND); + bufferAppearanceData(transportationAppearances, busData); break; default: break; @@ -220,23 +220,23 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { return; // nothing to send if (!pcAppearances.empty()) - sendAroundPacket(ref, pcAppearances, MAX_PC_PER_AROUND, P_FE2CL_PC_AROUND); + sendAroundPacket(ref, pcAppearances, P_FE2CL_PC_AROUND); if (!npcAppearances.empty()) - sendAroundPacket(ref, npcAppearances, MAX_NPC_PER_AROUND, P_FE2CL_NPC_AROUND); + sendAroundPacket(ref, npcAppearances, P_FE2CL_NPC_AROUND); if (!shinyAppearances.empty()) - sendAroundPacket(ref, shinyAppearances, MAX_SHINY_PER_AROUND, P_FE2CL_SHINY_AROUND); + sendAroundPacket(ref, shinyAppearances, P_FE2CL_SHINY_AROUND); if (!transportationAppearances.empty()) - sendAroundPacket(ref, transportationAppearances, MAX_TRANSPORTATION_PER_AROUND, P_FE2CL_TRANSPORTATION_AROUND); + sendAroundPacket(ref, transportationAppearances, P_FE2CL_TRANSPORTATION_AROUND); } void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef ref) { Entity *ent = ref.getEntity(); bool alive = ent->isExtant(); - std::vector> pcDisappearances; - std::vector> npcDisappearances; - std::vector> shinyDisappearances; - std::vector> transportationDisappearances; + std::vector> pcDisappearances; + std::vector> npcDisappearances; + std::vector> shinyDisappearances; + std::vector> transportationDisappearances; for (Chunk *chunk : chnks) { for (const EntityRef otherRef : chunk->entities) { // skip oneself @@ -257,21 +257,21 @@ void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef re { case EntityKind::PLAYER: id = dynamic_cast(other)->iID; - bufferIdForDisappearance(pcDisappearances, id, MAX_IDS_PER_AROUND_DEL); + bufferIdForDisappearance(pcDisappearances, id); 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); + bufferIdForDisappearance(npcDisappearances, id); break; case EntityKind::EGG: id = dynamic_cast(other)->id; - bufferIdForDisappearance(shinyDisappearances, id, MAX_IDS_PER_AROUND_DEL); + bufferIdForDisappearance(shinyDisappearances, id); break; case EntityKind::BUS: id = dynamic_cast(other)->id; - bufferIdForDisappearance(transportationDisappearances, id, MAX_IDS_PER_AROUND_DEL - 1); + bufferIdForDisappearance(transportationDisappearances, id); break; default: break;