#pragma once // Metin2 net wire format (this m2dev fork). Every packet on the wire is // [uint16 header LE][uint16 length LE][payload...] // where `length` is the total packet size including the 4-byte prefix // (>= PACKET_HEADER_SIZE). Runs of zero bytes between packets are padding and // are skipped. Headers are the namespaced CG::/GC:: constants from the client's // UserInterface/Packet.h + EterLib/ControlPackets.h. // // Struct layouts and header values verified against: // EterLib/ControlPackets.h (KX / ping / phase) // UserInterface/Packet.h (CG/GC enums, PHASE_*, login3, auth_success) // UserInterface/AccountConnector.cpp (auth-server flow: PHASE_AUTH -> LOGIN3 -> AUTH_SUCCESS) #include namespace mtnet { inline constexpr uint16_t PACKET_HEADER_SIZE = 4; inline constexpr int ID_MAX_NUM = 30; inline constexpr int PASS_MAX_NUM = 16; // EPhases (UserInterface/Packet.h) — value = enum index. enum Phase : uint8_t { PHASE_CLOSE = 0, PHASE_HANDSHAKE = 1, PHASE_LOGIN = 2, PHASE_SELECT = 3, PHASE_LOADING = 4, PHASE_GAME = 5, PHASE_DEAD = 6, PHASE_CLIENT_CONNECTING = 7, PHASE_DBCLIENT = 8, PHASE_P2P = 9, PHASE_AUTH = 10, }; // Control plane (EterLib/ControlPackets.h) inline constexpr uint16_t CG_PONG = 0x0006; inline constexpr uint16_t GC_PING = 0x0007; inline constexpr uint16_t GC_PHASE = 0x0008; inline constexpr uint16_t CG_KEY_RESPONSE = 0x000A; inline constexpr uint16_t GC_KEY_CHALLENGE = 0x000B; inline constexpr uint16_t GC_KEY_COMPLETE = 0x000C; inline constexpr uint16_t CG_CLIENT_VERSION = 0x000D; // sent after GC_MAIN_CHARACTER // Auth (UserInterface/Packet.h namespace CG/GC) inline constexpr uint16_t CG_LOGIN2 = 0x0101; // game server: name + login_key inline constexpr uint16_t CG_LOGIN3 = 0x0102; // auth server: name + pwd inline constexpr uint16_t CG_LOGIN_SECURE = 0x0103; // name + pwd + session_token inline constexpr uint16_t CG_EMPIRE = 0x010A; inline constexpr uint16_t CG_CHANGE_NAME = 0x010B; inline constexpr uint16_t GC_CHANGE_NAME = 0x010C; inline constexpr uint16_t CG_CHARACTER_CREATE = 0x0201; inline constexpr uint16_t CG_CHARACTER_DELETE = 0x0202; inline constexpr uint16_t CG_CHARACTER_SELECT = 0x0203; inline constexpr uint16_t CG_ENTERGAME = 0x0204; inline constexpr uint16_t GC_LOGIN_SUCCESS3 = 0x0104; // char list (PLAYER_PER_ACCOUNT3) inline constexpr uint16_t GC_LOGIN_SUCCESS4 = 0x0105; inline constexpr uint16_t GC_LOGIN_FAILURE = 0x0106; inline constexpr uint16_t GC_EMPIRE = 0x0109; inline constexpr uint16_t GC_AUTH_SUCCESS = 0x0108; inline constexpr uint16_t GC_PLAYER_CREATE_SUCCESS = 0x020C; inline constexpr uint16_t GC_PLAYER_CREATE_FAILURE = 0x020D; inline constexpr uint16_t GC_PLAYER_DELETE_SUCCESS = 0x020E; inline constexpr uint16_t GC_PLAYER_DELETE_WRONG_SOCIAL_ID = 0x020F; inline constexpr int PRIVATE_CODE_LENGTH = 8; // CG_CHARACTER_DELETE social-id code inline constexpr int CHARACTER_NAME_MAX_LEN = 64; inline constexpr int GUILD_NAME_MAX_LEN = 12; inline constexpr int PLAYER_PER_ACCOUNT3 = 3; inline constexpr int PLAYER_PER_ACCOUNT4 = 4; inline constexpr int CHR_EQUIPPART_NUM = 4; // ARMOR, WEAPON, HEAD, HAIR inline constexpr int MUSIC_NAME_MAX_LEN = 24; // Game phase — server -> client inline constexpr uint16_t GC_CHARACTER_ADD = 0x0205; inline constexpr uint16_t GC_CHARACTER_ADD2 = 0x0206; inline constexpr uint16_t GC_CHAR_ADD_INFO = 0x0207; // name/level/guild/mount for a vid inline constexpr uint16_t GC_CHARACTER_DEL = 0x0208; inline constexpr uint16_t GC_CHARACTER_UPDATE = 0x0209; inline constexpr uint16_t GC_MAIN_CHARACTER = 0x0210; inline constexpr uint16_t GC_CHARACTER_POSITION = 0x020B; inline constexpr uint16_t GC_SKILL_LEVEL = 0x021A; // flat u8[255] skill->level inline constexpr uint16_t GC_SKILL_LEVEL_NEW = 0x021B; // {master,level,next_read}[255] inline constexpr uint16_t GC_SKILL_COOLTIME_END = 0x021C; // one skill's cooldown ended inline constexpr uint16_t GC_CHANGE_SKILL_GROUP = 0x021D; inline constexpr uint16_t GC_VIEW_EQUIP = 0x021E; // inspect another character's equipment inline constexpr uint16_t GC_QUICKSLOT_ADD = 0x0519; inline constexpr uint16_t GC_QUICKSLOT_DEL = 0x051A; inline constexpr uint16_t GC_QUICKSLOT_SWAP = 0x051B; inline constexpr int QUICKSLOT_MAX_NUM = 36; inline constexpr uint16_t GC_PLAYER_POINTS = 0x0214; // full stat block (main char) inline constexpr uint16_t GC_PLAYER_POINT_CHANGE = 0x0215; // one point delta (any vid) inline constexpr uint16_t GC_STUN = 0x0216; inline constexpr uint16_t GC_DEAD = 0x0217; inline constexpr uint16_t GC_CHANGE_SPEED = 0x0218; inline constexpr uint16_t GC_WALK_MODE = 0x0219; inline constexpr uint16_t GC_MOVE = 0x0302; inline constexpr uint16_t GC_SYNC_POSITION = 0x0304; inline constexpr uint16_t GC_WARP = 0x0306; inline constexpr uint16_t GC_MOTION = 0x0307; // vid does at victim inline constexpr uint16_t GC_DIG_MOTION = 0x0308; // mining motion broadcast inline constexpr uint16_t GC_DAMAGE_INFO = 0x0410; // floating damage number inline constexpr uint16_t GC_FLY_TARGETING = 0x0411; inline constexpr uint16_t GC_ADD_FLY_TARGETING = 0x0412; inline constexpr uint16_t GC_CREATE_FLY = 0x0413; // projectile start_vid -> end_vid inline constexpr uint16_t GC_PVP = 0x0414; inline constexpr uint16_t GC_DUEL_START = 0x0415; inline constexpr uint16_t GC_ITEM_DEL = 0x0510; inline constexpr uint16_t GC_ITEM_SET = 0x0511; inline constexpr uint16_t GC_ITEM_USE = 0x0512; inline constexpr uint16_t GC_ITEM_UPDATE = 0x0514; inline constexpr uint16_t GC_ITEM_GROUND_ADD = 0x0515; inline constexpr uint16_t GC_ITEM_GROUND_DEL = 0x0516; inline constexpr uint16_t GC_ITEM_OWNERSHIP = 0x0517; inline constexpr uint16_t GC_ITEM_GET = 0x0518; inline constexpr uint16_t GC_SCRIPT = 0x0910; // NPC dialog EventManager script inline constexpr uint16_t GC_QUEST_CONFIRM = 0x0911; // yes/no confirm request inline constexpr uint16_t GC_QUEST_INFO = 0x0912; // quest-log entry (flag-driven) inline constexpr uint16_t GC_CHAT = 0x0603; inline constexpr uint16_t GC_WHISPER = 0x0604; // P8 — social / shop / storage (server -> client) inline constexpr uint16_t GC_EXCHANGE = 0x051C; // sub-header packet inline constexpr uint16_t GC_REFINE_INFO = 0x051D; // refine dialog data inline constexpr uint16_t GC_REFINE_INFO_NEW = 0x051E; // same layout as 0x051D inline constexpr uint16_t GC_DRAGON_SOUL_REFINE = 0x051F; // sub-type + item pos inline constexpr uint16_t GC_GUILD = 0x0730; // sub-header packet inline constexpr uint16_t GC_REQUEST_MAKE_GUILD = 0x0731; // server asks client to create guild inline constexpr uint16_t GC_SYMBOL_DATA = 0x0732; // guild symbol file bytes inline constexpr uint16_t GC_PARTY_INVITE = 0x0710; inline constexpr uint16_t GC_PARTY_ADD = 0x0711; inline constexpr uint16_t GC_PARTY_UPDATE = 0x0712; inline constexpr uint16_t GC_PARTY_REMOVE = 0x0713; inline constexpr uint16_t GC_PARTY_LINK = 0x0714; // pid <-> vid binding inline constexpr uint16_t GC_PARTY_UNLINK = 0x0715; inline constexpr uint16_t GC_PARTY_PARAMETER = 0x0716; // exp distribution mode inline constexpr uint16_t GC_MESSENGER = 0x0741; // sub-header + var name list inline constexpr uint16_t GC_LOVER_INFO = 0x0750; inline constexpr uint16_t GC_LOVE_POINT_UPDATE = 0x0751; inline constexpr uint16_t GC_SHOP = 0x0810; // sub-header packet inline constexpr uint16_t GC_SHOP_SIGN = 0x0811; inline constexpr uint16_t GC_SAFEBOX_SET = 0x0830; // body = GCItemSet inline constexpr uint16_t GC_SAFEBOX_DEL = 0x0831; // body = GCItemDel inline constexpr uint16_t GC_SAFEBOX_WRONG_PASSWORD = 0x0832; inline constexpr uint16_t GC_SAFEBOX_SIZE = 0x0833; // open: page count inline constexpr uint16_t GC_SAFEBOX_MONEY_CHANGE = 0x0834; inline constexpr uint16_t GC_MALL_OPEN = 0x0841; // {u8 size} inline constexpr uint16_t GC_MALL_SET = 0x0842; // body = GCItemSet (pos.cell) inline constexpr uint16_t GC_MALL_DEL = 0x0843; // body = GCItemDel (pos.cell) // TPacketGC*Sub subheaders (UserInterface/Packet.h namespaces). enum : uint8_t { // MessengerSub::GC MESSENGER_GC_LIST = 0, MESSENGER_GC_LOGIN = 1, MESSENGER_GC_LOGOUT = 2, MESSENGER_GC_INVITE = 3, MESSENGER_GC_REMOVE_FRIEND = 4, }; enum : uint8_t { // MessengerSub::CG MESSENGER_CG_ADD_BY_VID = 0, MESSENGER_CG_ADD_BY_NAME = 1, MESSENGER_CG_REMOVE = 2, MESSENGER_CG_INVITE_ANSWER = 3, }; enum : uint8_t { // ShopSub::GC SHOP_GC_START = 0, SHOP_GC_END = 1, SHOP_GC_UPDATE_ITEM = 2, SHOP_GC_UPDATE_PRICE = 3, SHOP_GC_OK = 4, SHOP_GC_NOT_ENOUGH_MONEY = 5, SHOP_GC_SOLDOUT = 6, SHOP_GC_INVENTORY_FULL = 7, SHOP_GC_INVALID_POS = 8, SHOP_GC_START_EX = 10, }; enum : uint8_t { // ShopSub::CG SHOP_CG_END = 0, SHOP_CG_BUY = 1, SHOP_CG_SELL = 2, SHOP_CG_SELL2 = 3, }; enum : uint8_t { // ExchangeSub (CG and GC share these low values) EXCHANGE_SUB_START = 0, EXCHANGE_SUB_ITEM_ADD = 1, EXCHANGE_SUB_ITEM_DEL = 2, EXCHANGE_SUB_ELK_ADD = 3, EXCHANGE_SUB_ACCEPT = 4, EXCHANGE_SUB_CANCEL = 5, // CG only EXCHANGE_GC_END = 5, // GC only (overlaps CANCEL slot) EXCHANGE_GC_ALREADY = 6, EXCHANGE_GC_LESS_ELK = 7, }; // TPacketGCPartyUpdate::state bits (PARTY_ROLE_*: 1=normal, then leader flag etc.) inline constexpr int PARTY_AFFECT_SLOT_MAX_NUM = 7; inline constexpr int SHOP_HOST_ITEM_MAX_NUM = 40; inline constexpr int SHOP_SIGN_MAX_LEN = 32; inline constexpr int SHOP_TAB_NAME_MAX = 32; // Packet.h SHOP_TAB_NAME_MAX inline constexpr int SHOP_TAB_COUNT_MAX = 3; // Packet.h SHOP_TAB_COUNT_MAX inline constexpr int SAFEBOX_MAX_NUM = 135; // up to 3 pages x 45 inline constexpr int MALL_MAX_NUM = 135; // item-mall storage, same page geometry inline constexpr int PRIVATE_SHOP_ITEM_MAX_NUM = 39; // TPacketCGMyShop::bCount cap inline constexpr int DRAGON_SOUL_MAX_NUM = 180; // 6 groups x 30 // Guild-mark image geometry (UserInterface/MarkImage.h). One 512x512 RGBA image // holds 8x10 blocks of 64x48 px; each block = 4x4 guild marks of 16x12 px. inline constexpr int GUILD_MARK_WIDTH = 16; inline constexpr int GUILD_MARK_HEIGHT = 12; inline constexpr int MARK_BLOCK_WIDTH = 64; inline constexpr int MARK_BLOCK_HEIGHT = 48; inline constexpr int MARK_BLOCK_PIXELS = MARK_BLOCK_WIDTH * MARK_BLOCK_HEIGHT; // 3072 inline constexpr int MARK_IMAGE_WIDTH = 512; inline constexpr int MARK_IMAGE_HEIGHT = 512; inline constexpr int MARK_IMAGE_PIXELS = MARK_IMAGE_WIDTH * MARK_IMAGE_HEIGHT; inline constexpr int MARK_BLOCK_COL_COUNT = 8; // 512 / 64 inline constexpr int MARK_BLOCK_ROW_COUNT = 10; // 512 / 48 (rounded down) inline constexpr int MARK_BLOCK_TOTAL_COUNT = MARK_BLOCK_COL_COUNT * MARK_BLOCK_ROW_COUNT; // 80 inline constexpr int MARK_COL_COUNT = 32; // guild marks per image row (8 * 4) inline constexpr int MARK_ROW_COUNT = 40; // guild marks per image col (10 * 4) inline constexpr int MARK_PER_IMAGE = MARK_COL_COUNT * MARK_ROW_COUNT; // 1280 inline constexpr int MARK_IMAGE_MAX_COUNT = 5; // TPacketGCQuestInfo::flag bits (Packet.h). enum : uint8_t { QUEST_SEND_IS_BEGIN = 1 << 0, QUEST_SEND_TITLE = 1 << 1, // <= 28 chars QUEST_SEND_CLOCK_NAME = 1 << 2, // <= 16 QUEST_SEND_CLOCK_VALUE = 1 << 3, // int32 QUEST_SEND_COUNTER_NAME = 1 << 4, // <= 16 QUEST_SEND_COUNTER_VALUE = 1 << 5,// int32 QUEST_SEND_ICON_FILE = 1 << 6, // <= 24 }; inline constexpr uint16_t GC_TARGET_INFO = 0x0A10; // selected target's HP % inline constexpr uint16_t GC_TARGET_UPDATE = 0x0A11; // move a world marker inline constexpr uint16_t GC_TARGET_DELETE = 0x0A12; // remove a world marker inline constexpr uint16_t GC_TARGET_CREATE = 0x0A13; // quest/world target marker (New form) inline constexpr uint16_t GC_AFFECT_ADD = 0x0A20; inline constexpr uint16_t GC_AFFECT_REMOVE = 0x0A21; inline constexpr uint16_t GC_SPECIAL_EFFECT = 0x0A30; // vid plays effect inline constexpr uint16_t GC_SPECIFIC_EFFECT = 0x0A31; // vid plays effect <.mse path> inline constexpr uint16_t GC_NPC_POSITION = 0x0A50; // atlas/minimap NPC list inline constexpr uint16_t GC_MOUNT = 0x0A40; // vid mounts/dismounts mount_vnum // P9 — world systems (server -> client) inline constexpr uint16_t CG_FISHING = 0x0B01; inline constexpr uint16_t CG_DUNGEON = 0x0B02; inline constexpr uint16_t GC_FISHING = 0x0B10; inline constexpr uint16_t GC_DUNGEON = 0x0B11; inline constexpr uint16_t GC_LAND_LIST = 0x0B12; // guild territory list inline constexpr uint16_t GC_TIME = 0x0B13; // server epoch for the day/night clock inline constexpr uint16_t GC_CHANNEL = 0x0B14; // which channel we're on inline constexpr uint16_t GC_MARK_UPDATE = 0x0B15; // main conn: a guild mark changed -> re-download inline constexpr uint16_t GC_OBSERVER_ADD = 0x0B20; // minimap observer add inline constexpr uint16_t GC_OBSERVER_REMOVE = 0x0B21; // minimap observer remove inline constexpr uint16_t GC_OBSERVER_MOVE = 0x0B22; // minimap observer move // Guild-mark side connection (0x0C00-0x0CFF). A separate stream with its own KX; // GC_MARK_* here use a u32 buf_size, NOT the u16 frame length, for the body. inline constexpr uint16_t GC_MARK_IDXLIST = 0x0C11; // guild_id -> mark_id table inline constexpr uint16_t GC_MARK_BLOCK = 0x0C10; // LZO-compressed 64x48 image blocks inline constexpr uint16_t GC_MARK_DIFF_DATA = 0x0C12; // (ignored) inline constexpr uint16_t CG_MARK_LOGIN = 0x0C01; // {handle, random_key} from GC_LOGIN_SUCCESS* inline constexpr uint16_t CG_MARK_CRCLIST = 0x0C02; // {img_idx, crc[80]} - request an image (0 = full) inline constexpr uint16_t CG_MARK_UPLOAD = 0x0C03; // {gid, u8 image[16*12*4]} raw 16x12 mark inline constexpr uint16_t CG_MARK_IDXLIST = 0x0C04; // request the guild_id -> mark_id table inline constexpr uint16_t CG_GUILD_SYMBOL_UPLOAD = 0x0722; // {handle=gid} + raw symbol file bytes // TPacketGCTargetCreateNew::byType (CREATE_TARGET_TYPE_*). enum : uint8_t { CREATE_TARGET_TYPE_NONE = 0, CREATE_TARGET_TYPE_LOCATION = 1, CREATE_TARGET_TYPE_CHARACTER = 2, }; // EChatType (Packet.h). BIG_NOTICE etc. beyond SHOUT are rendered as NOTICE. enum : uint8_t { CHAT_TYPE_TALKING = 0, CHAT_TYPE_INFO = 1, // "you picked up X", exp/gold changes CHAT_TYPE_NOTICE = 2, CHAT_TYPE_PARTY = 3, CHAT_TYPE_GUILD = 4, CHAT_TYPE_COMMAND = 5, CHAT_TYPE_SHOUT = 6, CHAT_TYPE_WHISPER = 7, // client-only marker CHAT_TYPE_BIG_NOTICE = 8, }; // TPacketGCWhisper::bType (PythonChat.h) enum : uint8_t { WHISPER_TYPE_CHAT = 0, WHISPER_TYPE_SYSTEM = 1, WHISPER_TYPE_GM = 2, }; // EWindows (GameType.h) — TItemPos.window_type enum : uint8_t { WINDOW_INVENTORY = 1, WINDOW_EQUIPMENT = 2, WINDOW_SAFEBOX = 3, WINDOW_MALL = 4, WINDOW_DRAGON_SOUL = 5, WINDOW_GROUND = 6, WINDOW_BELT = 7, }; inline constexpr int ITEM_SOCKET_SLOT_MAX_NUM = 3; // GameType.h inline constexpr int ITEM_ATTRIBUTE_SLOT_MAX_NUM = 7; inline constexpr int INVENTORY_MAX_NUM = 90; // 45 x 2 pages // Packet.h retains the 11 legacy wear entries, but the enabled costume / new // equipment runtime uses positions 0..23 (body through belt). On the wire the // equipment cells are normally INVENTORY_MAX_NUM + wear. inline constexpr int WEAR_MAX_NUM = 24; inline constexpr int EQUIPMENT_SLOT_START = INVENTORY_MAX_NUM; inline constexpr int EQUIPMENT_SLOT_RESERVED_MAX_NUM = 32; // GameType.h: 90 + 32 reserved wear + 12 active DragonSoul + 18 reserved DS. inline constexpr int BELT_INVENTORY_SLOT_START = EQUIPMENT_SLOT_START + EQUIPMENT_SLOT_RESERVED_MAX_NUM + 6 * 2 + 6 * 3; // 152 inline constexpr int BELT_INVENTORY_MAX_NUM = 16; // 4 x 4 // EPointTypes (UserInterface/Packet.h) — the ones we surface. enum : uint8_t { POINT_LEVEL = 1, POINT_EXP = 3, POINT_NEXT_EXP = 4, POINT_HP = 5, POINT_MAX_HP = 6, POINT_SP = 7, POINT_MAX_SP = 8, POINT_STAMINA = 9, POINT_MAX_STAMINA = 10, POINT_GOLD = 11, POINT_ATT_SPEED = 17, POINT_SUB_SKILL = 27, // 辅助/被动技能点 SKILL_SUB — uicharacter.py skillPageStatDict["SUPPORT"] POINT_SKILL = 28, // 主动技能点 SKILL — skillPageStatDict["ACTIVE"] POINT_BOW_DISTANCE = 34, // 활 사정거리 증가치 — __GetBowRange() bonus POINT_HORSE_SKILL = 113, // 马术技能点 — skillPageStatDict["HORSE"] (Packet.h:1641) POINT_ENERGY = 128, POINT_ENERGY_END_TIME = 129, }; inline constexpr int POINT_MAX_NUM = 255; // StdAfx.h inline constexpr int SKILL_MAX_NUM = 255; // Packet.h inline constexpr int QUEST_INPUT_STRING_MAX_NUM = 64; // Packet.h enum : uint8_t { FISHING_START = 0, FISHING_STOP = 1, FISHING_REACT = 2, FISHING_SUCCESS = 3, FISHING_FAIL = 4, FISHING_FISH = 5, }; enum : uint8_t { DUNGEON_TIME_ATTACK_START = 0, DUNGEON_DESTINATION_POSITION = 1, }; // (FUNC_SKILL = 0x80 lives in entity_store.h's FUNC_* enum; CG_MOVE.func = // FUNC_SKILL | skill_motion_idx to cast.) // TPacketGCDamageInfo::flag bits (InstanceBase.h EDamageFlag). enum : uint8_t { DAMAGE_NORMAL = 1 << 0, DAMAGE_POISON = 1 << 1, DAMAGE_DODGE = 1 << 2, DAMAGE_BLOCK = 1 << 3, DAMAGE_PENETRATE = 1 << 4, DAMAGE_CRITICAL = 1 << 5, }; // Game phase — client -> server inline constexpr uint16_t CG_SYNC_POSITION = 0x0303; inline constexpr uint16_t CG_MOVE = 0x0301; inline constexpr uint16_t CG_WARP = 0x0305; inline constexpr uint16_t CG_ATTACK = 0x0401; inline constexpr uint16_t CG_USE_SKILL = 0x0402; inline constexpr uint16_t CG_SHOOT = 0x0403; inline constexpr uint16_t CG_FLY_TARGETING = 0x0404; inline constexpr uint16_t CG_ADD_FLY_TARGETING = 0x0405; inline constexpr uint16_t CG_ITEM_USE = 0x0501; inline constexpr uint16_t CG_ITEM_DROP = 0x0502; inline constexpr uint16_t CG_ITEM_DROP2 = 0x0503; inline constexpr uint16_t CG_ITEM_MOVE = 0x0504; inline constexpr uint16_t CG_ITEM_PICKUP = 0x0505; inline constexpr uint16_t CG_ITEM_USE_TO_ITEM = 0x0506; inline constexpr uint16_t CG_ITEM_GIVE = 0x0507; inline constexpr uint16_t CG_QUICKSLOT_ADD = 0x0509; inline constexpr uint16_t CG_QUICKSLOT_DEL = 0x050A; inline constexpr uint16_t CG_QUICKSLOT_SWAP = 0x050B; inline constexpr uint16_t CG_CHAT = 0x0601; inline constexpr uint16_t CG_WHISPER = 0x0602; inline constexpr uint16_t CG_SCRIPT_ANSWER = 0x0901; // dialog choice (or 255 = continue) inline constexpr uint16_t CG_SCRIPT_BUTTON = 0x0902; // quest-log button inline constexpr uint16_t CG_SCRIPT_SELECT_ITEM = 0x0903; inline constexpr uint16_t CG_QUEST_INPUT_STRING = 0x0904; inline constexpr uint16_t CG_QUEST_CONFIRM = 0x0905; inline constexpr uint16_t CG_QUEST_CANCEL = 0x0906; inline constexpr uint16_t CG_ON_CLICK = 0x0A02; // click an NPC / interactable inline constexpr uint16_t CG_TARGET = 0x0A01; inline constexpr uint16_t CG_CHARACTER_POSITION = 0x0A60; // P8 — social / shop / storage (client -> server) inline constexpr uint16_t CG_EXCHANGE = 0x0508; inline constexpr uint16_t CG_REFINE = 0x050C; // {pos, type} inline constexpr uint16_t CG_DRAGON_SOUL_REFINE = 0x050D; // {sub_type, ItemPos grid[15]} // DragonSoulSub (UserInterface/Packet.h) — GC sub-type + the CG "do" mode. enum : uint8_t { DS_SUB_OPEN = 0, DS_SUB_CLOSE = 1, DS_SUB_DO_UPGRADE = 2, DS_SUB_DO_IMPROVEMENT = 3, DS_SUB_DO_REFINE = 4, DS_SUB_REFINE_FAIL = 5, DS_SUB_REFINE_FAIL_MAX_REFINE = 6, DS_SUB_REFINE_FAIL_INVALID_MATERIAL = 7, DS_SUB_REFINE_FAIL_NOT_ENOUGH_MONEY = 8, DS_SUB_REFINE_FAIL_NOT_ENOUGH_MATERIAL = 9, DS_SUB_REFINE_FAIL_TOO_MUCH_MATERIAL = 10, DS_SUB_REFINE_SUCCEED = 11, }; inline constexpr int DS_REFINE_WINDOW_MAX_NUM = 15; inline constexpr uint16_t CG_GUILD = 0x0720; // sub-header packet inline constexpr uint16_t CG_ANSWER_MAKE_GUILD = 0x0721; // {guild_name[13]} inline constexpr uint16_t CG_SYMBOL_CRC = 0x0723; // guild symbol CRC/size query // GuildSub::GC subheaders (UserInterface/Packet.h). enum : uint8_t { GUILD_GC_LOGIN = 0, GUILD_GC_LOGOUT = 1, GUILD_GC_LIST = 2, GUILD_GC_GRADE = 3, GUILD_GC_ADD = 4, GUILD_GC_REMOVE = 5, GUILD_GC_GRADE_NAME = 6, GUILD_GC_GRADE_AUTH = 7, GUILD_GC_INFO = 8, GUILD_GC_COMMENTS = 9, GUILD_GC_CHANGE_EXP = 10, GUILD_GC_CHANGE_MEMBER_GRADE = 11, GUILD_GC_SKILL_INFO = 12, GUILD_GC_CHANGE_MEMBER_GENERAL = 13, GUILD_GC_GUILD_INVITE = 14, GUILD_GC_WAR = 15, GUILD_GC_NAME = 16, GUILD_GC_GUILD_WAR_LIST = 17, // active GvG pairs {u32 src, u32 dst}[] GUILD_GC_GUILD_WAR_END_LIST = 18, // pairs to drop GUILD_GC_WAR_POINT = 19, // GuildWarPoint GUILD_GC_MONEY_CHANGE = 20, }; // EGuildWarState (UserInterface/Packet.h). enum : uint8_t { GUILD_WAR_NONE = 0, GUILD_WAR_SEND_DECLARE = 1, GUILD_WAR_REFUSE = 2, GUILD_WAR_RECV_DECLARE = 3, GUILD_WAR_WAIT_START = 4, GUILD_WAR_CANCEL = 5, GUILD_WAR_ON_WAR = 6, GUILD_WAR_END = 7, }; inline constexpr int GUILD_SKILL_MAX_NUM = 12; // PythonGuild.h enum : uint8_t { // GuildSub::CG GUILD_CG_ADD_MEMBER = 0, GUILD_CG_REMOVE_MEMBER = 1, GUILD_CG_CHANGE_GRADE_NAME = 2, GUILD_CG_CHANGE_GRADE_AUTHORITY = 3, GUILD_CG_OFFER = 4, GUILD_CG_POST_COMMENT = 5, GUILD_CG_DELETE_COMMENT = 6, GUILD_CG_REFRESH_COMMENT = 7, GUILD_CG_CHANGE_MEMBER_GRADE = 8, GUILD_CG_USE_SKILL = 9, GUILD_CG_CHANGE_MEMBER_GENERAL = 10, GUILD_CG_INVITE_ANSWER = 11, GUILD_CG_CHARGE_GSP = 12, GUILD_CG_DEPOSIT_MONEY = 13, GUILD_CG_WITHDRAW_MONEY = 14, }; inline constexpr int GUILD_GRADE_NAME_MAX_LEN = 8; inline constexpr int REFINE_MATERIAL_MAX_NUM = 5; inline constexpr uint16_t CG_PARTY_INVITE = 0x0701; inline constexpr uint16_t CG_PARTY_INVITE_ANSWER = 0x0702; inline constexpr uint16_t CG_PARTY_REMOVE = 0x0703; // expel / leave inline constexpr uint16_t CG_PARTY_SET_STATE = 0x0704; inline constexpr uint16_t CG_PARTY_USE_SKILL = 0x0705; inline constexpr uint16_t CG_PARTY_PARAMETER = 0x0706; inline constexpr uint16_t CG_MESSENGER = 0x0740; inline constexpr uint16_t CG_SHOP = 0x0801; inline constexpr uint16_t CG_MYSHOP = 0x0802; // open a private (PC) shop inline constexpr uint16_t CG_SAFEBOX_CHECKIN = 0x0820; // inv -> safebox inline constexpr uint16_t CG_SAFEBOX_CHECKOUT = 0x0821; // safebox -> inv inline constexpr uint16_t CG_SAFEBOX_ITEM_MOVE = 0x0822; // within safebox inline constexpr uint16_t CG_MALL_CHECKOUT = 0x0840; // item-mall -> inventory #pragma pack(push, 1) struct PacketHeaderPeek { uint16_t header; }; struct DynHeader { uint16_t header; uint16_t length; }; struct GCPhase { uint16_t header; uint16_t length; uint8_t phase; }; struct GCPing { uint16_t header; uint16_t length; uint32_t server_time; }; struct CGPong { uint16_t header; uint16_t length; }; struct GCKeyChallenge { uint16_t header; uint16_t length; uint8_t server_pk[32]; uint8_t challenge[32]; uint32_t server_time; }; struct CGKeyResponse { uint16_t header; uint16_t length; uint8_t client_pk[32]; uint8_t challenge_response[32]; }; struct GCKeyComplete { uint16_t header; uint16_t length; uint8_t encrypted_token[32 + 16]; // AEAD ciphertext + tag uint8_t nonce[24]; }; // auth server: sent when server enters PHASE_AUTH struct CGLogin3 { uint16_t header; uint16_t length; char name[ID_MAX_NUM + 1]; char pwd[PASS_MAX_NUM + 1]; }; // auth server result (GC_AUTH_SUCCESS on ok, GC_LOGIN_FAILURE otherwise) struct GCAuthSuccess { uint16_t header; uint16_t length; uint32_t login_key; uint8_t result; }; // game server: name + the login_key handed back by the auth server struct CGLogin2 { uint16_t header; uint16_t length; char name[ID_MAX_NUM + 1]; uint32_t login_key; }; // UserInterface/Packet.h TSimplePlayerInformation (pack(1)) — one per char slot. struct SimplePlayerInfo { uint32_t id; char name[CHARACTER_NAME_MAX_LEN + 1]; uint8_t job; uint8_t level; uint32_t play_minutes; uint8_t st, ht, dx, iq; uint16_t main_part; uint8_t change_name; uint16_t hair_part; uint8_t dummy[4]; int32_t x, y; uint32_t addr; uint16_t port; uint8_t skill_group; }; // GC_LOGIN_SUCCESS3 — the character list for a 3-slot account. struct GCLoginSuccess3 { uint16_t header; uint16_t length; SimplePlayerInfo players[PLAYER_PER_ACCOUNT3]; uint32_t guild_id[PLAYER_PER_ACCOUNT3]; char guild_name[PLAYER_PER_ACCOUNT3][GUILD_NAME_MAX_LEN + 1]; uint32_t handle; uint32_t random_key; }; // GC_LOGIN_SUCCESS4 — the character list for a 4-slot account (some servers). struct GCLoginSuccess4 { uint16_t header; uint16_t length; SimplePlayerInfo players[PLAYER_PER_ACCOUNT4]; uint32_t guild_id[PLAYER_PER_ACCOUNT4]; char guild_name[PLAYER_PER_ACCOUNT4][GUILD_NAME_MAX_LEN + 1]; uint32_t handle; uint32_t random_key; }; struct CGCharacterSelect { uint16_t header; uint16_t length; uint8_t player_index; }; // CG_CHARACTER_CREATE — TPacketCGCreateCharacter. NOTE job is uint16 in the fork. struct CGCreateCharacter { uint16_t header; uint16_t length; uint8_t index; char name[CHARACTER_NAME_MAX_LEN + 1]; uint16_t job; uint8_t shape; uint8_t con; uint8_t intel; uint8_t str; uint8_t dex; }; // CG_CHARACTER_DELETE — TPacketCGDestroyCharacter. struct CGDeleteCharacter { uint16_t header; uint16_t length; uint8_t index; char private_code[PRIVATE_CODE_LENGTH]; }; // CG_CHANGE_NAME — rename a character slot (the server validates the card). struct CGChangeName { uint16_t header; uint16_t length; uint8_t index; char name[CHARACTER_NAME_MAX_LEN + 1]; }; // GC_CHANGE_NAME — broadcast the resulting name by player id. struct GCChangeName { uint16_t header; uint16_t length; uint32_t pid; char name[CHARACTER_NAME_MAX_LEN + 1]; }; // GC_PLAYER_CREATE_SUCCESS — TPacketGCPlayerCreateSuccess (slot + SimplePlayerInfo). struct GCPlayerCreateSuccess { uint16_t header; uint16_t length; uint8_t slot; SimplePlayerInfo player; }; // GC_PLAYER_CREATE_FAILURE — TPacketGCCreateFailure ({u8 type}). struct GCCreateFailure { uint16_t header; uint16_t length; uint8_t type; }; // GC_PLAYER_DELETE_SUCCESS — TPacketGCDestroyCharacterSuccess ({u8 account_index}). struct GCDeleteSuccess { uint16_t header; uint16_t length; uint8_t account_index; }; // CG_CLIENT_VERSION — TPacketCGClientVersion. The server validates this after // GC_MAIN_CHARACTER; without it the connection is dropped before PHASE_GAME. struct CGClientVersion { uint16_t header; uint16_t length; char filename[33]; char timestamp[33]; }; // ---- game phase --------------------------------------------------------------- // GC_MAIN_CHARACTER — spawn info for the local player (name, position, BGM). struct GCMainCharacter { uint16_t header; uint16_t length; uint32_t vid; uint16_t race; char name[CHARACTER_NAME_MAX_LEN + 1]; char bgm[MUSIC_NAME_MAX_LEN + 1]; float bgm_vol; int32_t x, y, z; uint8_t empire; uint8_t skill_group; }; // GC_CHARACTER_ADD — a nearby entity appears. Name/level/guild/mount arrive in // a following GC_CHAR_ADD_INFO; `type` is 0 for NPCs/mobs on this fork (classify // by race via mob_proto). Matches TPacketGCCharacterAdd (//comment fields are // NOT on the wire). struct GCCharacterAdd { uint16_t header; uint16_t length; uint32_t vid; float angle; int32_t x, y, z; uint8_t type; uint16_t race; uint8_t moving_speed; uint8_t attack_speed; uint8_t state_flag; uint32_t affect_flag[2]; }; // GC_CHAR_ADD_INFO — TPacketGCCharacterAdditionalInfo. Sent right after // GC_CHARACTER_ADD to fill in name / equipment / level / guild / mount. struct GCCharAddInfo { uint16_t header; uint16_t length; uint32_t vid; char name[CHARACTER_NAME_MAX_LEN + 1]; uint16_t parts[CHR_EQUIPPART_NUM]; uint8_t empire; uint32_t guild_id; uint32_t level; int16_t alignment; uint8_t pk_mode; uint32_t mount_vnum; }; // GC_CHARACTER_UPDATE — TPacketGCCharacterUpdate. Equipment / speed / guild / // alignment / mount changed for an entity already in view. struct GCCharacterUpdate { uint16_t header; uint16_t length; uint32_t vid; uint16_t parts[CHR_EQUIPPART_NUM]; uint8_t moving_speed; uint8_t attack_speed; uint8_t state_flag; uint32_t affect_flag[2]; uint32_t guild_id; int16_t alignment; uint8_t pk_mode; uint32_t mount_vnum; }; // GC_CHARACTER_ADD2 — the fat variant with name + equipment. struct GCCharacterAdd2 { uint16_t header; uint16_t length; uint32_t vid; char name[CHARACTER_NAME_MAX_LEN + 1]; float angle; int32_t x, y, z; uint8_t type; uint16_t race; uint16_t parts[CHR_EQUIPPART_NUM]; uint8_t moving_speed; uint8_t attack_speed; uint8_t state_flag; uint32_t affect_flag[2]; uint8_t empire; uint32_t guild; int16_t alignment; uint8_t pk_mode; uint32_t mount_vnum; }; struct GCCharacterDel { uint16_t header; uint16_t length; uint32_t vid; }; // GC_MOVE — an entity's movement command (server-authoritative). struct GCMove { uint16_t header; uint16_t length; uint8_t func; uint8_t arg; uint8_t rot; uint32_t vid; int32_t x, y; uint32_t time; uint32_t duration; }; // Server-authoritative movement metadata and batched position correction. struct GCCharacterPosition { uint16_t header; uint16_t length; uint32_t vid; uint8_t position; }; struct GCChangeSpeed { uint16_t header; uint16_t length; uint32_t vid; uint16_t moving_speed; }; struct GCWalkMode { uint16_t header; uint16_t length; uint32_t vid; uint8_t mode; }; struct GCSyncPosition { uint16_t header; uint16_t length; }; struct GCSyncPositionElement { uint32_t vid; int32_t x; int32_t y; }; // GC_CHAT — fixed head; szChat text follows (length - sizeof(GCChat) bytes). struct GCChat { uint16_t header; uint16_t length; uint8_t type; uint32_t vid; uint8_t empire; }; // GC_WHISPER — fixed head; message text follows (length - sizeof(GCWhisper)). struct GCWhisper { uint16_t header; uint16_t length; uint8_t type; // WHISPER_TYPE_* char from_name[CHARACTER_NAME_MAX_LEN + 1]; }; // GC_SCRIPT — NPC dialog. EventManager script text follows (src_size bytes). struct GCScript { uint16_t header; uint16_t length; uint8_t skin; uint16_t src_size; }; // GC_QUEST_CONFIRM — a yes/no prompt with a timeout. struct GCQuestConfirm { uint16_t header; uint16_t length; char msg[65]; int32_t timeout; uint32_t request_pid; }; // GC_QUEST_INFO — fixed head; then flag-driven fields (see QUEST_SEND_*). struct GCQuestInfo { uint16_t header; uint16_t length; uint16_t index; uint8_t flag; }; // --- CG quest/script intents --- struct CGOnClick { uint16_t header; uint16_t length; uint32_t vid; }; struct CGScriptAnswer { uint16_t header; uint16_t length; uint8_t answer; }; struct CGScriptButton { uint16_t header; uint16_t length; uint32_t idx; }; struct CGScriptSelectItem { uint16_t header; uint16_t length; uint32_t selection; }; struct CGQuestInputString { uint16_t header; uint16_t length; char str[QUEST_INPUT_STRING_MAX_NUM + 1]; }; struct CGQuestConfirm { uint16_t header; uint16_t length; uint8_t answer; uint32_t request_pid; }; struct CGQuestCancel { uint16_t header; uint16_t length; }; // TPacketAffectElement — one buff/debuff on the local player. struct AffectElement { uint32_t type; // affect id (often a skill vnum) uint8_t point_idx; // EPointTypes it modifies int32_t apply_value; uint32_t flag; int32_t duration; // seconds int32_t sp_cost; }; struct GCAffectAdd { uint16_t header; uint16_t length; AffectElement elem; }; struct GCAffectRemove { uint16_t header; uint16_t length; uint32_t type; uint8_t apply_on; }; // GC_SPECIAL_EFFECT — vid plays a built-in effect identified by a type id. struct GCSpecialEffect { uint16_t header; uint16_t length; uint8_t type; uint32_t vid; }; // GC_SPECIFIC_EFFECT — vid plays the effect at (a .mse path). struct GCSpecificEffect { uint16_t header; uint16_t length; uint32_t vid; char effect_file[128]; }; // GC_SKILL_LEVEL — the local player's whole skill->level table (0 = don't have). struct GCSkillLevel { uint16_t header; uint16_t length; uint8_t levels[SKILL_MAX_NUM]; }; // GC_SKILL_LEVEL_NEW — {master_type, level, next_read}[255]. The fork's on-wire // TPlayerSkill uses a 32-bit time_t, so the record is 6 bytes (not 10). struct PlayerSkill { uint8_t master_type; // 0 normal / 1 master / 2 grand master / 3 perfect master uint8_t level; uint32_t next_read; // skill-book read cooldown expiry (server epoch) }; struct GCSkillLevelNew { uint16_t header; uint16_t length; PlayerSkill skills[SKILL_MAX_NUM]; }; // TQuickSlot (GameType.h). Type: 0 none / 1 item / 2 skill / 3 command / 4 emotion. struct QuickSlot { uint8_t type; uint8_t position; }; struct GCQuickSlotAdd { uint16_t header; uint16_t length; uint8_t pos; QuickSlot slot; }; struct GCQuickSlotDel { uint16_t header; uint16_t length; uint8_t pos; }; struct GCQuickSlotSwap { uint16_t header; uint16_t length; uint8_t pos; uint8_t change_pos; }; // GC_SKILL_COOLTIME_END — skill `skill`'s cooldown has finished. struct GCSkillCooltimeEnd { uint16_t header; uint16_t length; uint8_t skill; }; struct GCChangeSkillGroup { uint16_t header; uint16_t length; uint8_t skill_group; }; // GC_CREATE_FLY — a projectile of `type` from start_vid to end_vid. struct GCCreateFly { uint16_t header; uint16_t length; uint8_t type; uint32_t start_vid; uint32_t end_vid; }; // GC_FLY_TARGETING / GC_ADD_FLY_TARGETING — a shooter locks (or appends) a // target. Coordinates are server-space centimetres when target VID is zero. struct GCFlyTargeting { uint16_t header; uint16_t length; uint32_t shooter_vid; uint32_t target_vid; int32_t x; int32_t y; }; // GC_PLAYER_POINTS — the local player's full stat array (TPacketGCPoints). // Index with EPointTypes (POINT_HP, POINT_MAX_HP, ...). struct GCPlayerPoints { uint16_t header; uint16_t length; int32_t points[POINT_MAX_NUM]; }; // GC_PLAYER_POINT_CHANGE — one point of one entity changed (TPacketGCPointChange). struct GCPointChange { uint16_t header; uint16_t length; uint32_t vid; uint8_t type; // EPointTypes int32_t amount; // delta int32_t value; // new absolute value }; // GC_STUN / GC_DEAD — an entity is stunned / died (TPacketGCStun / TPacketGCDead). struct GCStun { uint16_t header; uint16_t length; uint32_t vid; }; struct GCDead { uint16_t header; uint16_t length; uint32_t vid; }; // GC_MOTION — vid plays (optionally at victim_vid) (TPacketGCMotion). struct GCMotion { uint16_t header; uint16_t length; uint32_t vid; uint32_t victim_vid; uint16_t motion; }; // GC_DIG_MOTION — one mining action may repeat `count` times toward target_vid. struct GCDigMotion { uint16_t header; uint16_t length; uint32_t vid; uint32_t target_vid; uint8_t count; }; // GC_DAMAGE_INFO — a floating damage number over an entity (TPacketGCDamageInfo). struct GCDamageInfo { uint16_t header; uint16_t length; uint32_t vid; uint8_t flag; // EDamageFlag bits int32_t damage; }; // GC_TARGET_INFO — the currently-selected target's HP percentage (TPacketGCTarget). struct GCTargetInfo { uint16_t header; uint16_t length; uint32_t vid; uint8_t hp_percent; }; // --- items (UserInterface/Packet.h + GameType.h) ------------------------------ // SItemPos: which window + which cell. 3 bytes packed. struct ItemPos { uint8_t window_type; uint16_t cell; }; // TPlayerItemAttribute: bonus type + value. 3 bytes. struct ItemAttr { uint8_t type; int16_t value; }; // The inspect-equipment packet keeps the legacy 11 visible wear slots. This // is intentionally distinct from this client's 24 local equipment slots. inline constexpr int VIEW_EQUIP_WEAR_MAX_NUM = 11; struct EquipmentItemSet { uint32_t vnum; uint8_t count; int32_t sockets[ITEM_SOCKET_SLOT_MAX_NUM]; ItemAttr attrs[ITEM_ATTRIBUTE_SLOT_MAX_NUM]; }; struct GCViewEquip { uint16_t header; uint16_t length; uint32_t vid; EquipmentItemSet equips[VIEW_EQUIP_WEAR_MAX_NUM]; }; // GC_ITEM_SET — a slot's full contents (vnum 0 = the slot was cleared). struct GCItemSet { uint16_t header; uint16_t length; ItemPos pos; uint32_t vnum; uint8_t count; uint32_t flags; uint32_t anti_flags; uint8_t highlight; int32_t sockets[ITEM_SOCKET_SLOT_MAX_NUM]; ItemAttr attrs[ITEM_ATTRIBUTE_SLOT_MAX_NUM]; }; struct GCItemDel { uint16_t header; uint16_t length; ItemPos pos; }; // GC_ITEM_UPDATE — count / sockets / attrs of an existing slot changed. struct GCItemUpdate { uint16_t header; uint16_t length; ItemPos cell; uint8_t count; int32_t sockets[ITEM_SOCKET_SLOT_MAX_NUM]; ItemAttr attrs[ITEM_ATTRIBUTE_SLOT_MAX_NUM]; }; // GC_ITEM_USE — someone used an item (cosmetic / effect trigger). struct GCItemUse { uint16_t header; uint16_t length; ItemPos cell; uint32_t ch_vid; uint32_t victim_vid; uint32_t vnum; }; // GC_ITEM_GROUND_ADD / DEL — an item lying in the world. struct GCItemGroundAdd { uint16_t header; uint16_t length; int32_t x, y, z; uint32_t vid; uint32_t vnum; }; struct GCItemGroundDel { uint16_t header; uint16_t length; uint32_t vid; }; struct GCItemOwnership { uint16_t header; uint16_t length; uint32_t vid; char owner[CHARACTER_NAME_MAX_LEN + 1]; }; // GC_ITEM_GET — pickup notification ("You picked up N x "). struct GCItemGet { uint16_t header; uint16_t length; uint32_t vnum; uint8_t count; uint8_t arg; // 0 normal, 1 from party member char from_name[CHARACTER_NAME_MAX_LEN + 1]; }; // GC_PVP / DUEL_START — relation changes used by target / name-color rules. struct GCPvp { uint16_t header; uint16_t length; uint32_t src_vid; uint32_t dst_vid; uint8_t mode; // 0 none, 1 agree, 2 fight, 3 revenge }; struct GCDuelStart { uint16_t header; uint16_t length; }; struct GCLoverInfo { uint16_t header; uint16_t length; char name[CHARACTER_NAME_MAX_LEN + 1]; uint8_t love_point; }; struct GCLovePointUpdate { uint16_t header; uint16_t length; uint8_t love_point; }; // --- CG item intents --- struct CGItemUse { uint16_t header; uint16_t length; ItemPos pos; }; struct CGItemMove { uint16_t header; uint16_t length; ItemPos pos; ItemPos change_pos; uint8_t num; }; struct CGItemDrop { uint16_t header; uint16_t length; ItemPos pos; uint32_t gold; }; struct CGItemDrop2 { uint16_t header; uint16_t length; ItemPos pos; uint32_t gold; uint8_t count; }; struct CGItemUseToItem { uint16_t header; uint16_t length; ItemPos source_pos; ItemPos target_pos; }; struct CGGiveItem { uint16_t header; uint16_t length; uint32_t target_vid; ItemPos pos; uint8_t count; }; struct CGItemPickUp { uint16_t header; uint16_t length; uint32_t vid; }; // CG_MOVE — the local player's movement intent. struct CGMove { uint16_t header; uint16_t length; uint8_t func; uint8_t arg; uint8_t rot; int32_t x, y; uint32_t time; }; // CG_CHARACTER_POSITION — local posture/character-position update. struct CGCharacterPosition { uint16_t header; uint16_t length; uint8_t position; }; // CG_SYNC_POSITION — batched position corrections for nearby victims/entities. struct CGSyncPosition { uint16_t header; uint16_t length; }; // CG_WARP — request a server-side teleport (destination is supplied by server). struct CGWarp { uint16_t header; uint16_t length; }; struct CGSyncPositionElement { uint32_t vid; int32_t x; int32_t y; }; struct CGAttack { uint16_t header; uint16_t length; uint8_t type; uint32_t victim_vid; uint8_t crc_proc; uint8_t crc_file; }; // Skill activation keeps the original game's separate intent packets: a // target's world position is supplied first, then the skill vnum and VID. struct CGUseSkill { uint16_t header; uint16_t length; uint32_t vnum; uint32_t target_vid; }; struct CGShoot { uint16_t header; uint16_t length; uint8_t type; }; struct CGFlyTargeting { uint16_t header; uint16_t length; uint32_t target_vid; int32_t x; int32_t y; }; struct CGQuickSlotAdd { uint16_t header; uint16_t length; uint8_t pos; QuickSlot slot; }; struct CGQuickSlotDel { uint16_t header; uint16_t length; uint8_t pos; }; struct CGQuickSlotSwap { uint16_t header; uint16_t length; uint8_t pos; uint8_t change_pos; }; struct CGTarget { uint16_t header; uint16_t length; uint32_t vid; }; // CG_CHAT — fixed head; text follows. struct CGChat { uint16_t header; uint16_t length; uint8_t type; }; // CG_WHISPER — fixed head; message text follows. struct CGWhisper { uint16_t header; uint16_t length; char to_name[CHARACTER_NAME_MAX_LEN + 1]; }; // --- P8 party (UserInterface/Packet.h) --------------------------------------- struct GCPartyInvite { // someone asks you to join their party uint16_t header; uint16_t length; uint32_t leader_pid; }; struct GCPartyAdd { uint16_t header; uint16_t length; uint32_t pid; char name[CHARACTER_NAME_MAX_LEN + 1]; }; struct GCPartyUpdate { uint16_t header; uint16_t length; uint32_t pid; uint8_t state; // role bits (bit0 leader in this fork's server) uint8_t percent_hp; // 0..100 int16_t affects[PARTY_AFFECT_SLOT_MAX_NUM]; }; struct GCPartyRemove { uint16_t header; uint16_t length; uint32_t pid; }; struct GCPartyLink { uint16_t header; uint16_t length; uint32_t pid; uint32_t vid; }; struct GCPartyUnlink { uint16_t header; uint16_t length; uint32_t pid; uint32_t vid; }; struct GCPartyParameter { uint16_t header; uint16_t length; uint8_t distribute_mode; }; struct CGPartyInvite { uint16_t header; uint16_t length; uint32_t vid; }; struct CGPartyInviteAnswer { uint16_t header; uint16_t length; uint32_t leader_pid; uint8_t accept; }; struct CGPartyRemove { uint16_t header; uint16_t length; uint32_t pid; }; struct CGPartyUseSkill { uint16_t header; uint16_t length; uint8_t skill_index; uint32_t target_vid; }; struct CGPartyParameter { uint16_t header; uint16_t length; uint8_t mode; }; // TPacketCGPartySetState (Packet.h): assign/clear a party role for a member. // dwVID field actually carries the member PID (matches uiparty.py). role = PARTY_ROLE_*. struct CGPartySetState { uint16_t header; uint16_t length; uint32_t pid; uint8_t role; uint8_t on; // byFlag: 1 = set this role, 0 = clear }; // PythonPlayer.h EPartyRole enum { PARTY_ROLE_NORMAL = 0, PARTY_ROLE_LEADER = 1, PARTY_ROLE_ATTACKER = 2, PARTY_ROLE_TANKER = 3, PARTY_ROLE_BUFFER = 4, PARTY_ROLE_SKILL_MASTER = 5, PARTY_ROLE_BERSERKER = 6, PARTY_ROLE_DEFENDER = 7, PARTY_ROLE_MAX_NUM = 8, }; // --- P8 messenger / shop / exchange (sub-header packets) -------------------- struct GCSubHeader { // GC_MESSENGER / GC_SHOP fixed head; body follows uint16_t header; uint16_t length; uint8_t subheader; }; struct CGSubHeader { // CG_MESSENGER / CG_SHOP fixed head; body follows uint16_t header; uint16_t length; uint8_t subheader; }; // packet_shop_item (GameType.h TShopItemData) — one row of an NPC shop. struct ShopItem { uint32_t vnum; uint32_t price; uint8_t count; uint8_t display_pos; int32_t sockets[ITEM_SOCKET_SLOT_MAX_NUM]; ItemAttr attrs[ITEM_ATTRIBUTE_SLOT_MAX_NUM]; }; // SHOP_GC_START body: u32 owner_vid, then ShopItem[SHOP_HOST_ITEM_MAX_NUM]. // // SHOP_GC_START_EX body (Packet.h TPacketGCShopStartEx): u32 owner_vid, u8 tab_count, // then tab_count x { ShopTabHead, ShopItem[SHOP_HOST_ITEM_MAX_NUM] }. struct ShopTabHead { char name[SHOP_TAB_NAME_MAX]; uint8_t coin_type; }; // TPacketGCExchange — one exchange-window mutation (both directions carry it). struct GCExchange { uint16_t header; uint16_t length; uint8_t subheader; uint8_t is_me; uint32_t arg1; // START: partner vid; ITEM_ADD: vnum; ITEM_DEL/ACCEPT/ELK: value ItemPos arg2; // ITEM_ADD: exchange slot index in .cell uint32_t arg3; // ITEM_ADD: count int32_t sockets[ITEM_SOCKET_SLOT_MAX_NUM]; ItemAttr attrs[ITEM_ATTRIBUTE_SLOT_MAX_NUM]; }; // TPacketCGExchange struct CGExchange { uint16_t header; uint16_t length; uint8_t subheader; uint32_t arg1; // START: target vid; ELK_ADD: gold; ITEM_DEL: slot uint8_t arg2; // ITEM_ADD: display pos ItemPos pos; // ITEM_ADD: inventory position }; // --- P8 safebox ----------------------------------------------------------- struct GCSafeboxSize { uint16_t header; uint16_t length; uint8_t size; // page count }; struct GCSafeboxMoneyChange { uint16_t header; uint16_t length; int32_t money; }; struct CGSafeboxCheckin { // inventory -> safebox uint16_t header; uint16_t length; uint8_t safe_pos; ItemPos inv_pos; }; struct CGSafeboxCheckout { // safebox -> inventory uint16_t header; uint16_t length; uint8_t safe_pos; ItemPos inv_pos; }; // CG_MALL_CHECKOUT — TPacketCGMallCheckout: pull one item-mall slot to inventory. struct CGMallCheckout { uint16_t header; uint16_t length; uint8_t mall_pos; ItemPos inv_pos; }; // GC_MALL_OPEN — TPacketGCMallOpen ({u8 size}). GC_MALL_SET/DEL reuse GCItemSet/GCItemDel. struct GCMallOpen { uint16_t header; uint16_t length; uint8_t size; }; // CG_MYSHOP — TPacketCGMyShop fixed head; PRIVATE_SHOP_ITEM_MAX_NUM MyShopItem follow. struct CGMyShopHead { uint16_t header; uint16_t length; char sign[SHOP_SIGN_MAX_LEN + 1]; uint8_t count; }; // TShopItemTable (PC-shop layout). struct MyShopItem { uint32_t vnum; uint8_t count; ItemPos pos; // source cell in the seller's inventory uint32_t price; uint8_t display_pos; // slot in the shop grid }; // --- guild (GC_GUILD sub-header packets) --------------------------------- // GUILD_GC_INFO body (TPacketGCGuildInfo). struct GuildInfoBody { uint16_t member_count; uint16_t max_member_count; uint32_t guild_id; uint32_t master_pid; uint32_t exp; uint8_t level; char name[GUILD_NAME_MAX_LEN + 1]; uint32_t gold; uint8_t has_land; }; // GUILD_GC_LIST body = repeated GuildSubMember; when name_flag != 0 a char // name[65] follows the fixed part. struct GuildSubMember { uint32_t pid; uint8_t grade; uint8_t is_general; uint8_t job; uint8_t level; uint32_t offer; uint8_t name_flag; }; // GUILD_GC_GRADE body = [u8 count] then count x [u8 index][GuildSubGrade]. struct GuildSubGrade { char grade_name[GUILD_GRADE_NAME_MAX_LEN + 1]; uint8_t auth_flag; }; // GUILD_GC_SKILL_INFO body (CPythonGuild::TGuildSkillData): skill point, the 12 // guild-skill levels, then current / max guild point. struct GuildSkillInfoBody { uint8_t skill_point; uint8_t skill_level[GUILD_SKILL_MAX_NUM]; uint16_t guild_point; uint16_t max_guild_point; }; // GUILD_GC_WAR body (TPacketGCGuildWar). struct GCGuildWar { uint32_t guild_self; uint32_t guild_opp; uint8_t type; uint8_t war_state; // GUILD_WAR_* }; // GUILD_GC_WAR_POINT body (TPacketGuildWarPoint). struct GuildWarPoint { uint32_t gain_guild_id; uint32_t opp_guild_id; int32_t point; }; // GUILD_GC_GUILD_WAR_LIST / _END_LIST body element. struct GuildWarPair { uint32_t src; uint32_t dst; }; // GUILD_GC_NAME body element: {u32 id, char name[GUILD_NAME_MAX_LEN]} (no NUL). struct GuildNameEntry { uint32_t id; char name[GUILD_NAME_MAX_LEN]; }; struct CGGuild { // sub-header + variable body uint16_t header; uint16_t length; uint8_t subheader; }; struct CGAnswerMakeGuild { uint16_t header; uint16_t length; char guild_name[GUILD_NAME_MAX_LEN + 1]; }; // --- refine (GC_REFINE_INFORMATION[_NEW] / CG_REFINE) ------------------- struct RefineMaterial { uint32_t vnum; int32_t count; }; struct RefineTable { uint32_t src_vnum; uint32_t result_vnum; uint8_t material_count; int32_t cost; int32_t prob; // success % RefineMaterial materials[REFINE_MATERIAL_MAX_NUM]; }; struct GCRefineInfo { uint16_t header; uint16_t length; uint8_t type; uint8_t pos; // inventory cell of the item being refined RefineTable table; }; struct CGRefine { uint16_t header; uint16_t length; uint8_t pos; uint8_t type; }; // GC_DRAGON_SOUL_REFINE — sub_type + one item position (the affected DS on // success/fail; unused for OPEN). struct GCDragonSoulRefine { uint16_t header; uint16_t length; uint8_t sub_type; ItemPos pos; }; // CG_DRAGON_SOUL_REFINE — sub_type = the mode; grid[0] = the DS, grid[1..] mats. struct CGDragonSoulRefine { uint16_t header; uint16_t length; uint8_t sub_type; ItemPos grid[DS_REFINE_WINDOW_MAX_NUM]; }; // --- guild marks (side connection) -------------------------------------- struct CGMarkLogin { uint16_t header; uint16_t length; uint32_t handle; uint32_t random_key; }; struct CGMarkIDXList { uint16_t header; uint16_t length; }; struct CGMarkCRCList { uint16_t header; uint16_t length; uint8_t img_idx; uint32_t crclist[MARK_BLOCK_TOTAL_COUNT]; // all-zero = "send the whole image" }; // CG_MARK_UPLOAD — a single raw 16x12 RGBA guild mark (native byte order). struct CGMarkUpload { uint16_t header; uint16_t length; uint32_t gid; uint8_t image[GUILD_MARK_WIDTH * GUILD_MARK_HEIGHT * 4]; // 768 }; struct CGSymbolCRC { uint16_t header; uint16_t length; uint32_t guild_id; uint32_t crc; uint32_t size; }; struct GCSymbolData { uint16_t header; uint16_t length; uint32_t guild_id; }; // CG_GUILD_SYMBOL_UPLOAD — head only; the raw symbol file bytes follow. // `handle` actually carries the guild id (reference misnames the field). struct CGSymbolUpload { uint16_t header; uint16_t length; uint32_t handle; }; // Fixed head of GC_MARK_IDXLIST; buf_size counts the whole packet, then // `count` x { uint16_t guild_id; uint16_t mark_id }. struct GCMarkIDXList { uint16_t header; uint16_t length; uint32_t buf_size; uint16_t count; }; // Fixed head of GC_MARK_BLOCK; buf_size counts the whole packet, then // `count` x { uint8_t block_pos; uint32_t comp_size; uint8_t comp[comp_size] }. struct GCMarkBlock { uint16_t header; uint16_t length; uint32_t buf_size; uint8_t img_idx; uint32_t count; }; // Main-connection push: a guild's mark image changed; re-download. struct GCMarkUpdate { uint16_t header; uint16_t length; uint32_t guild_id; uint16_t img_idx; }; // --- P9 world systems ----------------------------------------------------- struct CGFishing { uint16_t header; uint16_t length; uint8_t dir; }; struct GCFishing { uint16_t header; uint16_t length; uint8_t subheader; uint32_t info; uint8_t dir; }; struct CGDungeon { uint16_t header; uint16_t length; }; struct GCDungeon { uint16_t header; uint16_t length; uint8_t subheader; }; // GC_LAND_LIST — fixed head followed by TLandPacketElement entries. struct GCLandList { uint16_t header; uint16_t length; }; struct LandPacketElement { uint32_t id; int32_t x, y; int32_t width, height; uint32_t guild_id; }; // Observer markers are used by the legacy minimap. Coordinates are map units; // the original client multiplies them by 100 before rendering. struct GCObserverAdd { uint16_t header; uint16_t length; uint32_t vid; uint16_t x, y; }; struct GCObserverMove { uint16_t header; uint16_t length; uint32_t vid; uint16_t x, y; }; struct GCObserverRemove { uint16_t header; uint16_t length; uint32_t vid; }; // GC_WARP — teleport. addr/port 0 = same server (just move); nonzero = reconnect. struct GCWarp { uint16_t header; uint16_t length; int32_t x, y; int32_t addr; uint16_t port; }; // GC_TIME — server wall-clock (unix epoch, seconds). Drives the day/night cycle. struct GCTime { uint16_t header; uint16_t length; int64_t time; }; // GC_CHANNEL — the channel number we're playing on. struct GCChannel { uint16_t header; uint16_t length; uint8_t channel; }; // GC_NPC_POSITION — fixed head; then `count` x TNPCPosition for the atlas/minimap. struct GCNPCPosition { uint16_t header; uint16_t length; uint16_t count; }; struct NPCPositionEntry { uint8_t type; uint32_t vnum; char name[CHARACTER_NAME_MAX_LEN + 1]; int32_t x, y; }; // GC_TARGET_CREATE (New) — a quest/world marker (id + name + optional vid + type). struct GCTargetCreate { uint16_t header; uint16_t length; int32_t id; char name[33]; uint32_t vid; uint8_t type; // CREATE_TARGET_TYPE_* }; // GC_TARGET_UPDATE — move marker `id` to (x, y). struct GCTargetUpdate { uint16_t header; uint16_t length; int32_t id; int32_t x, y; }; // GC_TARGET_DELETE — remove marker `id`. struct GCTargetDelete { uint16_t header; uint16_t length; int32_t id; }; // GC_MOUNT — vid mounts (mount_vid != 0) or dismounts (TPacketGCMount). struct GCMount { uint16_t header; uint16_t length; uint32_t vid; uint32_t mount_vid; uint8_t pos; uint32_t x, y; }; #pragma pack(pop) // Wire-layout guards. If any of these fire, the struct drifted from the client's // #pragma pack(1) definition in UserInterface/Packet.h and the parser will read // garbage. Verified 2026-08-31 against m2dev-client-src-main. static_assert(sizeof(DynHeader) == 4); static_assert(sizeof(GCKeyChallenge) == 72); static_assert(sizeof(CGKeyResponse) == 68); static_assert(sizeof(GCKeyComplete) == 76); static_assert(sizeof(CGLogin3) == 52); static_assert(sizeof(GCAuthSuccess) == 9); static_assert(sizeof(CGLogin2) == 39); static_assert(sizeof(SimplePlayerInfo) == 103); static_assert(sizeof(GCLoginSuccess3) == 4 + 3 * 103 + 3 * 4 + 3 * 13 + 4 + 4); // 372 static_assert(sizeof(GCLoginSuccess4) == 4 + 4 * 103 + 4 * 4 + 4 * 13 + 4 + 4); // 492 static_assert(sizeof(CGCharacterSelect) == 5); static_assert(sizeof(CGCreateCharacter) == 4 + 1 + 65 + 2 + 1 + 4); // 77 static_assert(sizeof(CGDeleteCharacter) == 4 + 1 + 8); // 13 static_assert(sizeof(CGChangeName) == 4 + 1 + CHARACTER_NAME_MAX_LEN + 1); // 70 static_assert(sizeof(GCChangeName) == 4 + 4 + CHARACTER_NAME_MAX_LEN + 1); // 73 static_assert(sizeof(GCPlayerCreateSuccess) == 5 + 103); // 108 static_assert(sizeof(GCCreateFailure) == 5); static_assert(sizeof(GCDeleteSuccess) == 5); static_assert(sizeof(CGClientVersion) == 4 + 33 + 33); // 70 static_assert(sizeof(GCMainCharacter) == 118); static_assert(sizeof(GCCharacterAdd) == 38); static_assert(sizeof(GCCharAddInfo) == 4 + 4 + 65 + 8 + 1 + 4 + 4 + 2 + 1 + 4); // 97 static_assert(sizeof(GCCharacterUpdate) == 4 + 4 + 8 + 1 + 1 + 1 + 8 + 4 + 2 + 1 + 4); // 38 static_assert(sizeof(GCCharacterAdd2) == 123); static_assert(sizeof(GCCharacterDel) == 8); static_assert(sizeof(GCMove) == 27); static_assert(sizeof(GCCharacterPosition) == 9); static_assert(sizeof(GCChangeSpeed) == 10); static_assert(sizeof(GCWalkMode) == 9); static_assert(sizeof(GCSyncPosition) == 4); static_assert(sizeof(GCSyncPositionElement) == 12); static_assert(sizeof(GCChat) == 10); static_assert(sizeof(CGMove) == 19); static_assert(sizeof(CGCharacterPosition) == 5); static_assert(sizeof(CGSyncPosition) == 4); static_assert(sizeof(CGWarp) == 4); static_assert(sizeof(CGSyncPositionElement) == 12); static_assert(sizeof(CGAttack) == 11); static_assert(sizeof(CGUseSkill) == 12); static_assert(sizeof(CGShoot) == 5); static_assert(sizeof(CGFlyTargeting) == 16); static_assert(sizeof(CGQuickSlotAdd) == 7); static_assert(sizeof(CGQuickSlotDel) == 5); static_assert(sizeof(CGQuickSlotSwap) == 6); static_assert(sizeof(CGTarget) == 8); static_assert(sizeof(CGChat) == 5); static_assert(sizeof(GCWhisper) == 5 + CHARACTER_NAME_MAX_LEN + 1); // 70 static_assert(sizeof(CGWhisper) == 4 + CHARACTER_NAME_MAX_LEN + 1); // 69 static_assert(sizeof(AffectElement) == 21); static_assert(sizeof(GCAffectAdd) == 25); static_assert(sizeof(GCAffectRemove) == 9); static_assert(sizeof(GCSpecialEffect) == 9); static_assert(sizeof(GCSpecificEffect) == 8 + 128); // 136 static_assert(sizeof(GCSkillLevel) == 4 + 255); // 259 static_assert(sizeof(PlayerSkill) == 6); static_assert(sizeof(GCSkillLevelNew) == 4 + 6 * 255); // 1534 static_assert(sizeof(GCQuickSlotAdd) == 7); static_assert(sizeof(GCQuickSlotDel) == 5); static_assert(sizeof(GCQuickSlotSwap) == 6); static_assert(sizeof(GCSkillCooltimeEnd) == 5); static_assert(sizeof(GCChangeSkillGroup) == 5); static_assert(sizeof(GCCreateFly) == 13); static_assert(sizeof(GCFlyTargeting) == 20); static_assert(sizeof(GCScript) == 7); static_assert(sizeof(GCQuestConfirm) == 4 + 65 + 4 + 4); // 77 static_assert(sizeof(GCQuestInfo) == 7); static_assert(sizeof(CGOnClick) == 8); static_assert(sizeof(CGScriptAnswer) == 5); static_assert(sizeof(CGScriptButton) == 8); static_assert(sizeof(CGScriptSelectItem) == 8); static_assert(sizeof(CGQuestInputString) == 4 + 65); // 69 static_assert(sizeof(CGQuestConfirm) == 9); static_assert(sizeof(CGQuestCancel) == 4); static_assert(sizeof(GCPlayerPoints) == 4 + 255 * 4); // 1024 static_assert(sizeof(GCPointChange) == 17); static_assert(sizeof(GCStun) == 8); static_assert(sizeof(GCDead) == 8); static_assert(sizeof(GCMotion) == 14); static_assert(sizeof(GCDigMotion) == 13); static_assert(sizeof(GCDamageInfo) == 13); static_assert(sizeof(GCTargetInfo) == 9); static_assert(sizeof(ItemPos) == 3); static_assert(sizeof(ItemAttr) == 3); static_assert(sizeof(EquipmentItemSet) == 4 + 1 + 12 + 21); // 38 static_assert(sizeof(GCViewEquip) == 2 + 2 + 4 + 11 * 38); // 426 static_assert(sizeof(GCItemSet) == 2 + 2 + 3 + 4 + 1 + 4 + 4 + 1 + 12 + 21); // 54 static_assert(sizeof(GCItemDel) == 7); static_assert(sizeof(GCItemUpdate) == 2 + 2 + 3 + 1 + 12 + 21); // 41 static_assert(sizeof(GCItemUse) == 19); static_assert(sizeof(GCItemGroundAdd) == 24); static_assert(sizeof(GCItemGroundDel) == 8); static_assert(sizeof(GCItemOwnership) == 4 + 4 + CHARACTER_NAME_MAX_LEN + 1); // 73 static_assert(sizeof(GCItemGet) == 2 + 2 + 4 + 1 + 1 + 65); // 75 static_assert(sizeof(GCPvp) == 13); static_assert(sizeof(GCDuelStart) == 4); static_assert(sizeof(GCLoverInfo) == 4 + CHARACTER_NAME_MAX_LEN + 1 + 1); // 70 static_assert(sizeof(GCLovePointUpdate) == 5); static_assert(sizeof(CGItemUse) == 7); static_assert(sizeof(CGItemMove) == 11); static_assert(sizeof(CGItemDrop) == 11); static_assert(sizeof(CGItemDrop2) == 12); static_assert(sizeof(CGItemUseToItem) == 10); static_assert(sizeof(CGGiveItem) == 12); static_assert(sizeof(CGItemPickUp) == 8); // P8 social / shop / storage static_assert(sizeof(GCPartyInvite) == 8); static_assert(sizeof(GCPartyAdd) == 4 + 4 + CHARACTER_NAME_MAX_LEN + 1); // 73 static_assert(sizeof(GCPartyUpdate) == 4 + 4 + 1 + 1 + 2 * PARTY_AFFECT_SLOT_MAX_NUM); // 26 static_assert(sizeof(GCPartyRemove) == 8); static_assert(sizeof(GCPartyLink) == 12); static_assert(sizeof(GCPartyUnlink) == 12); static_assert(sizeof(GCPartyParameter) == 5); static_assert(sizeof(CGPartyInvite) == 8); static_assert(sizeof(CGPartyInviteAnswer) == 9); static_assert(sizeof(CGPartyRemove) == 8); static_assert(sizeof(CGPartyUseSkill) == 9); static_assert(sizeof(CGPartyParameter) == 5); static_assert(sizeof(CGPartySetState) == 2 + 2 + 4 + 1 + 1); // 10 static_assert(sizeof(GCSubHeader) == 5); static_assert(sizeof(CGSubHeader) == 5); static_assert(sizeof(ShopItem) == 4 + 4 + 1 + 1 + 12 + 21); // 43 static_assert(sizeof(ShopTabHead) == SHOP_TAB_NAME_MAX + 1); // 33 static_assert(sizeof(GCExchange) == 4 + 1 + 1 + 4 + 3 + 4 + 12 + 21); // 50 static_assert(sizeof(CGExchange) == 4 + 1 + 4 + 1 + 3); // 13 static_assert(sizeof(GCSafeboxSize) == 5); static_assert(sizeof(GCSafeboxMoneyChange) == 8); static_assert(sizeof(CGSafeboxCheckin) == 8); static_assert(sizeof(CGMallCheckout) == 4 + 1 + 3); // 8 static_assert(sizeof(GCMallOpen) == 5); static_assert(sizeof(CGMyShopHead) == 4 + (SHOP_SIGN_MAX_LEN + 1) + 1); // 38 static_assert(sizeof(MyShopItem) == 4 + 1 + 3 + 4 + 1); // 13 static_assert(sizeof(CGSafeboxCheckout) == 8); static_assert(sizeof(GuildInfoBody) == 2 + 2 + 4 + 4 + 4 + 1 + 13 + 4 + 1); // 35 static_assert(sizeof(GuildSubMember) == 13); static_assert(sizeof(GuildSubGrade) == 10); static_assert(sizeof(GuildSkillInfoBody) == 1 + 12 + 2 + 2); // 17 static_assert(sizeof(GCGuildWar) == 10); static_assert(sizeof(GuildWarPoint) == 12); static_assert(sizeof(GuildWarPair) == 8); static_assert(sizeof(GuildNameEntry) == 4 + 12); // 16 static_assert(sizeof(CGGuild) == 5); static_assert(sizeof(CGAnswerMakeGuild) == 4 + 13); // 17 static_assert(sizeof(CGSymbolCRC) == 16); static_assert(sizeof(GCSymbolData) == 8); static_assert(sizeof(RefineMaterial) == 8); static_assert(sizeof(RefineTable) == 4 + 4 + 1 + 4 + 4 + 8 * 5); // 57 static_assert(sizeof(GCRefineInfo) == 4 + 1 + 1 + 57); // 63 static_assert(sizeof(CGRefine) == 6); static_assert(sizeof(GCDragonSoulRefine) == 4 + 1 + 3); // 8 static_assert(sizeof(CGDragonSoulRefine) == 4 + 1 + 3 * 15); // 50 static_assert(sizeof(CGMarkLogin) == 12); static_assert(sizeof(CGMarkIDXList) == 4); static_assert(sizeof(CGMarkCRCList) == 4 + 1 + 4 * 80); // 325 static_assert(sizeof(CGMarkUpload) == 4 + 4 + 768); // 776 static_assert(sizeof(CGSymbolUpload) == 8); static_assert(sizeof(GCMarkIDXList) == 10); static_assert(sizeof(GCMarkBlock) == 13); static_assert(sizeof(GCMarkUpdate) == 10); // P9 world systems static_assert(sizeof(GCWarp) == 18); static_assert(sizeof(CGFishing) == 5); static_assert(sizeof(GCFishing) == 10); static_assert(sizeof(CGDungeon) == 4); static_assert(sizeof(GCDungeon) == 5); static_assert(sizeof(GCLandList) == 4); static_assert(sizeof(LandPacketElement) == 24); static_assert(sizeof(GCObserverAdd) == 12); static_assert(sizeof(GCObserverMove) == 12); static_assert(sizeof(GCObserverRemove) == 8); static_assert(sizeof(GCTime) == 12); static_assert(sizeof(GCChannel) == 5); static_assert(sizeof(GCNPCPosition) == 6); static_assert(sizeof(NPCPositionEntry) == 1 + 4 + CHARACTER_NAME_MAX_LEN + 1 + 4 + 4); // 78 static_assert(sizeof(GCTargetCreate) == 4 + 4 + 33 + 4 + 1); // 46 static_assert(sizeof(GCTargetUpdate) == 16); static_assert(sizeof(GCTargetDelete) == 8); static_assert(sizeof(GCMount) == 21); } // namespace mtnet