Skip to content

Commit ae327cc

Browse files
Use FE2CL_..._AROUND, _AROUND_DEL packets (#295)
* Use FE2CL_..._AROUND, _AROUND_DEL packets * Use increased buffer size for 728 and 1013 protocols
1 parent 6ffde9b commit ae327cc

File tree

9 files changed

+234
-30
lines changed

9 files changed

+234
-30
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ CXXHDR=\
115115
src/settings.hpp\
116116
src/Transport.hpp\
117117
src/TableData.hpp\
118+
src/Bucket.hpp\
118119
src/Chunking.hpp\
119120
src/Buddies.hpp\
120121
src/Groups.hpp\

src/Bucket.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include <array>
4+
#include <optional>
5+
6+
#include <assert.h>
7+
8+
template<class T, size_t N>
9+
class Bucket {
10+
std::array<T, N> buf;
11+
size_t sz;
12+
public:
13+
Bucket() {
14+
sz = 0;
15+
}
16+
17+
void add(const T& item) {
18+
assert(sz < N);
19+
buf[sz++] = item;
20+
}
21+
22+
std::optional<T> get(size_t idx) const {
23+
if (idx < sz) {
24+
return buf[idx];
25+
}
26+
return std::nullopt;
27+
}
28+
29+
size_t size() const {
30+
return sz;
31+
}
32+
33+
bool isFull() const {
34+
return sz == N;
35+
}
36+
37+
void clear() {
38+
sz = 0;
39+
}
40+
};

src/Chunking.cpp

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include "Chunking.hpp"
22

3+
#include "Player.hpp"
34
#include "MobAI.hpp"
45
#include "NPCManager.hpp"
6+
#include "Bucket.hpp"
57

68
#include <assert.h>
79

@@ -11,6 +13,12 @@ using namespace Chunking;
1113
* The initial chunkPos value before a player is placed into the world.
1214
*/
1315
const ChunkPos Chunking::INVALID_CHUNK = {};
16+
constexpr size_t MAX_PC_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sPCAppearanceData);
17+
constexpr size_t MAX_NPC_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sNPCAppearanceData);
18+
constexpr size_t MAX_SHINY_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sShinyAppearanceData);
19+
constexpr size_t MAX_TRANSPORTATION_PER_AROUND = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(sTransportationAppearanceData);
20+
constexpr size_t MAX_IDS_PER_AROUND_DEL = (CN_PACKET_BODY_SIZE - sizeof(int32_t)) / sizeof(int32_t);
21+
constexpr size_t MAX_TRANSPORTATION_IDS_PER_AROUND_DEL = MAX_IDS_PER_AROUND_DEL - 1; // 1 less for eTT
1422

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

@@ -75,11 +83,80 @@ void Chunking::untrackEntity(ChunkPos chunkPos, const EntityRef ref) {
7583
deleteChunk(chunkPos);
7684
}
7785

86+
template<class T, size_t N>
87+
static void sendAroundPackets(const EntityRef recipient, std::vector<Bucket<T, N>>& buckets, uint32_t packetId) {
88+
assert(recipient.kind == EntityKind::PLAYER);
89+
90+
uint8_t pktBuf[CN_PACKET_BODY_SIZE];
91+
for (const auto& bucket : buckets) {
92+
memset(pktBuf, 0, CN_PACKET_BODY_SIZE);
93+
int count = bucket.size();
94+
*((int32_t*)pktBuf) = count;
95+
T* data = (T*)(pktBuf + sizeof(int32_t));
96+
for (size_t i = 0; i < count; i++) {
97+
data[i] = bucket.get(i).value();
98+
}
99+
recipient.sock->sendPacket(pktBuf, packetId, sizeof(int32_t) + (count * sizeof(T)));
100+
}
101+
}
102+
103+
template<size_t N>
104+
static void sendAroundDelPackets(const EntityRef recipient, std::vector<Bucket<int32_t, N>>& buckets, uint32_t packetId) {
105+
assert(recipient.kind == EntityKind::PLAYER);
106+
107+
uint8_t pktBuf[CN_PACKET_BODY_SIZE];
108+
for (const auto& bucket : buckets) {
109+
memset(pktBuf, 0, CN_PACKET_BODY_SIZE);
110+
int count = bucket.size();
111+
assert(count <= N);
112+
113+
size_t baseSize;
114+
if (packetId == P_FE2CL_AROUND_DEL_TRANSPORTATION) {
115+
sP_FE2CL_AROUND_DEL_TRANSPORTATION* pkt = (sP_FE2CL_AROUND_DEL_TRANSPORTATION*)pktBuf;
116+
pkt->eTT = 3;
117+
pkt->iCnt = count;
118+
baseSize = sizeof(sP_FE2CL_AROUND_DEL_TRANSPORTATION);
119+
} else {
120+
*((int32_t*)pktBuf) = count;
121+
baseSize = sizeof(int32_t);
122+
}
123+
int32_t* ids = (int32_t*)(pktBuf + baseSize);
124+
125+
for (size_t i = 0; i < count; i++) {
126+
ids[i] = bucket.get(i).value();
127+
}
128+
recipient.sock->sendPacket(pktBuf, packetId, baseSize + (count * sizeof(int32_t)));
129+
}
130+
}
131+
132+
template<class T, size_t N>
133+
static void bufferAppearanceData(std::vector<Bucket<T, N>>& buckets, const T& data) {
134+
if (buckets.empty())
135+
buckets.push_back({});
136+
auto& bucket = buckets[buckets.size() - 1];
137+
bucket.add(data);
138+
if (bucket.isFull())
139+
buckets.push_back({});
140+
}
141+
142+
template<size_t N>
143+
static void bufferIdForDisappearance(std::vector<Bucket<int32_t, N>>& buckets, int32_t id) {
144+
if (buckets.empty())
145+
buckets.push_back({});
146+
auto& bucket = buckets[buckets.size() - 1];
147+
bucket.add(id);
148+
if (bucket.isFull())
149+
buckets.push_back({});
150+
}
151+
78152
void Chunking::addEntityToChunks(std::set<Chunk*> chnks, const EntityRef ref) {
79153
Entity *ent = ref.getEntity();
80154
bool alive = ent->isExtant();
81155

82-
// TODO: maybe optimize this, potentially using AROUND packets?
156+
std::vector<Bucket<sPCAppearanceData, MAX_PC_PER_AROUND>> pcAppearances;
157+
std::vector<Bucket<sNPCAppearanceData, MAX_NPC_PER_AROUND>> npcAppearances;
158+
std::vector<Bucket<sShinyAppearanceData, MAX_SHINY_PER_AROUND>> shinyAppearances;
159+
std::vector<Bucket<sTransportationAppearanceData, MAX_TRANSPORTATION_PER_AROUND>> transportationAppearances;
83160
for (Chunk *chunk : chnks) {
84161
for (const EntityRef otherRef : chunk->entities) {
85162
// skip oneself
@@ -95,7 +172,38 @@ void Chunking::addEntityToChunks(std::set<Chunk*> chnks, const EntityRef ref) {
95172

96173
// notify this *player* of the existence of all visible Entities
97174
if (ref.kind == EntityKind::PLAYER && other->isExtant()) {
98-
other->enterIntoViewOf(ref.sock);
175+
sPCAppearanceData pcData;
176+
sNPCAppearanceData npcData;
177+
sShinyAppearanceData eggData;
178+
sTransportationAppearanceData busData;
179+
switch(otherRef.kind) {
180+
case EntityKind::PLAYER:
181+
pcData = dynamic_cast<Player*>(other)->getAppearanceData();
182+
bufferAppearanceData(pcAppearances, pcData);
183+
break;
184+
case EntityKind::SIMPLE_NPC:
185+
npcData = dynamic_cast<BaseNPC*>(other)->getAppearanceData();
186+
bufferAppearanceData(npcAppearances, npcData);
187+
break;
188+
case EntityKind::COMBAT_NPC:
189+
npcData = dynamic_cast<CombatNPC*>(other)->getAppearanceData();
190+
bufferAppearanceData(npcAppearances, npcData);
191+
break;
192+
case EntityKind::MOB:
193+
npcData = dynamic_cast<Mob*>(other)->getAppearanceData();
194+
bufferAppearanceData(npcAppearances, npcData);
195+
break;
196+
case EntityKind::EGG:
197+
eggData = dynamic_cast<Egg*>(other)->getShinyAppearanceData();
198+
bufferAppearanceData(shinyAppearances, eggData);
199+
break;
200+
case EntityKind::BUS:
201+
busData = dynamic_cast<Bus*>(other)->getTransportationAppearanceData();
202+
bufferAppearanceData(transportationAppearances, busData);
203+
break;
204+
default:
205+
break;
206+
}
99207
}
100208

101209
// for mobs, increment playersInView
@@ -105,13 +213,27 @@ void Chunking::addEntityToChunks(std::set<Chunk*> chnks, const EntityRef ref) {
105213
((Mob*)other)->playersInView++;
106214
}
107215
}
216+
217+
if (ref.kind == EntityKind::PLAYER) {
218+
if (!pcAppearances.empty())
219+
sendAroundPackets(ref, pcAppearances, P_FE2CL_PC_AROUND);
220+
if (!npcAppearances.empty())
221+
sendAroundPackets(ref, npcAppearances, P_FE2CL_NPC_AROUND);
222+
if (!shinyAppearances.empty())
223+
sendAroundPackets(ref, shinyAppearances, P_FE2CL_SHINY_AROUND);
224+
if (!transportationAppearances.empty())
225+
sendAroundPackets(ref, transportationAppearances, P_FE2CL_TRANSPORTATION_AROUND);
226+
}
108227
}
109228

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

114-
// TODO: same as above
233+
std::vector<Bucket<int32_t, MAX_IDS_PER_AROUND_DEL>> pcDisappearances;
234+
std::vector<Bucket<int32_t, MAX_IDS_PER_AROUND_DEL>> npcDisappearances;
235+
std::vector<Bucket<int32_t, MAX_IDS_PER_AROUND_DEL>> shinyDisappearances;
236+
std::vector<Bucket<int32_t, MAX_TRANSPORTATION_IDS_PER_AROUND_DEL>> transportationDisappearances;
115237
for (Chunk *chunk : chnks) {
116238
for (const EntityRef otherRef : chunk->entities) {
117239
// skip oneself
@@ -127,7 +249,29 @@ void Chunking::removeEntityFromChunks(std::set<Chunk*> chnks, const EntityRef re
127249

128250
// notify this *player* of the departure of all visible Entities
129251
if (ref.kind == EntityKind::PLAYER && other->isExtant()) {
130-
other->disappearFromViewOf(ref.sock);
252+
int32_t id;
253+
switch(otherRef.kind) {
254+
case EntityKind::PLAYER:
255+
id = dynamic_cast<Player*>(other)->iID;
256+
bufferIdForDisappearance(pcDisappearances, id);
257+
break;
258+
case EntityKind::SIMPLE_NPC:
259+
case EntityKind::COMBAT_NPC:
260+
case EntityKind::MOB:
261+
id = dynamic_cast<BaseNPC*>(other)->id;
262+
bufferIdForDisappearance(npcDisappearances, id);
263+
break;
264+
case EntityKind::EGG:
265+
id = dynamic_cast<Egg*>(other)->id;
266+
bufferIdForDisappearance(shinyDisappearances, id);
267+
break;
268+
case EntityKind::BUS:
269+
id = dynamic_cast<Bus*>(other)->id;
270+
bufferIdForDisappearance(transportationDisappearances, id);
271+
break;
272+
default:
273+
break;
274+
}
131275
}
132276

133277
// for mobs, decrement playersInView
@@ -137,6 +281,17 @@ void Chunking::removeEntityFromChunks(std::set<Chunk*> chnks, const EntityRef re
137281
((Mob*)other)->playersInView--;
138282
}
139283
}
284+
285+
if (ref.kind == EntityKind::PLAYER) {
286+
if (!pcDisappearances.empty())
287+
sendAroundDelPackets(ref, pcDisappearances, P_FE2CL_AROUND_DEL_PC);
288+
if (!npcDisappearances.empty())
289+
sendAroundDelPackets(ref, npcDisappearances, P_FE2CL_AROUND_DEL_NPC);
290+
if (!shinyDisappearances.empty())
291+
sendAroundDelPackets(ref, shinyDisappearances, P_FE2CL_AROUND_DEL_SHINY);
292+
if (!transportationDisappearances.empty())
293+
sendAroundDelPackets(ref, transportationDisappearances, P_FE2CL_AROUND_DEL_TRANSPORTATION);
294+
}
140295
}
141296

142297
static void emptyChunk(ChunkPos chunkPos) {

src/Eggs.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,6 @@ static void eggStep(CNServer* serv, time_t currTime) {
126126

127127
}
128128

129-
void Eggs::npcDataToEggData(int x, int y, int z, sNPCAppearanceData* npc, sShinyAppearanceData* egg) {
130-
egg->iX = x;
131-
egg->iY = y;
132-
egg->iZ = z;
133-
// client doesn't care about egg->iMapNum
134-
egg->iShinyType = npc->iNPCType;
135-
egg->iShiny_ID = npc->iNPC_ID;
136-
}
137-
138129
static void eggPickup(CNSocket* sock, CNPacketData* data) {
139130
auto pickup = (sP_CL2FE_REQ_SHINY_PICKUP*)data->buf;
140131
Player* plr = PlayerManager::getPlayer(sock);

src/Eggs.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,4 @@ namespace Eggs {
1515
void init();
1616

1717
void eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration);
18-
void npcDataToEggData(int x, int y, int z, sNPCAppearanceData* npc, sShinyAppearanceData* egg);
1918
}

src/Entities.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,30 @@ void Bus::enterIntoViewOf(CNSocket *sock) {
7070
INITSTRUCT(sP_FE2CL_TRANSPORTATION_ENTER, pkt);
7171

7272
// TODO: Potentially decouple this from BaseNPC?
73-
pkt.AppearanceData = {
74-
3, id, type,
75-
x, y, z
76-
};
77-
73+
pkt.AppearanceData = getTransportationAppearanceData();
7874
sock->sendPacket(pkt, P_FE2CL_TRANSPORTATION_ENTER);
7975
}
8076

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

8480
// TODO: Potentially decouple this from BaseNPC?
85-
pkt.ShinyAppearanceData = {
86-
id, type, 0, // client doesn't care about map num
81+
pkt.ShinyAppearanceData = getShinyAppearanceData();
82+
sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER);
83+
}
84+
85+
sTransportationAppearanceData Bus::getTransportationAppearanceData() {
86+
return sTransportationAppearanceData {
87+
3, id, type,
8788
x, y, z
8889
};
90+
}
8991

90-
sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER);
92+
sShinyAppearanceData Egg::getShinyAppearanceData() {
93+
return sShinyAppearanceData {
94+
id, type, 0, // client doesn't care about map num
95+
x, y, z
96+
};
9197
}
9298

9399
sNano* Player::getActiveNano() {

src/Entities.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ struct Egg : public BaseNPC {
161161

162162
virtual void enterIntoViewOf(CNSocket *sock) override;
163163
virtual void disappearFromViewOf(CNSocket *sock) override;
164+
165+
sShinyAppearanceData getShinyAppearanceData();
164166
};
165167

166168
struct Bus : public BaseNPC {
@@ -172,4 +174,6 @@ struct Bus : public BaseNPC {
172174

173175
virtual void enterIntoViewOf(CNSocket *sock) override;
174176
virtual void disappearFromViewOf(CNSocket *sock) override;
177+
178+
sTransportationAppearanceData getTransportationAppearanceData();
175179
};

src/core/Defines.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* enum definitions from the client */
22
#pragma once
33

4+
#include "core/CNStructs.hpp"
5+
46
// floats
57
const float VALUE_BATTERY_EMPTY_PENALTY = 0.5f;
68
const float CN_EP_RANK_1 = 0.8f;
@@ -410,7 +412,13 @@ enum {
410412
SEND_ANYCAST_NEW = 3,
411413
SEND_BROADCAST = 4,
412414

415+
#if PROTOCOL_VERSION == 728
416+
CN_PACKET_BUFFER_SIZE = 8192,
417+
#elif PROTOCOL_VERSION == 1013
418+
CN_PACKET_BUFFER_SIZE = 8192,
419+
#else
413420
CN_PACKET_BUFFER_SIZE = 4096,
421+
#endif
414422

415423
P_CL2LS_REQ_LOGIN = 0x12000001, // 301989889
416424
P_CL2LS_REQ_CHECK_CHAR_NAME = 0x12000002, // 301989890

0 commit comments

Comments
 (0)