Implement 40250 classic client port

This commit is contained in:
shenlei
2026-09-01 18:49:28 +09:00
parent 2277b1dd60
commit 8f61990a83
481 changed files with 169826 additions and 293 deletions
+153
View File
@@ -0,0 +1,153 @@
// net_classic_wire_test — 40250 "classic" backend wire layer.
// The struct sizes are already guarded by static_assert in wire_classic.h (so a
// mismatch is a compile error); this test locks down the size-table / sequence /
// dynamic-flag lookups and a couple of round-trip byte layouts.
#include "../src/net/classic/wire_classic.h"
#include <cstdio>
#include <cstring>
using namespace mtnet::classic;
static int g_fail = 0;
#define CHECK(c, msg) \
do { \
if (!(c)) { \
std::fprintf(stderr, "FAIL: %s\n", msg); \
++g_fail; \
} \
} while (0)
int main() {
// --- header values (spot check vs server packet.h) ---
CHECK(HDR_CG_MOVE == 7, "CG_MOVE == 7");
CHECK(HDR_GC_MAIN_CHARACTER == 113, "GC_MAIN_CHARACTER == 113");
CHECK(HDR_GC_LOGIN_SUCCESS_NEWSLOT == 32, "GC_LOGIN_SUCCESS_NEWSLOT == 32");
CHECK(HDR_HANDSHAKE == 0xFF, "HANDSHAKE == 0xFF");
CHECK(HDR_GC_TIME_SYNC == 0xFC && HDR_CG_TIME_SYNC == 0xFC, "TIME_SYNC == 0xFC");
CHECK(HDR_KEY_AGREEMENT == 0xFB, "KEY_AGREEMENT == 0xFB");
CHECK(PHASE_GAME == 5 && PHASE_SELECT == 3, "EPhase ordinals");
// --- struct sizes (redundant with static_assert, but explicit) ---
CHECK(sizeof(Handshake) == 13, "sizeof Handshake");
CHECK(sizeof(CGText) == 1, "sizeof CG_TEXT base");
CHECK(sizeof(SimplePlayer) == 63, "sizeof SimplePlayer");
CHECK(sizeof(GCMainCharacter) == 46, "sizeof GCMainCharacter");
CHECK(sizeof(GCMainCharacter3BGM) == 71 && sizeof(GCMainCharacter4BGM) == 75,
"sizeof main-character BGM variants");
CHECK(sizeof(GCCharacterAdd2) == 80 && sizeof(GCCharacterUpdate) == 35,
"sizeof character add/update variants");
CHECK(sizeof(GCFishing) == 7, "sizeof GC_FISHING");
CHECK(sizeof(GCWarpClassic) == 15, "sizeof classic GC_WARP");
CHECK(sizeof(GCAuthSuccess) == 6 && sizeof(GCAuthSuccessOpenID) == 37,
"sizeof auth success packets");
CHECK(sizeof(GCPasspodFailed) == 130 && sizeof(GCXTrapRequest) == 129 && sizeof(GCHSRequest) == 403,
"sizeof optional security packets");
CHECK(sizeof(GCLoginSuccess) == 329, "sizeof GCLoginSuccess (NEWSLOT, 4 players)");
CHECK(sizeof(GCPoints) == 1 + 255 * 4, "sizeof GCPoints");
CHECK(sizeof(GCPointChange) == 17, "sizeof GCPointChange (int header quirk)");
CHECK(sizeof(CGMove) == 16, "sizeof CGMove");
CHECK(sizeof(GCMove) == 24, "sizeof GCMove");
CHECK(sizeof(CGMessenger) == 2 && sizeof(CGExchange) == 10, "sizeof P8 CG packets");
CHECK(sizeof(GCExchange) == 47, "sizeof GC_EXCHANGE");
CHECK(sizeof(GCMessengerHead) == 4 && sizeof(GCSafeboxMoneyChange) == 5,
"sizeof P8 dynamic/storage packets");
// --- size-table lookup (body size, no trailing seq byte) ---
CHECK(packet_size_gc(HDR_GC_PHASE) == 2, "gc size PHASE");
CHECK(packet_size_gc(HDR_GC_MAIN_CHARACTER) == 46, "gc size MAIN_CHARACTER");
CHECK(packet_size_gc(HDR_GC_WARP) == sizeof(GCWarpClassic), "gc size WARP");
CHECK(packet_size_gc(HDR_GC_MAIN_CHARACTER3_BGM) == 71 &&
packet_size_gc(HDR_GC_MAIN_CHARACTER4_BGM_VOL) == 75, "gc size MAIN_CHARACTER BGM");
CHECK(packet_size_gc(HDR_GC_CHARACTER_ADD2) == 80 &&
packet_size_gc(HDR_GC_CHARACTER_UPDATE2) == 35, "gc size character variants");
CHECK(packet_size_gc(HDR_GC_FISHING) == 7, "gc size FISHING");
CHECK(packet_size_gc(HDR_GC_AUTH_SUCCESS) == 6 &&
packet_size_gc(HDR_GC_XTRAP_CS1_REQUEST) == 129, "gc size auth/security");
CHECK(packet_size_gc(HDR_GC_LOGIN_SUCCESS_NEWSLOT) == 329, "gc size LOGIN_SUCCESS_NEWSLOT");
CHECK(packet_size_gc(HDR_GC_CHARACTER_POINTS) == 1021, "gc size CHARACTER_POINTS");
CHECK(packet_size_gc(HDR_GC_EXCHANGE) == 47, "gc size EXCHANGE");
CHECK(packet_size_gc(HDR_GC_SAFEBOX_MONEY_CHANGE) == 5, "gc size SAFEBOX_MONEY_CHANGE");
CHECK(packet_size_gc(HDR_GC_SAFEBOX_SET) == 51, "gc size SAFEBOX_SET");
CHECK(packet_size_gc(HDR_GC_MALL_OPEN) == 2, "gc size MALL_OPEN");
CHECK(packet_size_gc(HDR_GC_CHAT) == 0, "gc size CHAT == 0 (dynamic)");
CHECK(packet_size_cg(HDR_CG_LOGIN) == 49, "cg size LOGIN");
CHECK(packet_size_cg(HDR_CG_TEXT) == sizeof(CGText), "cg size TEXT base");
CHECK(packet_size_cg(HDR_CG_MOVE) == 16, "cg size MOVE");
CHECK(packet_size_cg(HDR_CG_ENTERGAME) == 1, "cg size ENTERGAME");
CHECK(packet_size_cg(HDR_CG_EXCHANGE) == 10, "cg size EXCHANGE");
CHECK(packet_size_cg(HDR_CG_MESSENGER) == 2, "cg size MESSENGER base");
CHECK(packet_size_cg(HDR_CG_SAFEBOX_ITEM_MOVE) == 8, "cg size SAFEBOX_ITEM_MOVE");
// --- sequence-byte flags (docs §2.2) ---
CHECK(is_sequence_cg(HDR_CG_MOVE), "CG_MOVE has sequence");
CHECK(is_sequence_cg(HDR_CG_LOGIN), "CG_LOGIN has sequence");
CHECK(is_sequence_cg(HDR_CG_TIME_SYNC), "CG_TIME_SYNC has sequence");
CHECK(!is_sequence_cg(HDR_HANDSHAKE), "HANDSHAKE no sequence");
CHECK(!is_sequence_cg(HDR_KEY_AGREEMENT), "KEY_AGREEMENT no sequence");
CHECK(!is_sequence_cg(HDR_CG_MARK_UPLOAD), "MARK_UPLOAD no sequence");
CHECK(!is_sequence_cg(HDR_CG_DRAGON_SOUL_REFINE), "DRAGON_SOUL_REFINE no sequence");
CHECK(!is_sequence_cg(HDR_CG_STATE_CHECKER), "STATE_CHECKER no sequence");
// --- dynamic-size flags (docs §2.2) ---
CHECK(is_dynamic_gc(HDR_GC_CHAT), "GC_CHAT dynamic");
CHECK(is_dynamic_gc(HDR_GC_SHOP), "GC_SHOP dynamic");
CHECK(is_dynamic_gc(HDR_GC_GUILD), "GC_GUILD dynamic");
CHECK(is_dynamic_gc(HDR_GC_QUEST_INFO), "GC_QUEST_INFO dynamic");
CHECK(!is_dynamic_gc(HDR_GC_MALL_SET), "GC_MALL_SET NOT dynamic");
CHECK(!is_dynamic_gc(HDR_GC_SAFEBOX_SET), "GC_SAFEBOX_SET NOT dynamic");
CHECK(!is_dynamic_gc(HDR_GC_PARTY_ADD), "GC_PARTY_ADD NOT dynamic");
// client map registers GC_WHISPER STATIC, but its wSize sits where
// TDynamicSizePacketHeader.size does — we frame it via the dynamic path.
CHECK(is_dynamic_gc(HDR_GC_WHISPER), "GC_WHISPER framed as dynamic (wSize prefix)");
CHECK(is_dynamic_cg(HDR_CG_CHAT) && is_dynamic_cg(HDR_CG_WHISPER), "CG chat/whisper dynamic");
CHECK(!is_dynamic_cg(HDR_CG_SHOP), "CG_SHOP NOT dynamic");
// --- byte-layout round trip: GC_MAIN_CHARACTER ---
{
GCMainCharacter mc{};
mc.header = HDR_GC_MAIN_CHARACTER;
mc.vid = 0x11223344;
mc.race = 4;
std::strcpy(mc.name, "Hero");
mc.x = 100000;
mc.y = 200000;
mc.z = 12;
mc.empire = 2;
mc.skill_group = 1;
uint8_t buf[sizeof(mc)];
std::memcpy(buf, &mc, sizeof(mc));
CHECK(buf[0] == 113, "wire byte 0 == header");
// header(1) vid(4) race(2) name(25) -> x at offset 32
int32_t x_wire;
std::memcpy(&x_wire, buf + 1 + 4 + 2 + 25, 4);
CHECK(x_wire == 100000, "wire x at offset 32");
GCMainCharacter rt{};
std::memcpy(&rt, buf, sizeof(rt));
CHECK(rt.vid == 0x11223344 && rt.z == 12 && rt.empire == 2, "round-trip fields");
CHECK(std::strcmp(rt.name, "Hero") == 0, "round-trip name");
}
// --- GCPointChange: real header byte is buf[0] despite `int32_t header` ---
{
GCPointChange pc{};
pc.header = HDR_GC_CHARACTER_POINT_CHANGE; // 17
pc.vid = 7;
pc.type = 5;
pc.amount = -3;
pc.value = 42;
uint8_t buf[sizeof(pc)];
std::memcpy(buf, &pc, sizeof(pc));
CHECK(buf[0] == 17, "point_change wire byte 0 == 17");
CHECK(sizeof(pc) == 17, "point_change wire size 17 (4B int header + 4+1+4+4)");
}
if (g_fail) {
std::fprintf(stderr, "%d check(s) failed\n", g_fail);
return 1;
}
std::puts("net_classic_wire_test OK");
return 0;
}