// 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 #include 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; }