bring subcommand abstraction in line with main command abstraction
This commit is contained in:
+1
-1
@@ -120,7 +120,7 @@ struct Client {
|
|||||||
bool infinite_hp; // cheats enabled
|
bool infinite_hp; // cheats enabled
|
||||||
bool infinite_tp; // cheats enabled
|
bool infinite_tp; // cheats enabled
|
||||||
bool switch_assist; // cheats enabled
|
bool switch_assist; // cheats enabled
|
||||||
PSOSubcommand last_switch_enabled_subcommand[3];
|
std::string last_switch_enabled_subcommand;
|
||||||
bool can_chat;
|
bool can_chat;
|
||||||
std::string pending_bb_save_username;
|
std::string pending_bb_save_username;
|
||||||
uint8_t pending_bb_save_player_index;
|
uint8_t pending_bb_save_player_index;
|
||||||
|
|||||||
+501
-88
@@ -36,45 +36,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This function is used in a lot of places to check received command sizes and
|
|
||||||
// cast them to the appropriate type
|
|
||||||
template <typename T>
|
|
||||||
const T& check_size_t(
|
|
||||||
const std::string& data,
|
|
||||||
size_t min_size = sizeof(T),
|
|
||||||
size_t max_size = sizeof(T)) {
|
|
||||||
if (data.size() < min_size) {
|
|
||||||
throw std::runtime_error(string_printf(
|
|
||||||
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
|
||||||
min_size, data.size()));
|
|
||||||
}
|
|
||||||
if (data.size() > max_size) {
|
|
||||||
throw std::runtime_error(string_printf(
|
|
||||||
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
|
||||||
max_size, data.size()));
|
|
||||||
}
|
|
||||||
return *reinterpret_cast<const T*>(data.data());
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
T& check_size_t(
|
|
||||||
std::string& data,
|
|
||||||
size_t min_size = sizeof(T),
|
|
||||||
size_t max_size = sizeof(T)) {
|
|
||||||
if (data.size() < min_size) {
|
|
||||||
throw std::runtime_error(string_printf(
|
|
||||||
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
|
||||||
min_size, data.size()));
|
|
||||||
}
|
|
||||||
if (data.size() > max_size) {
|
|
||||||
throw std::runtime_error(string_printf(
|
|
||||||
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
|
||||||
max_size, data.size()));
|
|
||||||
}
|
|
||||||
return *reinterpret_cast<T*>(data.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 00: Invalid command
|
// 00: Invalid command
|
||||||
|
|
||||||
// 01 (S->C): Lobby message box
|
// 01 (S->C): Lobby message box
|
||||||
@@ -357,7 +318,7 @@ struct C_GuildCardSearch_40 {
|
|||||||
le_uint32_t player_tag;
|
le_uint32_t player_tag;
|
||||||
le_uint32_t searcher_guild_card_number;
|
le_uint32_t searcher_guild_card_number;
|
||||||
le_uint32_t target_guild_card_number;
|
le_uint32_t target_guild_card_number;
|
||||||
} __attribute__((packed));
|
};
|
||||||
|
|
||||||
template <typename HeaderT, typename CharT>
|
template <typename HeaderT, typename CharT>
|
||||||
struct S_GuildCardSearchResult {
|
struct S_GuildCardSearchResult {
|
||||||
@@ -1185,7 +1146,7 @@ struct S_StreamFileIndexEntry_BB_01EB {
|
|||||||
le_uint32_t checksum;
|
le_uint32_t checksum;
|
||||||
le_uint32_t offset;
|
le_uint32_t offset;
|
||||||
ptext<char, 0x40> filename;
|
ptext<char, 0x40> filename;
|
||||||
} __attribute__((packed));
|
};
|
||||||
|
|
||||||
struct S_StreamFileChunk_BB_02EB {
|
struct S_StreamFileChunk_BB_02EB {
|
||||||
le_uint32_t chunk_index;
|
le_uint32_t chunk_index;
|
||||||
@@ -1209,7 +1170,7 @@ union C_UpdateAccountData_BB_ED {
|
|||||||
parray<uint8_t, 0x38> pad_config; // 05ED
|
parray<uint8_t, 0x38> pad_config; // 05ED
|
||||||
parray<uint8_t, 0x28> tech_menu; // 06ED
|
parray<uint8_t, 0x28> tech_menu; // 06ED
|
||||||
parray<uint8_t, 0xE8> customize; // 07ED
|
parray<uint8_t, 0xE8> customize; // 07ED
|
||||||
} __attribute__((packed));
|
};
|
||||||
|
|
||||||
// EE: Scrolling message (BB)
|
// EE: Scrolling message (BB)
|
||||||
// Contents are plain text (char16_t)
|
// Contents are plain text (char16_t)
|
||||||
@@ -1234,25 +1195,62 @@ union C_UpdateAccountData_BB_ED {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Now, the game subcommands (used in commands 60, 62, 6C, 6D, C9, and CB).
|
||||||
|
// These are laid out similarly as above. These structs start with G_ to
|
||||||
|
// indicate that they are (usually) bidirectional, and are (usually) generated
|
||||||
|
// by clients and consumed by clients.
|
||||||
|
|
||||||
|
// This structure is used by many commands (noted below)
|
||||||
|
struct G_ItemSubcommand {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
|
le_uint32_t item_id;
|
||||||
|
le_uint32_t amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct S_SendGuildCard_GC {
|
|
||||||
|
// 00: Invalid subcommand
|
||||||
|
// 01: Unknown
|
||||||
|
// 02: Unknown
|
||||||
|
// 03: Unknown
|
||||||
|
// 04: Unknown
|
||||||
|
|
||||||
|
// 05: Switch state changed
|
||||||
|
|
||||||
|
// TODO: make last_switch_enabled_subcommand in both Client and Proxy use this
|
||||||
|
// when it's available
|
||||||
|
// struct G_SwitchStateChanged_6x05 {
|
||||||
|
// uint8_t subcommand;
|
||||||
|
// uint8_t size;
|
||||||
|
// TODO;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// 06: Send guild card
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
struct G_SendGuildCard_PC_GC {
|
||||||
uint8_t subcommand;
|
uint8_t subcommand;
|
||||||
uint8_t subsize;
|
uint8_t size;
|
||||||
le_uint16_t unused;
|
le_uint16_t unused;
|
||||||
le_uint32_t player_tag;
|
le_uint32_t player_tag;
|
||||||
le_uint32_t guild_card_number;
|
le_uint32_t guild_card_number;
|
||||||
ptext<char, 0x18> name;
|
ptext<CharT, 0x18> name;
|
||||||
ptext<char, 0x6C> desc;
|
ptext<CharT, 0x48> desc;
|
||||||
|
parray<uint8_t, 0x24> unused2;
|
||||||
uint8_t reserved1;
|
uint8_t reserved1;
|
||||||
uint8_t reserved2;
|
uint8_t reserved2;
|
||||||
uint8_t section_id;
|
uint8_t section_id;
|
||||||
uint8_t char_class;
|
uint8_t char_class;
|
||||||
};
|
};
|
||||||
|
struct G_SendGuildCard_PC_6x06 : G_SendGuildCard_PC_GC<char16_t> { };
|
||||||
|
struct G_SendGuildCard_GC_6x06 : G_SendGuildCard_PC_GC<char> { };
|
||||||
|
|
||||||
struct S_SendGuildCard_BB {
|
struct G_SendGuildCard_BB_6x06 {
|
||||||
uint8_t subcommand;
|
uint8_t subcommand;
|
||||||
uint8_t subsize;
|
uint8_t size;
|
||||||
le_uint16_t unused;
|
le_uint16_t unused;
|
||||||
le_uint32_t guild_card_number;
|
le_uint32_t guild_card_number;
|
||||||
ptext<char16_t, 0x18> name;
|
ptext<char16_t, 0x18> name;
|
||||||
@@ -1264,32 +1262,150 @@ struct S_SendGuildCard_BB {
|
|||||||
uint8_t char_class;
|
uint8_t char_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct S_DropItem_BB {
|
// 07: Symbol chat
|
||||||
uint8_t subcommand;
|
// 08: Unknown
|
||||||
uint8_t subsize;
|
// 09: Unknown
|
||||||
le_uint16_t unused;
|
|
||||||
uint8_t area;
|
// 0A: Enemy hit
|
||||||
uint8_t dude;
|
|
||||||
le_uint16_t request_id;
|
struct G_EnemyHitByPlayer_6x0A {
|
||||||
float x;
|
uint8_t command;
|
||||||
float y;
|
uint8_t size;
|
||||||
le_uint32_t unused2;
|
le_uint16_t enemy_id2;
|
||||||
ItemData data;
|
le_uint16_t enemy_id;
|
||||||
|
le_uint16_t damage;
|
||||||
|
le_uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct S_DropStackedItem_BB {
|
// 0B: Unknown (supported; game only)
|
||||||
|
// 0C: Add condition (poison/slow/etc.)
|
||||||
|
// 0D: Remove condition (poison/slow/etc.)
|
||||||
|
// 0E: Unknown
|
||||||
|
// 0F: Unknown
|
||||||
|
// 10: Unknown
|
||||||
|
// 11: Unknown
|
||||||
|
// 12: Unknown (supported; game only)
|
||||||
|
// 13: Unknown (supported; game only)
|
||||||
|
// 14: Unknown (supported; game only)
|
||||||
|
// 15: Unknown (supported; game only)
|
||||||
|
// 16: Unknown
|
||||||
|
// 17: Unknown (supported; game only)
|
||||||
|
// 18: Unknown (supported; game only)
|
||||||
|
// 19: Unknown (supported; game only)
|
||||||
|
// 1A: Unknown
|
||||||
|
// 1B: Unknown
|
||||||
|
// 1C: Unknown
|
||||||
|
// 1D: Unknown
|
||||||
|
// 1E: Unknown
|
||||||
|
// 1F: Unknown (supported; lobby & game)
|
||||||
|
// 20: Unknown (supported; lobby & game)
|
||||||
|
// 21: Inter-level warp
|
||||||
|
// 22: Set player visibility
|
||||||
|
// 23: Set player visibility
|
||||||
|
// 24: Unknown (supported; game only)
|
||||||
|
|
||||||
|
// 25: Equip item
|
||||||
|
// Format is G_ItemSubcommand
|
||||||
|
|
||||||
|
// 26: Unequip item
|
||||||
|
// Format is G_ItemSubcommand
|
||||||
|
|
||||||
|
// 27: Use item
|
||||||
|
// Format is G_ItemSubcommand
|
||||||
|
|
||||||
|
// 28: Feed MAG
|
||||||
|
|
||||||
|
// 29: Delete item (via bank deposit / sale / feeding MAG)
|
||||||
|
|
||||||
|
struct G_DestroyItem_6x29 {
|
||||||
uint8_t subcommand;
|
uint8_t subcommand;
|
||||||
uint8_t subsize;
|
uint8_t subsize;
|
||||||
le_uint16_t unused;
|
le_uint16_t client_id;
|
||||||
|
le_uint32_t item_id;
|
||||||
|
le_uint32_t amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2A: Drop item
|
||||||
|
|
||||||
|
struct G_PlayerDropItem_6x2A {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
|
le_uint16_t unused2; // should be 1
|
||||||
le_uint16_t area;
|
le_uint16_t area;
|
||||||
le_uint16_t unused2;
|
le_uint32_t item_id;
|
||||||
float x;
|
le_float x;
|
||||||
float y;
|
le_float y;
|
||||||
le_uint32_t unused3;
|
le_float z;
|
||||||
ItemData data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct S_PickUpItem_BB {
|
// 2B: Unknown (supported; game only)
|
||||||
|
// 2C: Talk to NPC
|
||||||
|
// 2D: Done talking to NPC
|
||||||
|
// 2E: Unknown
|
||||||
|
// 2F: Hit by enemy
|
||||||
|
|
||||||
|
// 30: Level up
|
||||||
|
|
||||||
|
struct G_LevelUp_6x30 {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
|
le_uint16_t atp;
|
||||||
|
le_uint16_t mst;
|
||||||
|
le_uint16_t evp;
|
||||||
|
le_uint16_t hp;
|
||||||
|
le_uint16_t dfp;
|
||||||
|
le_uint16_t ata;
|
||||||
|
le_uint32_t level;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 31: Medical center
|
||||||
|
// 32: Medical center
|
||||||
|
// 33: Revive player (e.g. with moon atomizer)
|
||||||
|
// 34: Unknown
|
||||||
|
// 35: Unknown
|
||||||
|
// 36: Unknown (supported; game only)
|
||||||
|
// 37: Photon blast
|
||||||
|
// 38: Unknown
|
||||||
|
// 39: Photon blast ready
|
||||||
|
// 3A: Unknown (supported; game only)
|
||||||
|
// 3B: Unknown (supported; lobby & game)
|
||||||
|
// 3C: Unknown
|
||||||
|
// 3D: Unknown
|
||||||
|
// 3E: Stop moving
|
||||||
|
// 3F: Unknown (supported; lobby & game)
|
||||||
|
// 40: Walk
|
||||||
|
// 41: Unknown
|
||||||
|
// 42: Run
|
||||||
|
// 43: Unknown (supported; lobby & game)
|
||||||
|
// 44: Unknown (supported; lobby & game)
|
||||||
|
// 45: Unknown (supported; lobby & game)
|
||||||
|
// 46: Unknown (supported; lobby & game)
|
||||||
|
// 47: Unknown (supported; lobby & game)
|
||||||
|
// 48: Use technique
|
||||||
|
// 49: Unknown (supported; lobby & game)
|
||||||
|
// 4A: Unknown (supported; lobby & game)
|
||||||
|
// 4B: Hit by enemy
|
||||||
|
// 4C: Hit by enemy
|
||||||
|
// 4D: Unknown (supported; lobby & game)
|
||||||
|
// 4E: Unknown (supported; lobby & game)
|
||||||
|
// 4F: Unknown (supported; lobby & game)
|
||||||
|
// 50: Unknown (supported; lobby & game)
|
||||||
|
// 51: Unknown
|
||||||
|
// 52: Toggle shop/bank interaction
|
||||||
|
// 53: Unknown (supported; game only)
|
||||||
|
// 54: Unknown
|
||||||
|
// 55: Intra-map warp
|
||||||
|
// 56: Unknown (supported; lobby & game)
|
||||||
|
// 57: Unknown (supported; lobby & game)
|
||||||
|
// 58: Unknown (supported; game only)
|
||||||
|
|
||||||
|
// 59: Pick up item
|
||||||
|
|
||||||
|
struct G_PickUpItem_6x59 {
|
||||||
uint8_t subcommand;
|
uint8_t subcommand;
|
||||||
uint8_t subsize;
|
uint8_t subsize;
|
||||||
le_uint16_t client_id;
|
le_uint16_t client_id;
|
||||||
@@ -1298,35 +1414,171 @@ struct S_PickUpItem_BB {
|
|||||||
le_uint32_t item_id;
|
le_uint32_t item_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct S_CreateInventoryItem_BB {
|
// 5A: Request to pick up item
|
||||||
uint8_t subcommand;
|
|
||||||
uint8_t subsize;
|
|
||||||
le_uint16_t client_id;
|
|
||||||
ItemData item;
|
|
||||||
le_uint32_t unused;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct S_DestroyItem_BB {
|
struct G_PickUpItemRequest_6x5A {
|
||||||
uint8_t subcommand;
|
uint8_t command;
|
||||||
uint8_t subsize;
|
uint8_t size;
|
||||||
le_uint16_t client_id;
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
le_uint32_t item_id;
|
le_uint32_t item_id;
|
||||||
le_uint32_t amount;
|
uint8_t area;
|
||||||
|
uint8_t unused2[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct S_BankContentsHeader_BB {
|
// 5B: Unknown
|
||||||
|
// 5C: Unknown
|
||||||
|
|
||||||
|
// 5D: Drop meseta or stacked item
|
||||||
|
|
||||||
|
struct G_DropStackedItem_6x5D {
|
||||||
uint8_t subcommand;
|
uint8_t subcommand;
|
||||||
uint8_t unused1;
|
uint8_t subsize;
|
||||||
|
le_uint16_t unused;
|
||||||
|
le_uint16_t area;
|
||||||
le_uint16_t unused2;
|
le_uint16_t unused2;
|
||||||
le_uint32_t size; // same as size in overall command header
|
le_float x;
|
||||||
le_uint32_t checksum; // can be random; client won't notice
|
le_float y;
|
||||||
le_uint32_t numItems;
|
le_uint32_t unused3;
|
||||||
le_uint32_t meseta;
|
ItemData data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct S_ShopContents_BB {
|
// 5E: Buy item at shop
|
||||||
uint8_t subcommand; // B6
|
|
||||||
uint8_t size; // 2C regardless of the number of items??
|
// 5F: Drop item from box/enemy
|
||||||
|
|
||||||
|
struct G_DropItem_6x5F {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t subsize;
|
||||||
|
le_uint16_t unused;
|
||||||
|
uint8_t area;
|
||||||
|
uint8_t enemy_instance;
|
||||||
|
le_uint16_t request_id;
|
||||||
|
le_float x;
|
||||||
|
le_float y;
|
||||||
|
le_uint32_t unused2;
|
||||||
|
ItemData data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 60: Request for item drop (handled by the server on BB)
|
||||||
|
|
||||||
|
struct G_EnemyDropItemRequest_6x60 {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
le_uint16_t unused;
|
||||||
|
uint8_t area;
|
||||||
|
uint8_t enemy_id;
|
||||||
|
le_uint16_t request_id;
|
||||||
|
le_float x;
|
||||||
|
le_float y;
|
||||||
|
le_uint32_t unknown[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 61: Feed MAG
|
||||||
|
// 62: Unknown
|
||||||
|
// 63: Destroy an item on the ground (used when too many items have been dropped)
|
||||||
|
// 64: Unknown
|
||||||
|
// 65: Unknown
|
||||||
|
// 66: Unknown
|
||||||
|
// 67: Create enemy set
|
||||||
|
// 68: Telepipe/Ryuker
|
||||||
|
// 69: Unknown
|
||||||
|
// 6A: Unknown (supported; game only)
|
||||||
|
// 6B: Unknown (used while loading into game)
|
||||||
|
// 6C: Unknown (used while loading into game)
|
||||||
|
// 6D: Unknown (used while loading into game)
|
||||||
|
// 6E: Unknown (used while loading into game)
|
||||||
|
// 6F: Unknown (used while loading into game)
|
||||||
|
// 70: Unknown (used while loading into game)
|
||||||
|
// 71: Unknown (used while loading into game)
|
||||||
|
// 72: Unknown (used while loading into game)
|
||||||
|
// 73: Invalid subcommand
|
||||||
|
// 74: Word select
|
||||||
|
// 75: Unknown (supported; game only)
|
||||||
|
// 76: Enemy killed
|
||||||
|
// 77: Sync quest data
|
||||||
|
// 78: Unknown
|
||||||
|
// 79: Lobby 14/15 soccer game
|
||||||
|
// 7A: Unknown
|
||||||
|
// 7B: Unknown
|
||||||
|
// 7C: Unknown (supported; game only)
|
||||||
|
// 7D: Unknown (supported; game only)
|
||||||
|
// 7E: Unknown
|
||||||
|
// 7F: Unknown
|
||||||
|
// 80: Trigger trap
|
||||||
|
// 81: Unknown
|
||||||
|
// 82: Unknown
|
||||||
|
// 83: Place trap
|
||||||
|
// 84: Unknown (supported; game only)
|
||||||
|
// 85: Unknown (supported; game only)
|
||||||
|
// 86: Hit destructible wall
|
||||||
|
// 87: Unknown
|
||||||
|
// 88: Unknown (supported; game only)
|
||||||
|
// 89: Unknown (supported; game only)
|
||||||
|
// 8A: Unknown
|
||||||
|
// 8B: Unknown
|
||||||
|
// 8C: Unknown
|
||||||
|
// 8D: Unknown (supported; lobby & game)
|
||||||
|
// 8E: Unknown
|
||||||
|
// 8F: Unknown
|
||||||
|
// 90: Unknown
|
||||||
|
// 91: Unknown (supported; game only)
|
||||||
|
// 92: Unknown
|
||||||
|
// 93: Timed switch activated
|
||||||
|
// 94: Warp (the $warp chat command is implemented using this)
|
||||||
|
// 95: Unknown
|
||||||
|
// 96: Unknown
|
||||||
|
// 97: Unknown
|
||||||
|
// 98: Unknown
|
||||||
|
// 99: Unknown
|
||||||
|
// 9A: Update player stat ($infhp/$inftp are implemented using this command)
|
||||||
|
// 9B: Unknown
|
||||||
|
// 9C: Unknown (supported; game only)
|
||||||
|
// 9D: Unknown
|
||||||
|
// 9E: Unknown
|
||||||
|
// 9F: Episode 2 boss actions
|
||||||
|
// A0: Episode 2 boss actions
|
||||||
|
// A1: Unknown
|
||||||
|
|
||||||
|
// A2: Request for item drop from box (handled by server on BB)
|
||||||
|
|
||||||
|
struct G_BoxItemDropRequest_6xA2 {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
le_uint16_t unused;
|
||||||
|
uint8_t area;
|
||||||
|
uint8_t unused2;
|
||||||
|
le_uint16_t request_id;
|
||||||
|
le_float x;
|
||||||
|
le_float y;
|
||||||
|
le_uint32_t unknown[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
// A3: Episode 2 boss actions
|
||||||
|
// A4: Unknown
|
||||||
|
// A5: Episode 2 boss actions
|
||||||
|
// A6: Trade proposal
|
||||||
|
// A7: Unknown
|
||||||
|
// A8: Episode 2 boss actions
|
||||||
|
// A9: Episode 2 boss actions
|
||||||
|
// AA: Episode 2 boss actions
|
||||||
|
// AB: Create lobby chair
|
||||||
|
// AC: Unknown
|
||||||
|
// AD: Unknown
|
||||||
|
// AE: Unknown (supported; lobby & game)
|
||||||
|
// AF: Turn in lobby chair
|
||||||
|
// B0: Move in lobby chair
|
||||||
|
// B1: Unknown
|
||||||
|
// B2: Unknown
|
||||||
|
// B3: Unknown
|
||||||
|
// B4: Unknown
|
||||||
|
// B5: BB shop request
|
||||||
|
|
||||||
|
// B6: BB shop contents (server->client only)
|
||||||
|
|
||||||
|
struct G_ShopContents_BB_6xB6 {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t size; // always 2C regardless of the number of items??
|
||||||
le_uint16_t params; // 037F
|
le_uint16_t params; // 037F
|
||||||
uint8_t shop_type;
|
uint8_t shop_type;
|
||||||
uint8_t num_items;
|
uint8_t num_items;
|
||||||
@@ -1334,6 +1586,167 @@ struct S_ShopContents_BB {
|
|||||||
ItemData entries[20];
|
ItemData entries[20];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// B7: BB buy shop item
|
||||||
|
|
||||||
|
// B8: Accept tekker result
|
||||||
|
// Format is G_IdentifyResult
|
||||||
|
|
||||||
|
// B9: Provisional tekker result
|
||||||
|
|
||||||
|
struct G_IdentifyResult_BB_6xB9 {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
|
ItemData item;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BA: Unknown
|
||||||
|
// BB: BB bank request
|
||||||
|
|
||||||
|
// BC: BB bank contents (server->client only)
|
||||||
|
|
||||||
|
struct G_BankContentsHeader_BB_6xBC {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t unused1;
|
||||||
|
le_uint16_t unused2;
|
||||||
|
le_uint32_t size; // same as size in overall command header
|
||||||
|
le_uint32_t checksum; // can be random; client won't notice
|
||||||
|
le_uint32_t numItems;
|
||||||
|
le_uint32_t meseta;
|
||||||
|
// Item data follows
|
||||||
|
};
|
||||||
|
|
||||||
|
// BD: BB bank action (take/deposit meseta/item)
|
||||||
|
|
||||||
|
struct G_BankAction_BB_6xBD {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t size;
|
||||||
|
le_uint16_t unused;
|
||||||
|
le_uint32_t item_id; // 0xFFFFFFFF = meseta; anything else = item
|
||||||
|
le_uint32_t meseta_amount;
|
||||||
|
uint8_t action; // 0 = deposit, 1 = take
|
||||||
|
uint8_t item_amount;
|
||||||
|
le_uint16_t unused2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BE: BB create inventory item (server->client only)
|
||||||
|
|
||||||
|
struct G_CreateInventoryItem_BB_6xBE {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t subsize;
|
||||||
|
le_uint16_t client_id;
|
||||||
|
ItemData item;
|
||||||
|
le_uint32_t unused;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BF: Ep3 change music; also BB give EXP (BB usage is server->client only)
|
||||||
|
|
||||||
|
struct G_GiveExperience_BB_6xBF {
|
||||||
|
uint8_t subcommand;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
|
le_uint32_t amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
// C0: Unknown
|
||||||
|
// C1: Unknown
|
||||||
|
// C2: Unknown
|
||||||
|
|
||||||
|
// C3: Split stacked item - not sent if entire stack is dropped
|
||||||
|
|
||||||
|
struct G_SplitStackedItem_6xC3 {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t client_id;
|
||||||
|
uint8_t unused;
|
||||||
|
le_uint16_t area;
|
||||||
|
le_uint16_t unused2;
|
||||||
|
le_float x;
|
||||||
|
le_float y;
|
||||||
|
le_uint32_t item_id;
|
||||||
|
le_uint32_t amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
// C4: Sort inventory
|
||||||
|
|
||||||
|
struct G_SortInventory_6xC4 {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
le_uint16_t unused;
|
||||||
|
le_uint32_t item_ids[30];
|
||||||
|
};
|
||||||
|
|
||||||
|
// C5: Unknown
|
||||||
|
// C6: Unknown
|
||||||
|
// C7: Unknown
|
||||||
|
|
||||||
|
// C8: Enemy killed
|
||||||
|
|
||||||
|
struct G_EnemyKilled_6xC8 {
|
||||||
|
uint8_t command;
|
||||||
|
uint8_t size;
|
||||||
|
le_uint16_t enemy_id2;
|
||||||
|
le_uint16_t enemy_id;
|
||||||
|
le_uint16_t killer_client_id;
|
||||||
|
le_uint32_t unused;
|
||||||
|
};
|
||||||
|
|
||||||
|
// C9: Unknown
|
||||||
|
// CA: Unknown
|
||||||
|
// CB: Unknown
|
||||||
|
// CC: Unknown
|
||||||
|
// CD: Unknown
|
||||||
|
// CE: Unknown
|
||||||
|
// CF: Unknown (supported; game only)
|
||||||
|
// D0: Unknown
|
||||||
|
// D1: Unknown
|
||||||
|
// D2: Unknown
|
||||||
|
// D3: Unknown
|
||||||
|
// D4: Unknown
|
||||||
|
// D5: Unknown
|
||||||
|
// D6: Unknown
|
||||||
|
// D7: Unknown
|
||||||
|
// D8: Unknown
|
||||||
|
// D9: Unknown
|
||||||
|
// DA: Unknown
|
||||||
|
// DB: Unknown
|
||||||
|
// DC: Unknown
|
||||||
|
// DD: Unknown
|
||||||
|
// DE: Unknown
|
||||||
|
// DF: Unknown
|
||||||
|
// E0: Unknown
|
||||||
|
// E1: Unknown
|
||||||
|
// E2: Unknown
|
||||||
|
// E3: Unknown
|
||||||
|
// E4: Unknown
|
||||||
|
// E5: Unknown
|
||||||
|
// E6: Unknown
|
||||||
|
// E7: Unknown
|
||||||
|
// E8: Unknown
|
||||||
|
// E9: Unknown
|
||||||
|
// EA: Unknown
|
||||||
|
// EB: Unknown
|
||||||
|
// EC: Unknown
|
||||||
|
// ED: Unknown
|
||||||
|
// EE: Unknown
|
||||||
|
// EF: Unknown
|
||||||
|
// F0: Unknown
|
||||||
|
// F1: Unknown
|
||||||
|
// F2: Unknown
|
||||||
|
// F3: Unknown
|
||||||
|
// F4: Unknown
|
||||||
|
// F5: Unknown
|
||||||
|
// F6: Unknown
|
||||||
|
// F7: Unknown
|
||||||
|
// F8: Unknown
|
||||||
|
// F9: Unknown
|
||||||
|
// FA: Unknown
|
||||||
|
// FB: Unknown
|
||||||
|
// FC: Unknown
|
||||||
|
// FD: Unknown
|
||||||
|
// FE: Unknown
|
||||||
|
// FF: Unknown
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|||||||
+1
-1
@@ -67,7 +67,7 @@ struct Lobby {
|
|||||||
size_t count_clients() const;
|
size_t count_clients() const;
|
||||||
bool any_client_loading() const;
|
bool any_client_loading() const;
|
||||||
|
|
||||||
void add_client(std::shared_ptr<Client> c, bool reverse_indexes = false);
|
void add_client(std::shared_ptr<Client> c, bool reverse_indexes = true);
|
||||||
void remove_client(std::shared_ptr<Client> c);
|
void remove_client(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void move_client_to_lobby(std::shared_ptr<Lobby> dest_lobby,
|
void move_client_to_lobby(std::shared_ptr<Lobby> dest_lobby,
|
||||||
|
|||||||
+8
-4
@@ -97,22 +97,26 @@ void populate_state_from_config(shared_ptr<ServerState> s,
|
|||||||
s->common_item_creator.reset(new CommonItemCreator(enemy_categories,
|
s->common_item_creator.reset(new CommonItemCreator(enemy_categories,
|
||||||
box_categories, unit_types));
|
box_categories, unit_types));
|
||||||
|
|
||||||
shared_ptr<vector<MenuItem>> information_menu(new vector<MenuItem>());
|
shared_ptr<vector<MenuItem>> information_menu_pc(new vector<MenuItem>());
|
||||||
|
shared_ptr<vector<MenuItem>> information_menu_gc(new vector<MenuItem>());
|
||||||
shared_ptr<vector<u16string>> information_contents(new vector<u16string>());
|
shared_ptr<vector<u16string>> information_contents(new vector<u16string>());
|
||||||
|
|
||||||
information_menu->emplace_back(INFORMATION_MENU_GO_BACK, u"Go back",
|
information_menu_gc->emplace_back(INFORMATION_MENU_GO_BACK, u"Go back",
|
||||||
u"Return to the\nmain menu", 0);
|
u"Return to the\nmain menu", 0);
|
||||||
{
|
{
|
||||||
uint32_t item_id = 0;
|
uint32_t item_id = 0;
|
||||||
for (const auto& item : d.at("InformationMenuContents")->as_list()) {
|
for (const auto& item : d.at("InformationMenuContents")->as_list()) {
|
||||||
auto& v = item->as_list();
|
auto& v = item->as_list();
|
||||||
information_menu->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
|
information_menu_pc->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
|
||||||
|
decode_sjis(v.at(1)->as_string()), 0);
|
||||||
|
information_menu_gc->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
|
||||||
decode_sjis(v.at(1)->as_string()), MenuItem::Flag::REQUIRES_MESSAGE_BOXES);
|
decode_sjis(v.at(1)->as_string()), MenuItem::Flag::REQUIRES_MESSAGE_BOXES);
|
||||||
information_contents->emplace_back(decode_sjis(v.at(2)->as_string()));
|
information_contents->emplace_back(decode_sjis(v.at(2)->as_string()));
|
||||||
item_id++;
|
item_id++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s->information_menu = information_menu;
|
s->information_menu_pc = information_menu_pc;
|
||||||
|
s->information_menu_gc = information_menu_gc;
|
||||||
s->information_contents = information_contents;
|
s->information_contents = information_contents;
|
||||||
|
|
||||||
s->proxy_destinations_menu_pc.emplace_back(PROXY_DESTINATIONS_MENU_GO_BACK,
|
s->proxy_destinations_menu_pc.emplace_back(PROXY_DESTINATIONS_MENU_GO_BACK,
|
||||||
|
|||||||
@@ -207,3 +207,19 @@ void print_received_command(
|
|||||||
|
|
||||||
print_data(stderr, w.str());
|
print_data(stderr, w.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_size_v(size_t size, size_t min_size, size_t max_size) {
|
||||||
|
if (size < min_size) {
|
||||||
|
throw std::runtime_error(string_printf(
|
||||||
|
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
||||||
|
min_size, size));
|
||||||
|
}
|
||||||
|
if (max_size < min_size) {
|
||||||
|
max_size = min_size;
|
||||||
|
}
|
||||||
|
if (size > max_size) {
|
||||||
|
throw std::runtime_error(string_printf(
|
||||||
|
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
||||||
|
max_size, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <event2/bufferevent.h>
|
#include <event2/bufferevent.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <phosg/Strings.hh>
|
||||||
|
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
@@ -64,3 +65,42 @@ void print_received_command(
|
|||||||
size_t size,
|
size_t size,
|
||||||
GameVersion version,
|
GameVersion version,
|
||||||
const char* name = nullptr);
|
const char* name = nullptr);
|
||||||
|
|
||||||
|
// This function is used in a lot of places to check received command sizes and
|
||||||
|
// cast them to the appropriate type
|
||||||
|
template <typename T>
|
||||||
|
const T& check_size_t(
|
||||||
|
const std::string& data,
|
||||||
|
size_t min_size = sizeof(T),
|
||||||
|
size_t max_size = sizeof(T)) {
|
||||||
|
if (data.size() < min_size) {
|
||||||
|
throw std::runtime_error(string_printf(
|
||||||
|
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
||||||
|
min_size, data.size()));
|
||||||
|
}
|
||||||
|
if (data.size() > max_size) {
|
||||||
|
throw std::runtime_error(string_printf(
|
||||||
|
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
||||||
|
max_size, data.size()));
|
||||||
|
}
|
||||||
|
return *reinterpret_cast<const T*>(data.data());
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
T& check_size_t(
|
||||||
|
std::string& data,
|
||||||
|
size_t min_size = sizeof(T),
|
||||||
|
size_t max_size = sizeof(T)) {
|
||||||
|
if (data.size() < min_size) {
|
||||||
|
throw std::runtime_error(string_printf(
|
||||||
|
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
||||||
|
min_size, data.size()));
|
||||||
|
}
|
||||||
|
if (data.size() > max_size) {
|
||||||
|
throw std::runtime_error(string_printf(
|
||||||
|
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
||||||
|
max_size, data.size()));
|
||||||
|
}
|
||||||
|
return *reinterpret_cast<T*>(data.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_size_v(size_t size, size_t min_size, size_t max_size = 0);
|
||||||
|
|||||||
+53
-40
@@ -417,7 +417,7 @@ void process_client_checksum(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_server_time_request(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_server_time_request(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // B1
|
uint16_t, uint32_t, const string& data) { // B1
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
send_server_time(c);
|
send_server_time(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ void process_ep3_jukebox(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_ep3_menu_challenge(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_ep3_menu_challenge(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t flag, const string& data) { // DC
|
uint16_t, uint32_t flag, const string& data) { // DC
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
if (flag != 0) {
|
if (flag != 0) {
|
||||||
send_command(c, 0xDC);
|
send_command(c, 0xDC);
|
||||||
}
|
}
|
||||||
@@ -451,7 +451,7 @@ void process_ep3_menu_challenge(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_ep3_server_data_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_ep3_server_data_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // CA
|
uint16_t, uint32_t, const string& data) { // CA
|
||||||
check_size(data.size(), 8, 0xFFFF);
|
check_size_v(data.size(), 8, 0xFFFF);
|
||||||
const PSOSubcommand* cmds = reinterpret_cast<const PSOSubcommand*>(data.data());
|
const PSOSubcommand* cmds = reinterpret_cast<const PSOSubcommand*>(data.data());
|
||||||
|
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
@@ -557,9 +557,10 @@ void process_ep3_tournament_control(shared_ptr<ServerState>, shared_ptr<Client>
|
|||||||
|
|
||||||
void process_message_box_closed(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_message_box_closed(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // D6
|
uint16_t, uint32_t, const string& data) { // D6
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
if (c->flags & Client::Flag::IN_INFORMATION_MENU) {
|
if (c->flags & Client::Flag::IN_INFORMATION_MENU) {
|
||||||
send_menu(c, u"Information", INFORMATION_MENU_ID, *s->information_menu, false);
|
send_menu(c, u"Information", INFORMATION_MENU_ID,
|
||||||
|
*s->information_menu_for_version(c->version), false);
|
||||||
} else if (c->flags & Client::Flag::AT_WELCOME_MESSAGE) {
|
} else if (c->flags & Client::Flag::AT_WELCOME_MESSAGE) {
|
||||||
send_menu(c, s->name.c_str(), MAIN_MENU_ID, s->main_menu, false);
|
send_menu(c, s->name.c_str(), MAIN_MENU_ID, s->main_menu, false);
|
||||||
c->flags &= ~Client::Flag::AT_WELCOME_MESSAGE;
|
c->flags &= ~Client::Flag::AT_WELCOME_MESSAGE;
|
||||||
@@ -601,7 +602,7 @@ void process_menu_item_info_request(shared_ptr<ServerState> s, shared_ptr<Client
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// we use item_id + 1 here because "go back" is the first item
|
// we use item_id + 1 here because "go back" is the first item
|
||||||
send_ship_info(c, s->information_menu->at(cmd.item_id + 1).description.c_str());
|
send_ship_info(c, s->information_menu_for_version(c->version)->at(cmd.item_id + 1).description.c_str());
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
send_ship_info(c, u"$C6No such information exists.");
|
send_ship_info(c, u"$C6No such information exists.");
|
||||||
}
|
}
|
||||||
@@ -664,7 +665,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
case MAIN_MENU_INFORMATION:
|
case MAIN_MENU_INFORMATION:
|
||||||
send_menu(c, u"Information", INFORMATION_MENU_ID,
|
send_menu(c, u"Information", INFORMATION_MENU_ID,
|
||||||
*s->information_menu, false);
|
*s->information_menu_for_version(c->version), true);
|
||||||
c->flags |= Client::Flag::IN_INFORMATION_MENU;
|
c->flags |= Client::Flag::IN_INFORMATION_MENU;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -874,7 +875,14 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
send_quest_file(l->clients[x], bin_basename, *bin_contents, false, false);
|
send_quest_file(l->clients[x], bin_basename, *bin_contents, false, false);
|
||||||
send_quest_file(l->clients[x], dat_basename, *dat_contents, false, false);
|
send_quest_file(l->clients[x], dat_basename, *dat_contents, false, false);
|
||||||
|
|
||||||
l->clients[x]->flags |= Client::Flag::LOADING_QUEST;
|
// There is no such thing as command AC on PSO PC - quests just start
|
||||||
|
// immediately when they're done downloading. There are also no chunk
|
||||||
|
// acknowledgements (C->S 13 commands) like there are on GC. So, for
|
||||||
|
// PC clients, we can just not set the loading flag, since we never
|
||||||
|
// need to check/clear it later.
|
||||||
|
if (l->clients[x]->version != GameVersion::PC) {
|
||||||
|
l->clients[x]->flags |= Client::Flag::LOADING_QUEST;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -918,10 +926,17 @@ void process_change_lobby(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_game_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_game_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // 08
|
uint16_t, uint32_t, const string& data) { // 08
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
send_game_menu(c, s);
|
send_game_menu(c, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void process_information_menu_request_pc(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
|
uint16_t, uint32_t, const string& data) { // 1F
|
||||||
|
check_size_v(data.size(), 0);
|
||||||
|
send_menu(c, u"Information", INFORMATION_MENU_ID,
|
||||||
|
*s->information_menu_for_version(c->version), true);
|
||||||
|
}
|
||||||
|
|
||||||
void process_change_ship(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_change_ship(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string&) { // A0
|
uint16_t, uint32_t, const string&) { // A0
|
||||||
// The client actually sends data in this command... looks like nothing
|
// The client actually sends data in this command... looks like nothing
|
||||||
@@ -950,7 +965,7 @@ void process_change_block(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_quest_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_quest_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t flag, const string& data) { // A2
|
uint16_t, uint32_t flag, const string& data) { // A2
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
if (!s->quest_index) {
|
if (!s->quest_index) {
|
||||||
send_lobby_message_box(c, u"$C6Quests are not available.");
|
send_lobby_message_box(c, u"$C6Quests are not available.");
|
||||||
@@ -985,7 +1000,7 @@ void process_quest_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_quest_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_quest_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // AC
|
uint16_t, uint32_t, const string& data) { // AC
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
if (!l || !l->is_game()) {
|
if (!l || !l->is_game()) {
|
||||||
@@ -1042,22 +1057,21 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
// autoreply text is a variable length
|
// autoreply text is a variable length
|
||||||
switch (c->version) {
|
switch (c->version) {
|
||||||
case GameVersion::PC:
|
case GameVersion::PC:
|
||||||
check_size(data.size(), sizeof(PSOPlayerDataPC),
|
check_size_v(data.size(), sizeof(PSOPlayerDataPC), 0xFFFF);
|
||||||
sizeof(PSOPlayerDataPC) + sizeof(char16_t) * c->player.auto_reply.size());
|
|
||||||
c->player.import(*reinterpret_cast<const PSOPlayerDataPC*>(data.data()));
|
c->player.import(*reinterpret_cast<const PSOPlayerDataPC*>(data.data()));
|
||||||
break;
|
break;
|
||||||
case GameVersion::GC:
|
case GameVersion::GC:
|
||||||
if (flag == 4) { // Episode 3
|
if (flag == 4) { // Episode 3
|
||||||
check_size(data.size(), sizeof(PSOPlayerDataGC) + 0x23FC);
|
check_size_v(data.size(), sizeof(PSOPlayerDataGC) + 0x23FC);
|
||||||
// TODO: import Episode 3 data somewhere
|
// TODO: import Episode 3 data somewhere
|
||||||
} else {
|
} else {
|
||||||
check_size(data.size(), sizeof(PSOPlayerDataGC),
|
check_size_v(data.size(), sizeof(PSOPlayerDataGC),
|
||||||
sizeof(PSOPlayerDataGC) + sizeof(char) * c->player.auto_reply.size());
|
sizeof(PSOPlayerDataGC) + sizeof(char) * c->player.auto_reply.size());
|
||||||
}
|
}
|
||||||
c->player.import(*reinterpret_cast<const PSOPlayerDataGC*>(data.data()));
|
c->player.import(*reinterpret_cast<const PSOPlayerDataGC*>(data.data()));
|
||||||
break;
|
break;
|
||||||
case GameVersion::BB:
|
case GameVersion::BB:
|
||||||
check_size(data.size(), sizeof(PSOPlayerDataBB),
|
check_size_v(data.size(), sizeof(PSOPlayerDataBB),
|
||||||
sizeof(PSOPlayerDataBB) + sizeof(char16_t) * c->player.auto_reply.size());
|
sizeof(PSOPlayerDataBB) + sizeof(char16_t) * c->player.auto_reply.size());
|
||||||
c->player.import(*reinterpret_cast<const PSOPlayerDataBB*>(data.data()));
|
c->player.import(*reinterpret_cast<const PSOPlayerDataBB*>(data.data()));
|
||||||
break;
|
break;
|
||||||
@@ -1108,16 +1122,14 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_game_command(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_game_command(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t command, uint32_t flag, const string& data) { // 60 62 6C 6D C9 CB (C9 CB are ep3 only)
|
uint16_t command, uint32_t flag, const string& data) { // 60 62 6C 6D C9 CB (C9 CB are ep3 only)
|
||||||
check_size(data.size(), 4, 0xFFFF);
|
check_size_v(data.size(), 4, 0xFFFF);
|
||||||
const PSOSubcommand* sub = reinterpret_cast<const PSOSubcommand*>(data.data());
|
|
||||||
|
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
if (!l) {
|
if (!l) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_subcommand(s, l, c, command, flag, sub,
|
process_subcommand(s, l, c, command, flag, data);
|
||||||
data.size() / sizeof(PSOSubcommand));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1180,7 +1192,7 @@ void process_chat_dc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_key_config_request_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_key_config_request_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) {
|
uint16_t, uint32_t, const string& data) {
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
send_team_and_key_config_bb(c);
|
send_team_and_key_config_bb(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1217,7 +1229,7 @@ void process_player_preview_request_bb(shared_ptr<ServerState>, shared_ptr<Clien
|
|||||||
|
|
||||||
void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_client_checksum_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t command, uint32_t, const string& data) {
|
uint16_t command, uint32_t, const string& data) {
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
if (command == 0x01E8) {
|
if (command == 0x01E8) {
|
||||||
send_accept_client_checksum_bb(c);
|
send_accept_client_checksum_bb(c);
|
||||||
@@ -1238,7 +1250,7 @@ void process_guild_card_data_request_bb(shared_ptr<ServerState>, shared_ptr<Clie
|
|||||||
|
|
||||||
void process_stream_file_request_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_stream_file_request_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t command, uint32_t, const string& data) {
|
uint16_t command, uint32_t, const string& data) {
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
if (command == 0x04EB) {
|
if (command == 0x04EB) {
|
||||||
send_stream_file_bb(c);
|
send_stream_file_bb(c);
|
||||||
@@ -1307,31 +1319,31 @@ void process_change_account_data_bb(shared_ptr<ServerState>, shared_ptr<Client>
|
|||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 0x01ED:
|
case 0x01ED:
|
||||||
check_size(data.size(), sizeof(cmd->option));
|
check_size_v(data.size(), sizeof(cmd->option));
|
||||||
c->player.option_flags = cmd->option;
|
c->player.option_flags = cmd->option;
|
||||||
break;
|
break;
|
||||||
case 0x02ED:
|
case 0x02ED:
|
||||||
check_size(data.size(), sizeof(cmd->symbol_chats));
|
check_size_v(data.size(), sizeof(cmd->symbol_chats));
|
||||||
c->player.symbol_chats = cmd->symbol_chats;
|
c->player.symbol_chats = cmd->symbol_chats;
|
||||||
break;
|
break;
|
||||||
case 0x03ED:
|
case 0x03ED:
|
||||||
check_size(data.size(), sizeof(cmd->chat_shortcuts));
|
check_size_v(data.size(), sizeof(cmd->chat_shortcuts));
|
||||||
c->player.shortcuts = cmd->chat_shortcuts;
|
c->player.shortcuts = cmd->chat_shortcuts;
|
||||||
break;
|
break;
|
||||||
case 0x04ED:
|
case 0x04ED:
|
||||||
check_size(data.size(), sizeof(cmd->key_config));
|
check_size_v(data.size(), sizeof(cmd->key_config));
|
||||||
c->player.key_config.key_config = cmd->key_config;
|
c->player.key_config.key_config = cmd->key_config;
|
||||||
break;
|
break;
|
||||||
case 0x05ED:
|
case 0x05ED:
|
||||||
check_size(data.size(), sizeof(cmd->pad_config));
|
check_size_v(data.size(), sizeof(cmd->pad_config));
|
||||||
c->player.key_config.joystick_config = cmd->pad_config;
|
c->player.key_config.joystick_config = cmd->pad_config;
|
||||||
break;
|
break;
|
||||||
case 0x06ED:
|
case 0x06ED:
|
||||||
check_size(data.size(), sizeof(cmd->tech_menu));
|
check_size_v(data.size(), sizeof(cmd->tech_menu));
|
||||||
c->player.tech_menu_config = cmd->tech_menu;
|
c->player.tech_menu_config = cmd->tech_menu;
|
||||||
break;
|
break;
|
||||||
case 0x07ED:
|
case 0x07ED:
|
||||||
check_size(data.size(), sizeof(cmd->customize));
|
check_size_v(data.size(), sizeof(cmd->customize));
|
||||||
c->player.disp.config = cmd->customize;
|
c->player.disp.config = cmd->customize;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1354,7 +1366,7 @@ void process_return_player_data_bb(shared_ptr<ServerState>, shared_ptr<Client> c
|
|||||||
|
|
||||||
void process_change_arrow_color(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_change_arrow_color(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t flag, const string& data) { // 89
|
uint16_t, uint32_t flag, const string& data) { // 89
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
c->lobby_arrow_color = flag;
|
c->lobby_arrow_color = flag;
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
@@ -1417,14 +1429,14 @@ void process_simple_mail(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_info_board_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_info_board_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // D8
|
uint16_t, uint32_t, const string& data) { // D8
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
send_info_board(c, s->find_lobby(c->lobby_id));
|
send_info_board(c, s->find_lobby(c->lobby_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename CharT>
|
||||||
void process_write_info_board_t(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_write_info_board_t(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // D9
|
uint16_t, uint32_t, const string& data) { // D9
|
||||||
check_size(data.size(), 0, c->player.info_board.size() * sizeof(CharT));
|
check_size_v(data.size(), 0, c->player.info_board.size() * sizeof(CharT));
|
||||||
c->player.info_board.assign(
|
c->player.info_board.assign(
|
||||||
reinterpret_cast<const CharT*>(data.data()),
|
reinterpret_cast<const CharT*>(data.data()),
|
||||||
data.size() / sizeof(CharT));
|
data.size() / sizeof(CharT));
|
||||||
@@ -1433,7 +1445,7 @@ void process_write_info_board_t(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
template <typename CharT>
|
template <typename CharT>
|
||||||
void process_set_auto_reply_t(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_set_auto_reply_t(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // C7
|
uint16_t, uint32_t, const string& data) { // C7
|
||||||
check_size(data.size(), 0, c->player.auto_reply.size() * sizeof(CharT));
|
check_size_v(data.size(), 0, c->player.auto_reply.size() * sizeof(CharT));
|
||||||
c->player.auto_reply.assign(
|
c->player.auto_reply.assign(
|
||||||
reinterpret_cast<const CharT*>(data.data()),
|
reinterpret_cast<const CharT*>(data.data()),
|
||||||
data.size() / sizeof(CharT));
|
data.size() / sizeof(CharT));
|
||||||
@@ -1441,7 +1453,7 @@ void process_set_auto_reply_t(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_disable_auto_reply(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_disable_auto_reply(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // C8
|
uint16_t, uint32_t, const string& data) { // C8
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
c->player.auto_reply.clear();
|
c->player.auto_reply.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1500,7 +1512,8 @@ shared_ptr<Lobby> create_game_generic(shared_ptr<ServerState> s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t min_level = ((episode == 0xFF) ? 0 : default_minimum_levels[episode - 1][difficulty]);
|
uint8_t min_level = ((episode == 0xFF) ? 0 : default_minimum_levels[episode - 1][difficulty]);
|
||||||
if (min_level > c->player.disp.level) {
|
if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES) &&
|
||||||
|
(min_level > c->player.disp.level)) {
|
||||||
throw invalid_argument("level too low for difficulty");
|
throw invalid_argument("level too low for difficulty");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1658,7 +1671,7 @@ void process_create_game_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_lobby_name_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_lobby_name_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // 8A
|
uint16_t, uint32_t, const string& data) { // 8A
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
if (!l) {
|
if (!l) {
|
||||||
throw invalid_argument("client not in any lobby");
|
throw invalid_argument("client not in any lobby");
|
||||||
@@ -1668,7 +1681,7 @@ void process_lobby_name_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_client_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
void process_client_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // 6F
|
uint16_t, uint32_t, const string& data) { // 6F
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
if (!l || !l->is_game()) {
|
if (!l || !l->is_game()) {
|
||||||
@@ -1700,7 +1713,7 @@ void process_team_command_bb(shared_ptr<ServerState>, shared_ptr<Client> c,
|
|||||||
|
|
||||||
void process_encryption_ok_patch(shared_ptr<ServerState>, shared_ptr<Client> c,
|
void process_encryption_ok_patch(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) {
|
uint16_t, uint32_t, const string& data) {
|
||||||
check_size(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
send_command(c, 0x04); // this requests the user's login information
|
send_command(c, 0x04); // this requests the user's login information
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1879,7 +1892,7 @@ static process_command_t pc_handlers[0x100] = {
|
|||||||
process_menu_selection, nullptr, nullptr, process_ignored_command,
|
process_menu_selection, nullptr, nullptr, process_ignored_command,
|
||||||
nullptr, nullptr, nullptr, nullptr,
|
nullptr, nullptr, nullptr, nullptr,
|
||||||
nullptr, nullptr, nullptr, nullptr,
|
nullptr, nullptr, nullptr, nullptr,
|
||||||
nullptr, process_ignored_command, nullptr, nullptr,
|
nullptr, process_ignored_command, nullptr, process_information_menu_request_pc,
|
||||||
|
|
||||||
// 20
|
// 20
|
||||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
|||||||
+194
-312
@@ -15,50 +15,51 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// The functions in this file are called when a BB client sends a game command
|
// The functions in this file are called when a client sends a game command
|
||||||
// (60, 62, 6C, or 6D) that must be handled by the server.
|
// (60, 62, 6C, or 6D).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct ItemSubcommand {
|
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint8_t client_id;
|
|
||||||
uint8_t unused;
|
|
||||||
uint32_t item_id;
|
|
||||||
uint32_t amount;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void check_size(uint16_t size, uint16_t min_size, uint16_t max_size) {
|
|
||||||
if (size < min_size) {
|
|
||||||
throw runtime_error(string_printf(
|
|
||||||
"command too small (expected at least 0x%hX bytes, received 0x%hX bytes)",
|
|
||||||
min_size, size));
|
|
||||||
}
|
|
||||||
if (max_size == 0) {
|
|
||||||
max_size = min_size;
|
|
||||||
}
|
|
||||||
if (size > max_size) {
|
|
||||||
throw runtime_error(string_printf(
|
|
||||||
"command too large (expected at most 0x%hX bytes, received 0x%hX bytes)",
|
|
||||||
max_size, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool command_is_private(uint8_t command) {
|
bool command_is_private(uint8_t command) {
|
||||||
// TODO: are either of the ep3 commands private? looks like not
|
// TODO: are either of the Ep3 commands (C9/CB) private? Looks like not...
|
||||||
return (command == 0x62) || (command == 0x6D);
|
return (command == 0x62) || (command == 0x6D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void forward_subcommand(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
template <typename CmdT = PSOSubcommand>
|
||||||
uint8_t command, uint8_t flag, const PSOSubcommand* p,
|
const CmdT* check_size_sc(
|
||||||
size_t count) {
|
const string& data,
|
||||||
|
size_t min_size = sizeof(CmdT),
|
||||||
|
size_t max_size = sizeof(CmdT),
|
||||||
|
bool check_size_field = true) {
|
||||||
|
if (max_size < min_size) {
|
||||||
|
max_size = min_size;
|
||||||
|
}
|
||||||
|
const auto* cmd = &check_size_t<CmdT>(data, min_size, max_size);
|
||||||
|
if (check_size_field && cmd->size) {
|
||||||
|
throw runtime_error("invalid subcommand size field");
|
||||||
|
}
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const PSOSubcommand* check_size_sc<PSOSubcommand>(
|
||||||
|
const string& data, size_t min_size, size_t max_size, bool check_size_field) {
|
||||||
|
if (max_size < min_size) {
|
||||||
|
max_size = min_size;
|
||||||
|
}
|
||||||
|
const auto* ret = &check_size_t<PSOSubcommand>(data, min_size, max_size);
|
||||||
|
if (check_size_field && (ret[0].byte[1] != data.size() / 4)) {
|
||||||
|
throw runtime_error("invalid subcommand size field");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void forward_subcommand(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||||
|
uint8_t command, uint8_t flag, const string& data) {
|
||||||
|
|
||||||
// if the command is an Ep3-only command, make sure an Ep3 client sent it
|
// if the command is an Ep3-only command, make sure an Ep3 client sent it
|
||||||
bool command_is_ep3 = (command & 0xF0) == 0xC0;
|
bool command_is_ep3 = (command & 0xF0) == 0xC0;
|
||||||
@@ -77,7 +78,7 @@ void forward_subcommand(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
|||||||
if (command_is_ep3 && !(target->flags & Client::Flag::EPISODE_3)) {
|
if (command_is_ep3 && !(target->flags & Client::Flag::EPISODE_3)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
send_command(target, command, flag, p, count * 4);
|
send_command(target, command, flag, data);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (command_is_ep3) {
|
if (command_is_ep3) {
|
||||||
@@ -85,11 +86,11 @@ void forward_subcommand(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
|||||||
if (!target || (target == c) || !(target->flags & Client::Flag::EPISODE_3)) {
|
if (!target || (target == c) || !(target->flags & Client::Flag::EPISODE_3)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
send_command(target, command, flag, p, count * 4);
|
send_command(target, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
send_command_excluding_client(l, c, command, flag, p, count * 4);
|
send_command_excluding_client(l, c, command, flag, data.data(), data.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,23 +100,23 @@ void forward_subcommand(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Chat commands and the like
|
// Chat commands and the like
|
||||||
|
|
||||||
// client requests to send a guild card
|
|
||||||
static void process_subcommand_send_guild_card(shared_ptr<ServerState>,
|
static void process_subcommand_send_guild_card(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
check_size(count, 9, 0xFFFF);
|
if (!command_is_private(command) || !l || (flag >= l->max_clients) ||
|
||||||
|
(!l->clients[flag])) {
|
||||||
if (!command_is_private(command) || !l || flag >= l->max_clients ||
|
|
||||||
(!l->clients[flag]) || (p->byte[1] != count)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->version == GameVersion::GC) {
|
if (c->version == GameVersion::PC) {
|
||||||
if (count < 0x25) {
|
const auto* cmd = check_size_sc<G_SendGuildCard_PC_6x06>(data);
|
||||||
return;
|
c->player.guild_card_desc = cmd->desc;
|
||||||
}
|
} else if (c->version == GameVersion::GC) {
|
||||||
c->player.guild_card_desc = decode_sjis(
|
const auto* cmd = check_size_sc<G_SendGuildCard_GC_6x06>(data);
|
||||||
reinterpret_cast<const char*>(&p[9].byte[0]), 0x58);
|
c->player.guild_card_desc = cmd->desc;
|
||||||
|
} else if (c->version == GameVersion::BB) {
|
||||||
|
const auto* cmd = check_size_sc<G_SendGuildCard_BB_6x06>(data);
|
||||||
|
c->player.guild_card_desc = cmd->desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_guild_card(l->clients[flag], c);
|
send_guild_card(l->clients[flag], c);
|
||||||
@@ -124,24 +125,22 @@ static void process_subcommand_send_guild_card(shared_ptr<ServerState>,
|
|||||||
// client sends a symbol chat
|
// client sends a symbol chat
|
||||||
static void process_subcommand_symbol_chat(shared_ptr<ServerState>,
|
static void process_subcommand_symbol_chat(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
check_size(count, 2, 0xFFFF);
|
const auto* p = check_size_sc(data, 0x08, 0xFFFF);
|
||||||
|
|
||||||
if (!c->can_chat || (p->byte[1] != count) || (p->byte[1] < 2) ||
|
if (!c->can_chat || (p[1].byte[0] != c->lobby_client_id)) {
|
||||||
(p[1].byte[0] != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// client sends a word select chat
|
// client sends a word select chat
|
||||||
static void process_subcommand_word_select(shared_ptr<ServerState>,
|
static void process_subcommand_word_select(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
check_size(count, 8, 0xFFFF);
|
const auto* p = check_size_sc(data, 0x20, 0xFFFF);
|
||||||
|
|
||||||
if (!c->can_chat || (p->byte[1] != count) || (p->byte[1] < 8) ||
|
if (!c->can_chat || (p[0].byte[2] != c->lobby_client_id)) {
|
||||||
(p->byte[2] != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +155,7 @@ static void process_subcommand_word_select(shared_ptr<ServerState>,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -165,23 +164,24 @@ static void process_subcommand_word_select(shared_ptr<ServerState>,
|
|||||||
// need to process changing areas since we keep track of where players are
|
// need to process changing areas since we keep track of where players are
|
||||||
static void process_subcommand_change_area(shared_ptr<ServerState>,
|
static void process_subcommand_change_area(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
check_size(count, 2, 0xFFFF);
|
const auto* p = check_size_sc(data, 0x08, 0xFFFF);
|
||||||
if (!l->is_game() || (p->byte[1] != count)) {
|
if (!l->is_game()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c->area = p[1].dword;
|
c->area = p[1].dword;
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when a player is hit by a monster, heal them if infinite HP is enabled
|
// when a player is hit by an enemy, heal them if infinite HP is enabled
|
||||||
static void process_subcommand_hit_by_monster(shared_ptr<ServerState>,
|
static void process_subcommand_hit_by_enemy(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
|
const auto* p = check_size_sc(data, 0x04, 0xFFFF);
|
||||||
if (!l->is_game() || (p->byte[2] != c->lobby_client_id)) {
|
if (!l->is_game() || (p->byte[2] != c->lobby_client_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->infinite_hp) {
|
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->infinite_hp) {
|
||||||
send_player_stats_change(l, c, PlayerStatsChange::ADD_HP, 1020);
|
send_player_stats_change(l, c, PlayerStatsChange::ADD_HP, 1020);
|
||||||
}
|
}
|
||||||
@@ -190,11 +190,12 @@ static void process_subcommand_hit_by_monster(shared_ptr<ServerState>,
|
|||||||
// when a player casts a tech, restore TP if infinite TP is enabled
|
// when a player casts a tech, restore TP if infinite TP is enabled
|
||||||
static void process_subcommand_use_technique(shared_ptr<ServerState>,
|
static void process_subcommand_use_technique(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (!l->is_game() || (p->byte[1] != count) || (p->byte[2] != c->lobby_client_id)) {
|
const auto* p = check_size_sc(data, 0x04, 0xFFFF);
|
||||||
|
if (!l->is_game() || (p->byte[2] != c->lobby_client_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->infinite_tp) {
|
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->infinite_tp) {
|
||||||
send_player_stats_change(l, c, PlayerStatsChange::ADD_TP, 255);
|
send_player_stats_change(l, c, PlayerStatsChange::ADD_TP, 255);
|
||||||
}
|
}
|
||||||
@@ -202,18 +203,18 @@ static void process_subcommand_use_technique(shared_ptr<ServerState>,
|
|||||||
|
|
||||||
static void process_subcommand_switch_state_changed(shared_ptr<ServerState>,
|
static void process_subcommand_switch_state_changed(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (!l->is_game() || (p->byte[1] != count)) {
|
const auto* p = check_size_sc(data, 0x0C);
|
||||||
|
if (!l->is_game()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
if ((count == 3) && (p[2].byte[3] == 1)) { // If this is a switch enable command
|
if (p[2].byte[3] == 1) { // If this is a switch enable command
|
||||||
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->switch_assist &&
|
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->switch_assist &&
|
||||||
(c->last_switch_enabled_subcommand[0].byte[0] == 0x05)) {
|
!c->last_switch_enabled_subcommand.empty()) {
|
||||||
log(INFO, "[Switch assist] Replaying previous enable command");
|
log(INFO, "[Switch assist] Replaying previous enable command");
|
||||||
forward_subcommand(l, c, command, flag, c->last_switch_enabled_subcommand, 3);
|
forward_subcommand(l, c, command, flag, c->last_switch_enabled_subcommand);
|
||||||
send_command(c, command, flag, c->last_switch_enabled_subcommand,
|
send_command(c, command, flag, c->last_switch_enabled_subcommand);
|
||||||
sizeof(c->last_switch_enabled_subcommand));
|
|
||||||
}
|
}
|
||||||
memcpy(&c->last_switch_enabled_subcommand, p, sizeof(c->last_switch_enabled_subcommand));
|
memcpy(&c->last_switch_enabled_subcommand, p, sizeof(c->last_switch_enabled_subcommand));
|
||||||
}
|
}
|
||||||
@@ -225,25 +226,11 @@ static void process_subcommand_switch_state_changed(shared_ptr<ServerState>,
|
|||||||
// player drops an item
|
// player drops an item
|
||||||
static void process_subcommand_drop_item(shared_ptr<ServerState>,
|
static void process_subcommand_drop_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 6);
|
const auto* cmd = check_size_sc<G_PlayerDropItem_6x2A>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if ((cmd->client_id != c->lobby_client_id)) {
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint8_t client_id;
|
|
||||||
uint8_t unused;
|
|
||||||
uint16_t unused2; // should be 1
|
|
||||||
uint16_t area;
|
|
||||||
uint32_t item_id;
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float z;
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if ((cmd->size != 6) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,31 +238,17 @@ static void process_subcommand_drop_item(shared_ptr<ServerState>,
|
|||||||
c->player.remove_item(cmd->item_id, 0, &item);
|
c->player.remove_item(cmd->item_id, 0, &item);
|
||||||
l->add_item(item);
|
l->add_item(item);
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// player splits a stack and drops part of it
|
// player splits a stack and drops part of it
|
||||||
static void process_subcommand_drop_stacked_item(shared_ptr<ServerState>,
|
static void process_subcommand_drop_stacked_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 6);
|
const auto* cmd = check_size_sc<G_SplitStackedItem_6xC3>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if (!l->is_game() || (cmd->client_id != c->lobby_client_id)) {
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint8_t client_id;
|
|
||||||
uint8_t unused;
|
|
||||||
uint16_t area;
|
|
||||||
uint16_t unused2;
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
uint32_t item_id;
|
|
||||||
uint32_t amount;
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if (!l->is_game() || (cmd->size != 6) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,29 +266,18 @@ static void process_subcommand_drop_stacked_item(shared_ptr<ServerState>,
|
|||||||
send_drop_stacked_item(l, item.data, cmd->area, cmd->x, cmd->y);
|
send_drop_stacked_item(l, item.data, cmd->area, cmd->x, cmd->y);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// player requests to pick up an item
|
// player requests to pick up an item
|
||||||
static void process_subcommand_pick_up_item(shared_ptr<ServerState>,
|
static void process_subcommand_pick_up_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 3);
|
auto* cmd = check_size_sc<G_PickUpItemRequest_6x5A>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if (!l->is_game() || (cmd->client_id != c->lobby_client_id)) {
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint8_t client_id;
|
|
||||||
uint8_t unused;
|
|
||||||
uint32_t item_id;
|
|
||||||
uint8_t area;
|
|
||||||
uint8_t unused2[3];
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if (!l->is_game() || (cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,19 +288,18 @@ static void process_subcommand_pick_up_item(shared_ptr<ServerState>,
|
|||||||
send_pick_up_item(l, c, item.data.item_id, cmd->area);
|
send_pick_up_item(l, c, item.data.item_id, cmd->area);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// player equips an item
|
// player equips an item
|
||||||
static void process_subcommand_equip_unequip_item(shared_ptr<ServerState>,
|
static void process_subcommand_equip_unequip_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 3);
|
const auto* cmd = check_size_sc<G_ItemSubcommand>(data);
|
||||||
|
|
||||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
if ((cmd->client_id != c->lobby_client_id)) {
|
||||||
if ((cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,18 +311,17 @@ static void process_subcommand_equip_unequip_item(shared_ptr<ServerState>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_use_item(shared_ptr<ServerState>,
|
static void process_subcommand_use_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 2);
|
const auto* cmd = check_size_sc<G_ItemSubcommand>(data);
|
||||||
|
|
||||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
if (cmd->client_id != c->lobby_client_id) {
|
||||||
if ((cmd->size != 2) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,17 +335,18 @@ static void process_subcommand_use_item(shared_ptr<ServerState>,
|
|||||||
player_use_item(c, index);
|
player_use_item(c, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_open_shop_or_ep3_unknown(shared_ptr<ServerState> s,
|
static void process_subcommand_open_shop_or_ep3_unknown(shared_ptr<ServerState> s,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
if (l->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
||||||
check_size(count, 2, 0xFFFF);
|
check_size_sc(data, 0x08, 0xFFFF);
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
const auto* p = check_size_sc(data, 0x08);
|
||||||
uint32_t shop_type = p[1].dword;
|
uint32_t shop_type = p[1].dword;
|
||||||
|
|
||||||
if ((l->version == GameVersion::BB) && l->is_game()) {
|
if ((l->version == GameVersion::BB) && l->is_game()) {
|
||||||
@@ -413,33 +374,18 @@ static void process_subcommand_open_shop_or_ep3_unknown(shared_ptr<ServerState>
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_open_bank(shared_ptr<ServerState>,
|
static void process_subcommand_open_bank(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t, uint8_t,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t, uint8_t, const string&) {
|
||||||
const PSOSubcommand*, size_t) {
|
|
||||||
if ((l->version == GameVersion::BB) && l->is_game()) {
|
if ((l->version == GameVersion::BB) && l->is_game()) {
|
||||||
send_bank(c);
|
send_bank(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// player performs some bank action
|
|
||||||
static void process_subcommand_bank_action(shared_ptr<ServerState>,
|
static void process_subcommand_bank_action(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t, uint8_t,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t, uint8_t, const string& data) {
|
||||||
const PSOSubcommand* p, size_t count) {
|
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 4);
|
const auto* cmd = check_size_sc<G_BankAction_BB_6xBD>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if (!l->is_game()) {
|
||||||
uint8_t subcommand;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t unused;
|
|
||||||
uint32_t item_id;
|
|
||||||
uint32_t meseta_amount;
|
|
||||||
uint8_t action;
|
|
||||||
uint8_t item_amount;
|
|
||||||
uint16_t unused2;
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if (!l->is_game() || (cmd->size != 4)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,21 +430,9 @@ static void process_subcommand_bank_action(shared_ptr<ServerState>,
|
|||||||
// player sorts the items in their inventory
|
// player sorts the items in their inventory
|
||||||
static void process_subcommand_sort_inventory(shared_ptr<ServerState>,
|
static void process_subcommand_sort_inventory(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t, uint8_t,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t, uint8_t,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 31);
|
const auto* cmd = check_size_sc<G_SortInventory_6xC4>(data);
|
||||||
|
|
||||||
struct Cmd {
|
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t unused;
|
|
||||||
uint32_t item_ids[30];
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if (cmd->size != 31) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInventory sorted;
|
PlayerInventory sorted;
|
||||||
memset(&sorted, 0, sizeof(PlayerInventory));
|
memset(&sorted, 0, sizeof(PlayerInventory));
|
||||||
@@ -526,24 +460,11 @@ static void process_subcommand_sort_inventory(shared_ptr<ServerState>,
|
|||||||
// enemy killed; leader sends drop item request
|
// enemy killed; leader sends drop item request
|
||||||
static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 6);
|
const auto* cmd = check_size_sc<G_EnemyDropItemRequest_6x60>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if (!l->is_game()) {
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t unused;
|
|
||||||
uint8_t area;
|
|
||||||
uint8_t monster_id;
|
|
||||||
uint16_t request_id;
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
uint32_t unknown[2];
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if ((cmd->size != 6) || !l->is_game()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,13 +477,13 @@ static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
|||||||
l->next_drop_item.data.item_data1d[0] = 0;
|
l->next_drop_item.data.item_data1d[0] = 0;
|
||||||
} else {
|
} else {
|
||||||
if (l->rare_item_set) {
|
if (l->rare_item_set) {
|
||||||
if (cmd->monster_id <= 0x65) {
|
if (cmd->enemy_id <= 0x65) {
|
||||||
is_rare = sample_rare_item(l->rare_item_set->rares[cmd->monster_id].probability);
|
is_rare = sample_rare_item(l->rare_item_set->rares[cmd->enemy_id].probability);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_rare) {
|
if (is_rare) {
|
||||||
memcpy(&item.data.item_data1d, l->rare_item_set->rares[cmd->monster_id].item_code, 3);
|
memcpy(&item.data.item_data1d, l->rare_item_set->rares[cmd->enemy_id].item_code, 3);
|
||||||
//RandPercentages();
|
//RandPercentages();
|
||||||
if (item.data.item_data1d[0] == 0) {
|
if (item.data.item_data1d[0] == 0) {
|
||||||
item.data.item_data1[4] |= 0x80; // make it untekked if it's a weapon
|
item.data.item_data1[4] |= 0x80; // make it untekked if it's a weapon
|
||||||
@@ -584,31 +505,18 @@ static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
|||||||
cmd->request_id);
|
cmd->request_id);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// box broken; leader sends drop item request
|
// box broken; leader sends drop item request
|
||||||
static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 10);
|
const auto* cmd = check_size_sc<G_BoxItemDropRequest_6xA2>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if (!l->is_game()) {
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t unused;
|
|
||||||
uint8_t area;
|
|
||||||
uint8_t unused2;
|
|
||||||
uint16_t request_id;
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
uint32_t unknown[6];
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if ((cmd->size != 10) || !l->is_game()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,31 +564,20 @@ static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
|||||||
cmd->request_id);
|
cmd->request_id);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// monster hit by player
|
// enemy hit by player
|
||||||
static void process_subcommand_monster_hit(shared_ptr<ServerState>,
|
static void process_subcommand_enemy_hit(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 10);
|
const auto* cmd = check_size_sc<G_EnemyHitByPlayer_6x0A>(data);
|
||||||
|
|
||||||
struct Cmd {
|
if (!l->is_game()) {
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t enemy_id2;
|
|
||||||
uint16_t enemy_id;
|
|
||||||
uint16_t damage;
|
|
||||||
uint32_t flags;
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if (cmd->size != 10) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->enemy_id >= l->enemies.size()) {
|
if (cmd->enemy_id >= l->enemies.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -692,35 +589,22 @@ static void process_subcommand_monster_hit(shared_ptr<ServerState>,
|
|||||||
l->enemies[cmd->enemy_id].last_hit = c->lobby_client_id;
|
l->enemies[cmd->enemy_id].last_hit = c->lobby_client_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// monster killed by player
|
// enemy killed by player
|
||||||
static void process_subcommand_monster_killed(shared_ptr<ServerState> s,
|
static void process_subcommand_enemy_killed(shared_ptr<ServerState> s,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
forward_subcommand(l, c, command, flag, data);
|
||||||
check_size(count, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
|
||||||
|
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
struct Cmd {
|
const auto* cmd = check_size_sc<G_EnemyKilled_6xC8>(data);
|
||||||
uint8_t command;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t enemy_id2;
|
|
||||||
uint16_t enemy_id;
|
|
||||||
uint16_t killer_client_id;
|
|
||||||
uint32_t unused;
|
|
||||||
} __attribute__((packed));
|
|
||||||
auto* cmd = reinterpret_cast<const Cmd*>(p);
|
|
||||||
|
|
||||||
if (!l->is_game() || (cmd->size != 3) || (cmd->enemy_id >= l->enemies.size() ||
|
if (!l->is_game() || (cmd->enemy_id >= l->enemies.size() ||
|
||||||
(l->enemies[cmd->enemy_id].hit_flags & 0x80))) {
|
(l->enemies[cmd->enemy_id].hit_flags & 0x80))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l->enemies[cmd->enemy_id].experience == 0xFFFFFFFF) {
|
if (l->enemies[cmd->enemy_id].experience == 0xFFFFFFFF) {
|
||||||
send_text_message(c, u"$C6Unknown enemy type killed");
|
send_text_message(c, u"$C6Unknown enemy type killed");
|
||||||
return;
|
return;
|
||||||
@@ -774,29 +658,25 @@ static void process_subcommand_monster_killed(shared_ptr<ServerState> s,
|
|||||||
// destroy item (sent when there are too many items on the ground)
|
// destroy item (sent when there are too many items on the ground)
|
||||||
static void process_subcommand_destroy_item(shared_ptr<ServerState>,
|
static void process_subcommand_destroy_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 3);
|
const auto* cmd = check_size_sc<G_ItemSubcommand>(data);
|
||||||
|
if (!l->is_game()) {
|
||||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
|
||||||
if ((cmd->size != 3) || !l->is_game()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
l->remove_item(cmd->item_id, nullptr);
|
l->remove_item(cmd->item_id, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// player requests to tekk an item
|
// player requests to tekk an item
|
||||||
static void process_subcommand_identify_item(shared_ptr<ServerState>,
|
static void process_subcommand_identify_item(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->version == GameVersion::BB) {
|
if (l->version == GameVersion::BB) {
|
||||||
check_size(count, 3);
|
const auto* cmd = check_size_sc<G_ItemSubcommand>(data);
|
||||||
|
if (!l->is_game() || (cmd->client_id != c->lobby_client_id)) {
|
||||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
|
||||||
if (!l->is_game() || (cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,16 +689,17 @@ static void process_subcommand_identify_item(shared_ptr<ServerState>,
|
|||||||
c->player.identify_result = c->player.inventory.items[x];
|
c->player.identify_result = c->player.inventory.items[x];
|
||||||
c->player.identify_result.data.item_data1[4] &= 0x7F;
|
c->player.identify_result.data.item_data1[4] &= 0x7F;
|
||||||
|
|
||||||
// TODO: move this into a SendCommands.cc
|
// TODO: move this into a SendCommands.cc function
|
||||||
PSOSubcommand sub[6];
|
G_IdentifyResult_BB_6xB9 res;
|
||||||
sub[0].byte[0] = 0xB9;
|
res.subcommand = 0xB9;
|
||||||
sub[0].byte[1] = 0x06;
|
res.size = sizeof(cmd) / 4;
|
||||||
sub[0].word[1] = c->lobby_client_id;
|
res.client_id = c->lobby_client_id;
|
||||||
memcpy(&sub[1], &c->player.identify_result.data, sizeof(ItemData));
|
res.unused = 0;
|
||||||
send_command(l, 0x60, 0x00, sub, 0x18);
|
res.item = c->player.identify_result.data;
|
||||||
|
send_command(l, 0x60, 0x00, res);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,13 +708,12 @@ static void process_subcommand_identify_item(shared_ptr<ServerState>,
|
|||||||
// correct though so we can just put it in the table when we figure out the id
|
// correct though so we can just put it in the table when we figure out the id
|
||||||
// static void process_subcommand_accept_identified_item(shared_ptr<ServerState> s,
|
// static void process_subcommand_accept_identified_item(shared_ptr<ServerState> s,
|
||||||
// shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
// shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
// const PSOSubcommand* p, size_t count) {
|
// const string& data) {
|
||||||
//
|
//
|
||||||
// if (l->version == GameVersion::BB) {
|
// if (l->version == GameVersion::BB) {
|
||||||
// check_size(count, 3);
|
// const auto* cmd = check_size_sc<G_ItemSubcommand>(data);
|
||||||
//
|
//
|
||||||
// auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
// if (cmd->client_id != c->lobby_client_id) {
|
||||||
// if ((cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -842,7 +722,7 @@ static void process_subcommand_identify_item(shared_ptr<ServerState>,
|
|||||||
// // TODO: what do we send to the other clients? anything?
|
// // TODO: what do we send to the other clients? anything?
|
||||||
//
|
//
|
||||||
// } else {
|
// } else {
|
||||||
// forward_subcommand(l, c, command, flag, p, count);
|
// forward_subcommand(l, c, command, flag, data);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@@ -850,79 +730,78 @@ static void process_subcommand_identify_item(shared_ptr<ServerState>,
|
|||||||
|
|
||||||
static void process_subcommand_forward_check_size(shared_ptr<ServerState>,
|
static void process_subcommand_forward_check_size(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (p->byte[1] != count) {
|
check_size_sc(data, sizeof(PSOSubcommand), 0xFFFF);
|
||||||
return;
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_forward_check_game(shared_ptr<ServerState>,
|
static void process_subcommand_forward_check_game(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (!l->is_game()) {
|
if (!l->is_game()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_forward_check_game_loading(shared_ptr<ServerState>,
|
static void process_subcommand_forward_check_game_loading(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (!l->is_game() || !l->any_client_loading()) {
|
if (!l->is_game() || !l->any_client_loading()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_forward_check_size_client(shared_ptr<ServerState>,
|
static void process_subcommand_forward_check_size_client(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if ((p->byte[1] != count) || (p->byte[2] != c->lobby_client_id)) {
|
const auto* p = check_size_sc(data, sizeof(PSOSubcommand), 0xFFFF);
|
||||||
|
if (p->byte[2] != c->lobby_client_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_forward_check_size_game(shared_ptr<ServerState>,
|
static void process_subcommand_forward_check_size_game(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (!l->is_game() || (p->byte[1] != count)) {
|
check_size_sc(data, sizeof(PSOSubcommand), 0xFFFF);
|
||||||
|
if (!l->is_game()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_forward_check_size_ep3_lobby(shared_ptr<ServerState>,
|
static void process_subcommand_forward_check_size_ep3_lobby(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY) || (p->byte[1] != count)) {
|
check_size_sc(data, sizeof(PSOSubcommand), 0xFFFF);
|
||||||
|
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forward_subcommand(l, c, command, flag, p, count);
|
forward_subcommand(l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_invalid(shared_ptr<ServerState>,
|
static void process_subcommand_invalid(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby>, shared_ptr<Client>, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby>, shared_ptr<Client>, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
|
const auto* p = check_size_sc(data, sizeof(PSOSubcommand), 0xFFFF);
|
||||||
if (command_is_private(command)) {
|
if (command_is_private(command)) {
|
||||||
log(WARNING, "Invalid subcommand: %02hhX (%zu of them) (private to player %hhu)",
|
log(WARNING, "Invalid subcommand: %02hhX (private to %hhu)", p->byte[0], flag);
|
||||||
p->byte[0], count, flag);
|
|
||||||
} else {
|
} else {
|
||||||
log(WARNING, "Invalid subcommand: %02hhX (%zu of them) (public)",
|
log(WARNING, "Invalid subcommand: %02hhX (public)", p->byte[0]);
|
||||||
p->byte[0], count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_subcommand_unimplemented(shared_ptr<ServerState>,
|
static void process_subcommand_unimplemented(shared_ptr<ServerState>,
|
||||||
shared_ptr<Lobby>, shared_ptr<Client>, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby>, shared_ptr<Client>, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count) {
|
const string& data) {
|
||||||
|
const auto* p = check_size_sc(data, sizeof(PSOSubcommand), 0xFFFF);
|
||||||
if (command_is_private(command)) {
|
if (command_is_private(command)) {
|
||||||
log(WARNING, "Unknown subcommand: %02hhX (%zu of them) (private to player %hhu)",
|
log(WARNING, "Unknown subcommand: %02hhX (private to %hhu)", p->byte[0], flag);
|
||||||
p->byte[0], count, flag);
|
|
||||||
} else {
|
} else {
|
||||||
log(WARNING, "Unknown subcommand: %02hhX (%zu of them) (public)",
|
log(WARNING, "Unknown subcommand: %02hhX (public)", p->byte[0]);
|
||||||
p->byte[0], count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -933,7 +812,7 @@ static void process_subcommand_unimplemented(shared_ptr<ServerState>,
|
|||||||
// for more information on flags. The maximum size is not enforced if it's zero.
|
// for more information on flags. The maximum size is not enforced if it's zero.
|
||||||
typedef void (*subcommand_handler_t)(shared_ptr<ServerState> s,
|
typedef void (*subcommand_handler_t)(shared_ptr<ServerState> s,
|
||||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||||
const PSOSubcommand* p, size_t count);
|
const string& data);
|
||||||
|
|
||||||
subcommand_handler_t subcommand_handlers[0x100] = {
|
subcommand_handler_t subcommand_handlers[0x100] = {
|
||||||
/* 00 */ process_subcommand_invalid,
|
/* 00 */ process_subcommand_invalid,
|
||||||
@@ -946,7 +825,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* 07 */ process_subcommand_symbol_chat,
|
/* 07 */ process_subcommand_symbol_chat,
|
||||||
/* 08 */ process_subcommand_unimplemented,
|
/* 08 */ process_subcommand_unimplemented,
|
||||||
/* 09 */ process_subcommand_unimplemented,
|
/* 09 */ process_subcommand_unimplemented,
|
||||||
/* 0A */ process_subcommand_monster_hit,
|
/* 0A */ process_subcommand_enemy_hit,
|
||||||
/* 0B */ process_subcommand_forward_check_size_game,
|
/* 0B */ process_subcommand_forward_check_size_game,
|
||||||
/* 0C */ process_subcommand_forward_check_size_game, // Add condition (poison/slow/etc.)
|
/* 0C */ process_subcommand_forward_check_size_game, // Add condition (poison/slow/etc.)
|
||||||
/* 0D */ process_subcommand_forward_check_size_game, // Remove condition (poison/slow/etc.)
|
/* 0D */ process_subcommand_forward_check_size_game, // Remove condition (poison/slow/etc.)
|
||||||
@@ -983,7 +862,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* 2C */ process_subcommand_forward_check_size, // Talk to NPC
|
/* 2C */ process_subcommand_forward_check_size, // Talk to NPC
|
||||||
/* 2D */ process_subcommand_forward_check_size, // Done talking to NPC
|
/* 2D */ process_subcommand_forward_check_size, // Done talking to NPC
|
||||||
/* 2E */ process_subcommand_unimplemented,
|
/* 2E */ process_subcommand_unimplemented,
|
||||||
/* 2F */ process_subcommand_hit_by_monster,
|
/* 2F */ process_subcommand_hit_by_enemy,
|
||||||
/* 30 */ process_subcommand_forward_check_size_game, // Level up
|
/* 30 */ process_subcommand_forward_check_size_game, // Level up
|
||||||
/* 31 */ process_subcommand_forward_check_size_game, // Medical center
|
/* 31 */ process_subcommand_forward_check_size_game, // Medical center
|
||||||
/* 32 */ process_subcommand_forward_check_size_game, // Medical center
|
/* 32 */ process_subcommand_forward_check_size_game, // Medical center
|
||||||
@@ -1011,8 +890,8 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* 48 */ process_subcommand_use_technique,
|
/* 48 */ process_subcommand_use_technique,
|
||||||
/* 49 */ process_subcommand_forward_check_size_client,
|
/* 49 */ process_subcommand_forward_check_size_client,
|
||||||
/* 4A */ process_subcommand_forward_check_size_client,
|
/* 4A */ process_subcommand_forward_check_size_client,
|
||||||
/* 4B */ process_subcommand_hit_by_monster,
|
/* 4B */ process_subcommand_hit_by_enemy,
|
||||||
/* 4C */ process_subcommand_hit_by_monster,
|
/* 4C */ process_subcommand_hit_by_enemy,
|
||||||
/* 4D */ process_subcommand_forward_check_size_client,
|
/* 4D */ process_subcommand_forward_check_size_client,
|
||||||
/* 4E */ process_subcommand_forward_check_size_client,
|
/* 4E */ process_subcommand_forward_check_size_client,
|
||||||
/* 4F */ process_subcommand_forward_check_size_client,
|
/* 4F */ process_subcommand_forward_check_size_client,
|
||||||
@@ -1031,7 +910,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* 5C */ process_subcommand_unimplemented,
|
/* 5C */ process_subcommand_unimplemented,
|
||||||
/* 5D */ process_subcommand_forward_check_size_game, // Drop meseta or stacked item
|
/* 5D */ process_subcommand_forward_check_size_game, // Drop meseta or stacked item
|
||||||
/* 5E */ process_subcommand_forward_check_size_game, // Buy item at shop
|
/* 5E */ process_subcommand_forward_check_size_game, // Buy item at shop
|
||||||
/* 5F */ process_subcommand_forward_check_size_game, // Drop item from box/monster
|
/* 5F */ process_subcommand_forward_check_size_game, // Drop item from box/enemy
|
||||||
/* 60 */ process_subcommand_enemy_drop_item, // Request for item drop (handled by the server on BB)
|
/* 60 */ process_subcommand_enemy_drop_item, // Request for item drop (handled by the server on BB)
|
||||||
/* 61 */ process_subcommand_forward_check_size_game, // Feed mag
|
/* 61 */ process_subcommand_forward_check_size_game, // Feed mag
|
||||||
/* 62 */ process_subcommand_unimplemented,
|
/* 62 */ process_subcommand_unimplemented,
|
||||||
@@ -1054,7 +933,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* 73 */ process_subcommand_invalid,
|
/* 73 */ process_subcommand_invalid,
|
||||||
/* 74 */ process_subcommand_word_select,
|
/* 74 */ process_subcommand_word_select,
|
||||||
/* 75 */ process_subcommand_forward_check_size_game,
|
/* 75 */ process_subcommand_forward_check_size_game,
|
||||||
/* 76 */ process_subcommand_forward_check_size_game, // Monster killed
|
/* 76 */ process_subcommand_forward_check_size_game, // Enemy killed
|
||||||
/* 77 */ process_subcommand_forward_check_size_game, // Sync quest data
|
/* 77 */ process_subcommand_forward_check_size_game, // Sync quest data
|
||||||
/* 78 */ process_subcommand_unimplemented,
|
/* 78 */ process_subcommand_unimplemented,
|
||||||
/* 79 */ process_subcommand_forward_check_size, // Lobby 14/15 soccer game
|
/* 79 */ process_subcommand_forward_check_size, // Lobby 14/15 soccer game
|
||||||
@@ -1136,7 +1015,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
/* C5 */ process_subcommand_unimplemented,
|
/* C5 */ process_subcommand_unimplemented,
|
||||||
/* C6 */ process_subcommand_unimplemented,
|
/* C6 */ process_subcommand_unimplemented,
|
||||||
/* C7 */ process_subcommand_unimplemented,
|
/* C7 */ process_subcommand_unimplemented,
|
||||||
/* C8 */ process_subcommand_monster_killed,
|
/* C8 */ process_subcommand_enemy_killed,
|
||||||
/* C9 */ process_subcommand_unimplemented,
|
/* C9 */ process_subcommand_unimplemented,
|
||||||
/* CA */ process_subcommand_unimplemented,
|
/* CA */ process_subcommand_unimplemented,
|
||||||
/* CB */ process_subcommand_unimplemented,
|
/* CB */ process_subcommand_unimplemented,
|
||||||
@@ -1195,9 +1074,12 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void process_subcommand(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
void process_subcommand(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||||
shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
shared_ptr<Client> c, uint8_t command, uint8_t flag, const string& data) {
|
||||||
const PSOSubcommand* sub, size_t count) {
|
if (data.empty()) {
|
||||||
subcommand_handlers[sub->byte[0]](s, l, c, command, flag, sub, count);
|
throw runtime_error("game command is empty");
|
||||||
|
}
|
||||||
|
uint8_t which = static_cast<uint8_t>(data[0]);
|
||||||
|
subcommand_handlers[which](s, l, c, command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subcommand_is_implemented(uint8_t which) {
|
bool subcommand_is_implemented(uint8_t which) {
|
||||||
|
|||||||
@@ -7,10 +7,9 @@
|
|||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
|
|
||||||
void check_size(uint16_t size, uint16_t min_size, uint16_t max_size = 0);
|
|
||||||
|
|
||||||
void process_subcommand(std::shared_ptr<ServerState> s,
|
void process_subcommand(std::shared_ptr<ServerState> s,
|
||||||
std::shared_ptr<Lobby> l, std::shared_ptr<Client> c, uint8_t command,
|
std::shared_ptr<Lobby> l, std::shared_ptr<Client> c, uint8_t command,
|
||||||
uint8_t flag, const PSOSubcommand* sub, size_t count);
|
uint8_t flag, const std::string& data);
|
||||||
|
|
||||||
bool subcommand_is_implemented(uint8_t which);
|
bool subcommand_is_implemented(uint8_t which);
|
||||||
|
|||||||
+52
-40
@@ -598,42 +598,45 @@ void send_card_search_result(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void send_guild_card_gc(shared_ptr<Client> c, shared_ptr<Client> source) {
|
template <typename CmdT>
|
||||||
S_SendGuildCard_GC cmd;
|
void send_guild_card_pc_gc(shared_ptr<Client> c, shared_ptr<Client> source) {
|
||||||
|
CmdT cmd;
|
||||||
cmd.subcommand = 0x06;
|
cmd.subcommand = 0x06;
|
||||||
cmd.subsize = 0x25;
|
cmd.size = sizeof(CmdT) / 4;
|
||||||
cmd.unused = 0x0000;
|
cmd.unused = 0x0000;
|
||||||
cmd.player_tag = 0x00010000;
|
cmd.player_tag = 0x00010000;
|
||||||
cmd.reserved1 = 1;
|
|
||||||
cmd.reserved2 = 1;
|
|
||||||
cmd.guild_card_number = source->license->serial_number;
|
cmd.guild_card_number = source->license->serial_number;
|
||||||
cmd.name = source->player.disp.name;
|
cmd.name = source->player.disp.name;
|
||||||
remove_language_marker_inplace(cmd.name);
|
remove_language_marker_inplace(cmd.name);
|
||||||
cmd.desc = source->player.guild_card_desc;
|
cmd.desc = source->player.guild_card_desc;
|
||||||
|
cmd.reserved1 = 1;
|
||||||
|
cmd.reserved2 = 1;
|
||||||
cmd.section_id = source->player.disp.section_id;
|
cmd.section_id = source->player.disp.section_id;
|
||||||
cmd.char_class = source->player.disp.char_class;
|
cmd.char_class = source->player.disp.char_class;
|
||||||
send_command(c, 0x62, c->lobby_client_id, cmd);
|
send_command(c, 0x62, c->lobby_client_id, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_guild_card_bb(shared_ptr<Client> c, shared_ptr<Client> source) {
|
void send_guild_card_bb(shared_ptr<Client> c, shared_ptr<Client> source) {
|
||||||
S_SendGuildCard_BB cmd;
|
G_SendGuildCard_BB_6x06 cmd;
|
||||||
cmd.subcommand = 0x06;
|
cmd.subcommand = 0x06;
|
||||||
cmd.subsize = 0x43;
|
cmd.size = sizeof(cmd) / 4;
|
||||||
cmd.unused = 0x0000;
|
cmd.unused = 0x0000;
|
||||||
cmd.reserved1 = 1;
|
|
||||||
cmd.reserved2 = 1;
|
|
||||||
cmd.guild_card_number = source->license->serial_number;
|
cmd.guild_card_number = source->license->serial_number;
|
||||||
cmd.name = remove_language_marker(source->player.disp.name);
|
cmd.name = remove_language_marker(source->player.disp.name);
|
||||||
cmd.team_name = remove_language_marker(source->player.team_name);
|
cmd.team_name = remove_language_marker(source->player.team_name);
|
||||||
cmd.desc = source->player.guild_card_desc;
|
cmd.desc = source->player.guild_card_desc;
|
||||||
|
cmd.reserved1 = 1;
|
||||||
|
cmd.reserved2 = 1;
|
||||||
cmd.section_id = source->player.disp.section_id;
|
cmd.section_id = source->player.disp.section_id;
|
||||||
cmd.char_class = source->player.disp.char_class;
|
cmd.char_class = source->player.disp.char_class;
|
||||||
send_command(c, 0x62, c->lobby_client_id, cmd);
|
send_command(c, 0x62, c->lobby_client_id, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_guild_card(shared_ptr<Client> c, shared_ptr<Client> source) {
|
void send_guild_card(shared_ptr<Client> c, shared_ptr<Client> source) {
|
||||||
if (c->version == GameVersion::GC) {
|
if (c->version == GameVersion::PC) {
|
||||||
send_guild_card_gc(c, source);
|
send_guild_card_pc_gc<G_SendGuildCard_PC_6x06>(c, source);
|
||||||
|
} else if (c->version == GameVersion::GC) {
|
||||||
|
send_guild_card_pc_gc<G_SendGuildCard_GC_6x06>(c, source);
|
||||||
} else if (c->version == GameVersion::BB) {
|
} else if (c->version == GameVersion::BB) {
|
||||||
send_guild_card_bb(c, source);
|
send_guild_card_bb(c, source);
|
||||||
} else {
|
} else {
|
||||||
@@ -1118,7 +1121,7 @@ void send_revive_player(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
|||||||
// notifies other players of a dropped item from a box or enemy
|
// notifies other players of a dropped item from a box or enemy
|
||||||
void send_drop_item(shared_ptr<Lobby> l, const ItemData& item,
|
void send_drop_item(shared_ptr<Lobby> l, const ItemData& item,
|
||||||
bool from_enemy, uint8_t area, float x, float y, uint16_t request_id) {
|
bool from_enemy, uint8_t area, float x, float y, uint16_t request_id) {
|
||||||
S_DropItem_BB cmd = {
|
G_DropItem_6x5F cmd = {
|
||||||
0x5F, 0x0A, 0x0000, area, from_enemy, request_id, x, y, 0, item};
|
0x5F, 0x0A, 0x0000, area, from_enemy, request_id, x, y, 0, item};
|
||||||
send_command(l, 0x60, 0x00, cmd);
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
}
|
}
|
||||||
@@ -1126,7 +1129,7 @@ void send_drop_item(shared_ptr<Lobby> l, const ItemData& item,
|
|||||||
// notifies other players that a stack was split and part of it dropped (a new item was created)
|
// notifies other players that a stack was split and part of it dropped (a new item was created)
|
||||||
void send_drop_stacked_item(shared_ptr<Lobby> l, const ItemData& item,
|
void send_drop_stacked_item(shared_ptr<Lobby> l, const ItemData& item,
|
||||||
uint8_t area, float x, float y) {
|
uint8_t area, float x, float y) {
|
||||||
S_DropStackedItem_BB cmd = {
|
G_DropStackedItem_6x5D cmd = {
|
||||||
0x5D, 0x09, 0x0000, area, 0, x, y, 0, item};
|
0x5D, 0x09, 0x0000, area, 0, x, y, 0, item};
|
||||||
send_command(l, 0x60, 0x00, cmd);
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
}
|
}
|
||||||
@@ -1134,7 +1137,7 @@ void send_drop_stacked_item(shared_ptr<Lobby> l, const ItemData& item,
|
|||||||
// notifies other players that an item was picked up
|
// notifies other players that an item was picked up
|
||||||
void send_pick_up_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
void send_pick_up_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||||
uint32_t item_id, uint8_t area) {
|
uint32_t item_id, uint8_t area) {
|
||||||
S_PickUpItem_BB cmd = {
|
G_PickUpItem_6x59 cmd = {
|
||||||
0x59, 0x03, c->lobby_client_id, c->lobby_client_id, area, item_id};
|
0x59, 0x03, c->lobby_client_id, c->lobby_client_id, area, item_id};
|
||||||
send_command(l, 0x60, 0x00, cmd);
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
}
|
}
|
||||||
@@ -1142,7 +1145,7 @@ void send_pick_up_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
|||||||
// creates an item in a player's inventory (used for withdrawing items from the bank)
|
// creates an item in a player's inventory (used for withdrawing items from the bank)
|
||||||
void send_create_inventory_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
void send_create_inventory_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||||
const ItemData& item) {
|
const ItemData& item) {
|
||||||
S_CreateInventoryItem_BB cmd = {
|
G_CreateInventoryItem_BB_6xBE cmd = {
|
||||||
0xBE, 0x07, c->lobby_client_id, item, 0};
|
0xBE, 0x07, c->lobby_client_id, item, 0};
|
||||||
send_command(l, 0x60, 0x00, cmd);
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
}
|
}
|
||||||
@@ -1150,7 +1153,7 @@ void send_create_inventory_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
|||||||
// destroys an item
|
// destroys an item
|
||||||
void send_destroy_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
void send_destroy_item(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||||
uint32_t item_id, uint32_t amount) {
|
uint32_t item_id, uint32_t amount) {
|
||||||
S_DestroyItem_BB cmd = {
|
G_DestroyItem_6x29 cmd = {
|
||||||
0x29, 0x03, c->lobby_client_id, item_id, amount};
|
0x29, 0x03, c->lobby_client_id, item_id, amount};
|
||||||
send_command(l, 0x60, 0x00, cmd);
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
}
|
}
|
||||||
@@ -1161,7 +1164,7 @@ void send_bank(shared_ptr<Client> c) {
|
|||||||
&c->player.bank.items[c->player.bank.num_items]);
|
&c->player.bank.items[c->player.bank.num_items]);
|
||||||
|
|
||||||
uint32_t checksum = random_object<uint32_t>();
|
uint32_t checksum = random_object<uint32_t>();
|
||||||
S_BankContentsHeader_BB cmd = {
|
G_BankContentsHeader_BB_6xBC cmd = {
|
||||||
0xBC, 0, 0, 0, checksum, c->player.bank.num_items, c->player.bank.meseta};
|
0xBC, 0, 0, 0, checksum, c->player.bank.num_items, c->player.bank.meseta};
|
||||||
|
|
||||||
size_t size = 8 + sizeof(cmd) + items.size() * sizeof(PlayerBankItem);
|
size_t size = 8 + sizeof(cmd) + items.size() * sizeof(PlayerBankItem);
|
||||||
@@ -1172,7 +1175,7 @@ void send_bank(shared_ptr<Client> c) {
|
|||||||
|
|
||||||
// sends the player a shop's contents
|
// sends the player a shop's contents
|
||||||
void send_shop(shared_ptr<Client> c, uint8_t shop_type) {
|
void send_shop(shared_ptr<Client> c, uint8_t shop_type) {
|
||||||
S_ShopContents_BB cmd = {
|
G_ShopContents_BB_6xB6 cmd = {
|
||||||
0xB6,
|
0xB6,
|
||||||
0x2C,
|
0x2C,
|
||||||
0x037F,
|
0x037F,
|
||||||
@@ -1208,30 +1211,27 @@ void send_level_up(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make a real struct for this
|
G_LevelUp_6x30 cmd = {
|
||||||
PSOSubcommand sub[5];
|
0x30,
|
||||||
sub[0].byte[0] = 0x30;
|
sizeof(G_LevelUp_6x30) / 4,
|
||||||
sub[0].byte[1] = 0x05;
|
c->lobby_client_id,
|
||||||
sub[0].word[1] = c->lobby_client_id;
|
0,
|
||||||
sub[1].word[0] = stats.atp;
|
stats.atp,
|
||||||
sub[1].word[1] = stats.mst;
|
stats.mst,
|
||||||
sub[2].word[0] = stats.evp;
|
stats.evp,
|
||||||
sub[2].word[1] = stats.hp;
|
stats.hp,
|
||||||
sub[3].word[0] = stats.dfp;
|
stats.dfp,
|
||||||
sub[3].word[1] = stats.ata;
|
stats.ata,
|
||||||
sub[4].dword = c->player.disp.level;
|
c->player.disp.level};
|
||||||
send_command(l, 0x60, 0x00, sub, 0x14);
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// gives a player EXP
|
// gives a player EXP
|
||||||
void send_give_experience(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
void send_give_experience(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||||
uint32_t amount) {
|
uint32_t amount) {
|
||||||
// TODO: Make a real struct for this
|
G_GiveExperience_BB_6xBF cmd = {
|
||||||
PSOSubcommand sub[2];
|
0xBF, sizeof(G_GiveExperience_BB_6xBF) / 4, c->lobby_client_id, 0, amount};
|
||||||
sub[0].word[0] = 0x02BF;
|
send_command(l, 0x60, 0x00, cmd);
|
||||||
sub[0].word[1] = c->lobby_client_id;
|
|
||||||
sub[1].dword = amount;
|
|
||||||
send_command(l, 0x60, 0x00, sub, 8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1368,13 +1368,25 @@ void send_server_time(shared_ptr<Client> c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void send_change_event(shared_ptr<Client> c, uint8_t new_event) {
|
void send_change_event(shared_ptr<Client> c, uint8_t new_event) {
|
||||||
send_command(c, 0xDA, new_event);
|
// THis command isn't supported on versions before GC apparently
|
||||||
|
if ((c->version == GameVersion::GC) || (c->version == GameVersion::BB)) {
|
||||||
|
send_command(c, 0xDA, new_event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_change_event(shared_ptr<Lobby> l, uint8_t new_event) {
|
void send_change_event(shared_ptr<Lobby> l, uint8_t new_event) {
|
||||||
send_command(l, 0xDA, new_event);
|
for (auto& c : l->clients) {
|
||||||
|
if (!c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
send_change_event(c, new_event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_change_event(shared_ptr<ServerState> s, uint8_t new_event) {
|
void send_change_event(shared_ptr<ServerState> s, uint8_t new_event) {
|
||||||
send_command(s, 0xDA, new_event);
|
// TODO: Create a collection of all clients on the server (including those not
|
||||||
|
// in lobbies) and use that here instead
|
||||||
|
for (auto& l : s->all_lobbies()) {
|
||||||
|
send_change_event(l, new_event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,6 +197,15 @@ uint32_t ServerState::connect_address_for_client(std::shared_ptr<Client> c) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shared_ptr<const vector<MenuItem>> ServerState::information_menu_for_version(GameVersion version) {
|
||||||
|
if (version == GameVersion::PC) {
|
||||||
|
return this->information_menu_pc;
|
||||||
|
} else if (version == GameVersion::GC) {
|
||||||
|
return this->information_menu_gc;
|
||||||
|
}
|
||||||
|
throw out_of_range("no information menu exists for this version");
|
||||||
|
}
|
||||||
|
|
||||||
const vector<MenuItem>& ServerState::proxy_destinations_menu_for_version(GameVersion version) {
|
const vector<MenuItem>& ServerState::proxy_destinations_menu_for_version(GameVersion version) {
|
||||||
if (version == GameVersion::PC) {
|
if (version == GameVersion::PC) {
|
||||||
return this->proxy_destinations_menu_pc;
|
return this->proxy_destinations_menu_pc;
|
||||||
|
|||||||
+3
-1
@@ -53,7 +53,8 @@ struct ServerState {
|
|||||||
std::shared_ptr<LicenseManager> license_manager;
|
std::shared_ptr<LicenseManager> license_manager;
|
||||||
|
|
||||||
std::vector<MenuItem> main_menu;
|
std::vector<MenuItem> main_menu;
|
||||||
std::shared_ptr<std::vector<MenuItem>> information_menu;
|
std::shared_ptr<std::vector<MenuItem>> information_menu_pc;
|
||||||
|
std::shared_ptr<std::vector<MenuItem>> information_menu_gc;
|
||||||
std::shared_ptr<std::vector<std::u16string>> information_contents;
|
std::shared_ptr<std::vector<std::u16string>> information_contents;
|
||||||
std::vector<MenuItem> proxy_destinations_menu_pc;
|
std::vector<MenuItem> proxy_destinations_menu_pc;
|
||||||
std::vector<MenuItem> proxy_destinations_menu_gc;
|
std::vector<MenuItem> proxy_destinations_menu_gc;
|
||||||
@@ -98,6 +99,7 @@ struct ServerState {
|
|||||||
|
|
||||||
uint32_t connect_address_for_client(std::shared_ptr<Client> c);
|
uint32_t connect_address_for_client(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
|
std::shared_ptr<const std::vector<MenuItem>> information_menu_for_version(GameVersion version);
|
||||||
const std::vector<MenuItem>& proxy_destinations_menu_for_version(GameVersion version);
|
const std::vector<MenuItem>& proxy_destinations_menu_for_version(GameVersion version);
|
||||||
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations_for_version(GameVersion version);
|
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations_for_version(GameVersion version);
|
||||||
|
|
||||||
|
|||||||
@@ -386,6 +386,7 @@ template <typename CharT, size_t Count>
|
|||||||
void remove_language_marker_inplace(ptext<CharT, Count>& a) {
|
void remove_language_marker_inplace(ptext<CharT, Count>& a) {
|
||||||
if ((a.items[0] == '\t') && (a.items[1] != 'C')) {
|
if ((a.items[0] == '\t') && (a.items[1] != 'C')) {
|
||||||
text_strnzcpy_t(a.items, Count, &a.items[2], Count);
|
text_strnzcpy_t(a.items, Count, &a.items[2], Count);
|
||||||
|
a.items[text_strlen_t(a.items) + 1] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,6 +430,8 @@ size_t add_color_inplace(T* a, size_t max_chars) {
|
|||||||
a++;
|
a++;
|
||||||
}
|
}
|
||||||
*d = 0;
|
*d = 0;
|
||||||
|
// TODO: we should clear the chars after the null if the new string is shorter
|
||||||
|
// than the original
|
||||||
|
|
||||||
return d - orig_d;
|
return d - orig_d;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user