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
This commit is contained in:
dongresource 2022-07-22 02:40:33 +02:00
parent 1bd4d2fbee
commit 129d1c2fe3
9 changed files with 46 additions and 27 deletions

View File

@ -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<ChunkPos, Chunk*> 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<Chunk*> Chunking::getViewableChunks(ChunkPos chunk) {
@ -213,7 +218,7 @@ std::set<Chunk*> 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))

View File

@ -1,7 +1,6 @@
#pragma once
#include "core/Core.hpp"
#include "Entities.hpp"
#include <utility>
#include <set>
@ -9,14 +8,23 @@
#include <tuple>
#include <algorithm>
struct EntityRef;
class Chunk {
public:
//std::set<CNSocket*> players;
//std::set<int32_t> NPCs;
std::set<EntityRef> entities;
int nplayers = 0;
};
// to help the readability of ChunkPos
typedef std::tuple<int, int, uint64_t> _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<ChunkPos, Chunk*> chunks;
extern const ChunkPos INVALID_CHUNK;
void updateEntityChunk(const EntityRef& ref, ChunkPos from, ChunkPos to);
void trackEntity(ChunkPos chunkPos, const EntityRef& ref);

View File

@ -378,12 +378,9 @@ static void npcRotateCommand(std::string full, std::vector<std::string>& args, C
}
static void refreshCommand(std::string full, std::vector<std::string>& 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<std::string>& args, CNSocket* sock) {

View File

@ -1,6 +1,7 @@
#pragma once
#include "core/Core.hpp"
#include "Chunking.hpp"
#include <stdint.h>
#include <set>
@ -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;

View File

@ -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())

View File

@ -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) {

View File

@ -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);

View File

@ -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<Vec3>* route) {

View File

@ -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<int, int, uint64_t> ChunkPos;
// TODO: rewrite U16toU8 & U8toU16 to not use codecvt
std::string U16toU8(char16_t* src, size_t max);