2040 lines
60 KiB
C++
2040 lines
60 KiB
C++
#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 <cstdint>
|
|
|
|
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 <motion> 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 <type id>
|
|
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 <effect_file> (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 <motion> (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 <vnum>").
|
|
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
|