Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use FE2CL_..._AROUND, _AROUND_DEL packets #295

Merged
merged 7 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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\
Expand Down
40 changes: 40 additions & 0 deletions src/Bucket.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include <array>
#include <optional>

#include <assert.h>

template<class T, size_t N>
class Bucket {
yungcomputerchair marked this conversation as resolved.
Show resolved Hide resolved
std::array<T, N> buf;
size_t sz;
public:
Bucket() {
sz = 0;
}

void add(const T& item) {
assert(sz < N);
buf[sz++] = item;
}

std::optional<T> 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;
}
};
163 changes: 159 additions & 4 deletions src/Chunking.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "Chunking.hpp"

#include "Player.hpp"
#include "MobAI.hpp"
#include "NPCManager.hpp"
#include "Bucket.hpp"

#include <assert.h>

Expand All @@ -11,6 +13,12 @@ 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_BODY_SIZE - sizeof(int32_t)) / sizeof(sPCAppearanceData);
constexpr size_t MAX_NPC_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sNPCAppearanceData);
constexpr size_t MAX_SHINY_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sShinyAppearanceData);
constexpr size_t MAX_TRANSPORTATION_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sTransportationAppearanceData);
constexpr size_t MAX_IDS_PER_AROUND_DEL = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(int32_t);
constexpr size_t MAX_TRANSPORTATION_IDS_PER_AROUND_DEL = MAX_IDS_PER_AROUND_DEL - 1; // 1 less for eTT

std::map<ChunkPos, Chunk*> Chunking::chunks;

Expand Down Expand Up @@ -75,11 +83,80 @@ void Chunking::untrackEntity(ChunkPos chunkPos, const EntityRef ref) {
deleteChunk(chunkPos);
}

template<class T, size_t N>
static void sendAroundPackets(const EntityRef recipient, std::vector<Bucket<T, N>>& buckets, uint32_t packetId) {
assert(recipient.kind == EntityKind::PLAYER);

uint8_t pktBuf[CN_PACKET_BODY_SIZE];
for (const auto& bucket : buckets) {
memset(pktBuf, 0, CN_PACKET_BODY_SIZE);
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] = bucket.get(i).value();
}
recipient.sock->sendPacket(pktBuf, packetId, sizeof(int32_t) + (count * sizeof(T)));
}
}

template<size_t N>
static void sendAroundDelPackets(const EntityRef recipient, std::vector<Bucket<int32_t, N>>& buckets, uint32_t packetId) {
assert(recipient.kind == EntityKind::PLAYER);

uint8_t pktBuf[CN_PACKET_BODY_SIZE];
for (const auto& bucket : buckets) {
memset(pktBuf, 0, CN_PACKET_BODY_SIZE);
int count = bucket.size();
assert(count <= N);

size_t baseSize;
if (packetId == P_FE2CL_AROUND_DEL_TRANSPORTATION) {
sP_FE2CL_AROUND_DEL_TRANSPORTATION* pkt = (sP_FE2CL_AROUND_DEL_TRANSPORTATION*)pktBuf;
pkt->eTT = 3;
pkt->iCnt = count;
baseSize = sizeof(sP_FE2CL_AROUND_DEL_TRANSPORTATION);
} else {
*((int32_t*)pktBuf) = count;
baseSize = sizeof(int32_t);
}
int32_t* ids = (int32_t*)(pktBuf + baseSize);

for (size_t i = 0; i < count; i++) {
ids[i] = bucket.get(i).value();
}
recipient.sock->sendPacket(pktBuf, packetId, baseSize + (count * sizeof(int32_t)));
}
}

template<class T, size_t N>
static void bufferAppearanceData(std::vector<Bucket<T, N>>& buckets, const T& data) {
if (buckets.empty())
buckets.push_back({});
auto& bucket = buckets[buckets.size() - 1];
bucket.add(data);
if (bucket.isFull())
buckets.push_back({});
}

template<size_t N>
static void bufferIdForDisappearance(std::vector<Bucket<int32_t, N>>& buckets, int32_t id) {
if (buckets.empty())
buckets.push_back({});
auto& bucket = buckets[buckets.size() - 1];
bucket.add(id);
if (bucket.isFull())
buckets.push_back({});
}

void Chunking::addEntityToChunks(std::set<Chunk*> chnks, const EntityRef ref) {
Entity *ent = ref.getEntity();
bool alive = ent->isExtant();

// TODO: maybe optimize this, potentially using AROUND packets?
std::vector<Bucket<sPCAppearanceData, MAX_PC_PER_AROUND>> pcAppearances;
std::vector<Bucket<sNPCAppearanceData, MAX_NPC_PER_AROUND>> npcAppearances;
std::vector<Bucket<sShinyAppearanceData, MAX_SHINY_PER_AROUND>> shinyAppearances;
std::vector<Bucket<sTransportationAppearanceData, MAX_TRANSPORTATION_PER_AROUND>> transportationAppearances;
for (Chunk *chunk : chnks) {
for (const EntityRef otherRef : chunk->entities) {
// skip oneself
Expand All @@ -95,7 +172,38 @@ void Chunking::addEntityToChunks(std::set<Chunk*> 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);
sPCAppearanceData pcData;
sNPCAppearanceData npcData;
sShinyAppearanceData eggData;
sTransportationAppearanceData busData;
switch(otherRef.kind) {
case EntityKind::PLAYER:
pcData = dynamic_cast<Player*>(other)->getAppearanceData();
bufferAppearanceData(pcAppearances, pcData);
break;
case EntityKind::SIMPLE_NPC:
npcData = dynamic_cast<BaseNPC*>(other)->getAppearanceData();
bufferAppearanceData(npcAppearances, npcData);
break;
case EntityKind::COMBAT_NPC:
npcData = dynamic_cast<CombatNPC*>(other)->getAppearanceData();
bufferAppearanceData(npcAppearances, npcData);
break;
case EntityKind::MOB:
npcData = dynamic_cast<Mob*>(other)->getAppearanceData();
bufferAppearanceData(npcAppearances, npcData);
break;
case EntityKind::EGG:
eggData = dynamic_cast<Egg*>(other)->getShinyAppearanceData();
bufferAppearanceData(shinyAppearances, eggData);
break;
case EntityKind::BUS:
busData = dynamic_cast<Bus*>(other)->getTransportationAppearanceData();
bufferAppearanceData(transportationAppearances, busData);
break;
default:
break;
}
}

// for mobs, increment playersInView
Expand All @@ -105,13 +213,27 @@ void Chunking::addEntityToChunks(std::set<Chunk*> chnks, const EntityRef ref) {
((Mob*)other)->playersInView++;
}
}

if (ref.kind == EntityKind::PLAYER) {
if (!pcAppearances.empty())
sendAroundPackets(ref, pcAppearances, P_FE2CL_PC_AROUND);
if (!npcAppearances.empty())
sendAroundPackets(ref, npcAppearances, P_FE2CL_NPC_AROUND);
if (!shinyAppearances.empty())
sendAroundPackets(ref, shinyAppearances, P_FE2CL_SHINY_AROUND);
if (!transportationAppearances.empty())
sendAroundPackets(ref, transportationAppearances, P_FE2CL_TRANSPORTATION_AROUND);
}
}

void Chunking::removeEntityFromChunks(std::set<Chunk*> chnks, const EntityRef ref) {
Entity *ent = ref.getEntity();
bool alive = ent->isExtant();

// TODO: same as above
std::vector<Bucket<int32_t, MAX_IDS_PER_AROUND_DEL>> pcDisappearances;
std::vector<Bucket<int32_t, MAX_IDS_PER_AROUND_DEL>> npcDisappearances;
std::vector<Bucket<int32_t, MAX_IDS_PER_AROUND_DEL>> shinyDisappearances;
std::vector<Bucket<int32_t, MAX_TRANSPORTATION_IDS_PER_AROUND_DEL>> transportationDisappearances;
for (Chunk *chunk : chnks) {
for (const EntityRef otherRef : chunk->entities) {
// skip oneself
Expand All @@ -127,7 +249,29 @@ void Chunking::removeEntityFromChunks(std::set<Chunk*> 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<Player*>(other)->iID;
bufferIdForDisappearance(pcDisappearances, id);
break;
case EntityKind::SIMPLE_NPC:
case EntityKind::COMBAT_NPC:
case EntityKind::MOB:
id = dynamic_cast<BaseNPC*>(other)->id;
bufferIdForDisappearance(npcDisappearances, id);
break;
case EntityKind::EGG:
id = dynamic_cast<Egg*>(other)->id;
bufferIdForDisappearance(shinyDisappearances, id);
break;
case EntityKind::BUS:
id = dynamic_cast<Bus*>(other)->id;
bufferIdForDisappearance(transportationDisappearances, id);
break;
default:
break;
}
}

// for mobs, decrement playersInView
Expand All @@ -137,6 +281,17 @@ void Chunking::removeEntityFromChunks(std::set<Chunk*> chnks, const EntityRef re
((Mob*)other)->playersInView--;
}
}

if (ref.kind == EntityKind::PLAYER) {
if (!pcDisappearances.empty())
sendAroundDelPackets(ref, pcDisappearances, P_FE2CL_AROUND_DEL_PC);
if (!npcDisappearances.empty())
sendAroundDelPackets(ref, npcDisappearances, P_FE2CL_AROUND_DEL_NPC);
if (!shinyDisappearances.empty())
sendAroundDelPackets(ref, shinyDisappearances, P_FE2CL_AROUND_DEL_SHINY);
if (!transportationDisappearances.empty())
sendAroundDelPackets(ref, transportationDisappearances, P_FE2CL_AROUND_DEL_TRANSPORTATION);
}
}

static void emptyChunk(ChunkPos chunkPos) {
Expand Down
9 changes: 0 additions & 9 deletions src/Eggs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion src/Eggs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
22 changes: 14 additions & 8 deletions src/Entities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,30 @@ 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);
}

void Egg::enterIntoViewOf(CNSocket *sock) {
INITSTRUCT(sP_FE2CL_SHINY_ENTER, pkt);

// TODO: Potentially decouple this from BaseNPC?
pkt.ShinyAppearanceData = {
id, type, 0, // client doesn't care about map num
pkt.ShinyAppearanceData = getShinyAppearanceData();
sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER);
}

sTransportationAppearanceData Bus::getTransportationAppearanceData() {
return sTransportationAppearanceData {
3, id, type,
x, y, z
};
}

sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER);
sShinyAppearanceData Egg::getShinyAppearanceData() {
return sShinyAppearanceData {
id, type, 0, // client doesn't care about map num
x, y, z
};
}

sNano* Player::getActiveNano() {
Expand Down
4 changes: 4 additions & 0 deletions src/Entities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -172,4 +174,6 @@ struct Bus : public BaseNPC {

virtual void enterIntoViewOf(CNSocket *sock) override;
virtual void disappearFromViewOf(CNSocket *sock) override;

sTransportationAppearanceData getTransportationAppearanceData();
};
8 changes: 8 additions & 0 deletions src/core/Defines.hpp
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading