From 129d1c2fe3871199a9a705a3fd87d0b69ec92ea7 Mon Sep 17 00:00:00 2001 From: dongresource Date: Fri, 22 Jul 2022 02:40:33 +0200 Subject: [PATCH] Use a specialized null value for ChunkPos This prevents logic errors related to being in chunk 0 0 0. Also: * Moved some duplicated chunk teleportation logic to a new helper function * Made ChunkPos into a proper class so it can default to INVALID_CHUNK when default-initialized * Reversed the inclusion order of Chunking.hpp and Entities.hpp to work around problems with type definitions --- src/Chunking.cpp | 13 +++++++++---- src/Chunking.hpp | 16 +++++++++++++--- src/CustomCommands.cpp | 9 +++------ src/Entities.hpp | 3 +-- src/NPCManager.cpp | 3 +-- src/PlayerManager.cpp | 21 ++++++++++++++++----- src/PlayerManager.hpp | 1 + src/Transport.cpp | 4 ++-- src/core/CNStructs.hpp | 3 --- 9 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/Chunking.cpp b/src/Chunking.cpp index cc620f3..3d3330a 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -7,11 +7,16 @@ using namespace Chunking; +/* + * The initial chunkPos value before a player is placed into the world. + */ +const ChunkPos Chunking::INVALID_CHUNK = {}; + std::map Chunking::chunks; static void newChunk(ChunkPos pos) { if (chunkExists(pos)) { - std::cout << "[WARN] Tried to create a chunk that already exists\n"; + std::cout << "[WARN] Tried to create a chunk that already exists" << std::endl; return; } @@ -27,7 +32,7 @@ static void newChunk(ChunkPos pos) { static void deleteChunk(ChunkPos pos) { if (!chunkExists(pos)) { - std::cout << "[WARN] Tried to delete a chunk that doesn't exist\n"; + std::cout << "[WARN] Tried to delete a chunk that doesn't exist" << std::endl; return; } @@ -200,7 +205,7 @@ bool Chunking::chunkExists(ChunkPos chunk) { } ChunkPos Chunking::chunkPosAt(int posX, int posY, uint64_t instanceID) { - return std::make_tuple(posX / (settings::VIEWDISTANCE / 3), posY / (settings::VIEWDISTANCE / 3), instanceID); + return ChunkPos(posX / (settings::VIEWDISTANCE / 3), posY / (settings::VIEWDISTANCE / 3), instanceID); } std::set Chunking::getViewableChunks(ChunkPos chunk) { @@ -213,7 +218,7 @@ std::set Chunking::getViewableChunks(ChunkPos chunk) { // grabs surrounding chunks if they exist for (int i = -1; i < 2; i++) { for (int z = -1; z < 2; z++) { - ChunkPos pos = std::make_tuple(x+i, y+z, inst); + ChunkPos pos = ChunkPos(x+i, y+z, inst); // if chunk exists, add it to the set if (chunkExists(pos)) diff --git a/src/Chunking.hpp b/src/Chunking.hpp index 3d716a8..61ead49 100644 --- a/src/Chunking.hpp +++ b/src/Chunking.hpp @@ -1,7 +1,6 @@ #pragma once #include "core/Core.hpp" -#include "Entities.hpp" #include #include @@ -9,14 +8,23 @@ #include #include +struct EntityRef; + class Chunk { public: - //std::set players; - //std::set NPCs; std::set entities; int nplayers = 0; }; +// to help the readability of ChunkPos +typedef std::tuple _ChunkPos; + +class ChunkPos : public _ChunkPos { +public: + ChunkPos() : _ChunkPos(0, 0, (uint64_t) -1) {} + ChunkPos(int x, int y, uint64_t inst) : _ChunkPos(x, y, inst) {} +}; + enum { INSTANCE_OVERWORLD, // default instance every player starts in INSTANCE_IZ, // these aren't actually used @@ -26,6 +34,8 @@ enum { namespace Chunking { extern std::map chunks; + extern const ChunkPos INVALID_CHUNK; + void updateEntityChunk(const EntityRef& ref, ChunkPos from, ChunkPos to); void trackEntity(ChunkPos chunkPos, const EntityRef& ref); diff --git a/src/CustomCommands.cpp b/src/CustomCommands.cpp index cb8f6ae..06d9d69 100644 --- a/src/CustomCommands.cpp +++ b/src/CustomCommands.cpp @@ -378,12 +378,9 @@ static void npcRotateCommand(std::string full, std::vector& args, C } static void refreshCommand(std::string full, std::vector& args, CNSocket* sock) { - EntityRef ref = {sock}; - Entity* plr = ref.getEntity(); - ChunkPos currentChunk = plr->chunkPos; - ChunkPos nullChunk = std::make_tuple(0, 0, 0); - Chunking::updateEntityChunk(ref, currentChunk, nullChunk); - Chunking::updateEntityChunk(ref, nullChunk, currentChunk); + Player *plr = PlayerManager::getPlayer(sock); + + PlayerManager::updatePlayerPositionForWarp(sock, plr->x, plr->y, plr->z, plr->instanceID); } static void instanceCommand(std::string full, std::vector& args, CNSocket* sock) { diff --git a/src/Entities.hpp b/src/Entities.hpp index 9c98379..4975279 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -1,6 +1,7 @@ #pragma once #include "core/Core.hpp" +#include "Chunking.hpp" #include #include @@ -15,8 +16,6 @@ enum class EntityType : uint8_t { BUS }; -class Chunk; - struct Entity { EntityType type = EntityType::INVALID; int x = 0, y = 0, z = 0; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 50b84fc..901bfb3 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -246,8 +246,7 @@ static void handleWarp(CNSocket* sock, int32_t warpId) { Missions::failInstancedMissions(sock); // fail any instanced missions sock->sendPacket(resp, P_FE2CL_REP_PC_WARP_USE_NPC_SUCC); - Chunking::updateEntityChunk({sock}, plr->chunkPos, std::make_tuple(0, 0, 0)); // force player to reload chunks - PlayerManager::updatePlayerPosition(sock, resp.iX, resp.iY, resp.iZ, INSTANCE_OVERWORLD, plr->angle); + PlayerManager::updatePlayerPositionForWarp(sock, resp.iX, resp.iY, resp.iZ, INSTANCE_OVERWORLD); // remove the player's ongoing race, if any if (Racing::EPRaces.find(sock) != Racing::EPRaces.end()) diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 32e2b75..9b080f5 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -35,7 +35,7 @@ static void addPlayer(CNSocket* key, Player plr) { *p = plr; players[key] = p; - p->chunkPos = std::make_tuple(0, 0, 0); // TODO: maybe replace with specialized "no chunk" value + p->chunkPos = Chunking::INVALID_CHUNK; p->lastHeartbeat = 0; std::cout << getPlayerName(p) << " has joined!" << std::endl; @@ -97,6 +97,19 @@ void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, ui Chunking::updateEntityChunk({sock}, oldChunk, newChunk); } +/* + * Low-level helper function for correctly updating chunks when teleporting players. + * + * Use PlayerManager::sendPlayerTo() to actually teleport players. + */ +void PlayerManager::updatePlayerPositionForWarp(CNSocket* sock, int X, int Y, int Z, uint64_t inst) { + Player *plr = getPlayer(sock); + + // force player to reload chunks + Chunking::updateEntityChunk({sock}, plr->chunkPos, Chunking::INVALID_CHUNK); + updatePlayerPosition(sock, X, Y, Z, inst, plr->angle); +} + void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I) { Player* plr = getPlayer(sock); plr->onMonkey = false; @@ -148,8 +161,7 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I pkt2.iZ = Z; sock->sendPacket(pkt2, P_FE2CL_REP_PC_GOTO_SUCC); - Chunking::updateEntityChunk({sock}, plr->chunkPos, std::make_tuple(0, 0, 0)); // force player to reload chunks - updatePlayerPosition(sock, X, Y, Z, I, plr->angle); + updatePlayerPositionForWarp(sock, X, Y, Z, I); // post-warp: check if the source instance has no more players in it and delete it if so Chunking::destroyInstanceIfEmpty(fromInstance); @@ -466,8 +478,7 @@ static void revivePlayer(CNSocket* sock, CNPacketData* data) { if (!move) return; - Chunking::updateEntityChunk({sock}, plr->chunkPos, std::make_tuple(0, 0, 0)); // force player to reload chunks - updatePlayerPosition(sock, x, y, z, plr->instanceID, plr->angle); + updatePlayerPositionForWarp(sock, x, y, z, plr->instanceID); } static void enterPlayerVehicle(CNSocket* sock, CNPacketData* data) { diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index a32bd62..0ab2049 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -18,6 +18,7 @@ namespace PlayerManager { void removePlayer(CNSocket* key); void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z, uint64_t I, int angle); + void updatePlayerPositionForWarp(CNSocket* sock, int X, int Y, int Z, uint64_t inst); 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/Transport.cpp b/src/Transport.cpp index ea9cdcc..0fe4f10 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -165,9 +165,9 @@ static void transportWarpHandler(CNSocket* sock, CNPacketData* data) { if (target == nullptr) return; + // we warped; update position and chunks - Chunking::updateEntityChunk({sock}, plr->chunkPos, std::make_tuple(0, 0, 0)); // force player to reload chunks - PlayerManager::updatePlayerPosition(sock, target->x, target->y, target->z, INSTANCE_OVERWORLD, plr->angle); + PlayerManager::updatePlayerPositionForWarp(sock, target->x, target->y, target->z, INSTANCE_OVERWORLD); } void Transport::testMssRoute(CNSocket *sock, std::vector* route) { diff --git a/src/core/CNStructs.hpp b/src/core/CNStructs.hpp index b760ccd..fbadff2 100644 --- a/src/core/CNStructs.hpp +++ b/src/core/CNStructs.hpp @@ -41,9 +41,6 @@ #define ARRLEN(x) (sizeof(x)/sizeof(*x)) #define AUTOU16TOU8(x) U16toU8(x, ARRLEN(x)) -// typedef for chunk position tuple -typedef std::tuple ChunkPos; - // TODO: rewrite U16toU8 & U8toU16 to not use codecvt std::string U16toU8(char16_t* src, size_t max);