refine many subcommand formats

This commit is contained in:
Martin Michelsen
2025-01-26 09:47:19 -08:00
parent 65a1b97093
commit 78b7bfac70
9 changed files with 528 additions and 321 deletions
+114 -82
View File
@@ -788,11 +788,11 @@ Parsed6x70Data::Parsed6x70Data(
unknown_a6(0),
battle_team_number(0),
telepipe(cmd.telepipe),
unknown_a8(cmd.unknown_a8),
unknown_a9_nte_112000(cmd.unknown_a9),
death_flags(cmd.death_flags),
npc_talk_state(cmd.npc_talk_state),
area(cmd.area),
flags2_value(cmd.flags2),
flags2_is_v3(false),
game_flags(cmd.game_flags),
game_flags_is_v3(false),
visual(cmd.visual),
stats(cmd.stats),
num_items(cmd.num_items),
@@ -824,11 +824,11 @@ Parsed6x70Data::Parsed6x70Data(
unknown_a6(0),
battle_team_number(0),
telepipe(cmd.telepipe),
unknown_a8(cmd.unknown_a8),
unknown_a9_nte_112000(cmd.unknown_a9),
death_flags(cmd.death_flags),
npc_talk_state(cmd.npc_talk_state),
area(cmd.area),
flags2_value(cmd.flags2),
flags2_is_v3(false),
game_flags(cmd.game_flags),
game_flags_is_v3(false),
visual(cmd.visual),
stats(cmd.stats),
num_items(cmd.num_items),
@@ -860,7 +860,7 @@ Parsed6x70Data::Parsed6x70Data(
Version from_version,
bool from_client_customization)
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
this->flags2_is_v3 = true;
this->game_flags_is_v3 = true;
this->stats = cmd.stats;
this->num_items = cmd.num_items;
this->items = cmd.items;
@@ -876,7 +876,7 @@ Parsed6x70Data::Parsed6x70Data(
Version from_version,
bool from_client_customization)
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
this->flags2_is_v3 = true;
this->game_flags_is_v3 = true;
this->stats = cmd.stats;
this->num_items = cmd.num_items;
this->items = cmd.items;
@@ -892,7 +892,7 @@ Parsed6x70Data::Parsed6x70Data(
Version from_version,
bool from_client_customization)
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
this->flags2_is_v3 = true;
this->game_flags_is_v3 = true;
this->stats = cmd.stats;
this->num_items = cmd.num_items;
this->items = cmd.items;
@@ -909,10 +909,10 @@ G_SyncPlayerDispAndInventory_DCNTE_6x70 Parsed6x70Data::as_dc_nte(shared_ptr<Ser
ret.unknown_a5 = this->unknown_a5_nte;
ret.unknown_a6 = this->unknown_a6;
ret.telepipe = this->telepipe;
ret.unknown_a8 = this->unknown_a8;
ret.unknown_a9 = this->unknown_a9_nte_112000;
ret.death_flags = this->death_flags;
ret.npc_talk_state = this->npc_talk_state;
ret.area = this->area;
ret.flags2 = this->get_flags2(false);
ret.game_flags = this->get_game_flags(false);
ret.visual = this->visual;
ret.stats = this->stats;
ret.num_items = this->num_items;
@@ -937,10 +937,10 @@ G_SyncPlayerDispAndInventory_DC112000_6x70 Parsed6x70Data::as_dc_112000(shared_p
ret.bonus_tp_from_materials = this->bonus_tp_from_materials;
ret.unknown_a5 = this->unknown_a5_112000;
ret.telepipe = this->telepipe;
ret.unknown_a8 = this->unknown_a8;
ret.unknown_a9 = this->unknown_a9_nte_112000;
ret.death_flags = this->death_flags;
ret.npc_talk_state = this->npc_talk_state;
ret.area = this->area;
ret.flags2 = this->get_flags2(false);
ret.game_flags = this->get_game_flags(false);
ret.visual = this->visual;
ret.stats = this->stats;
ret.num_items = this->num_items;
@@ -1099,11 +1099,11 @@ Parsed6x70Data::Parsed6x70Data(
unknown_a6(base.unknown_a6),
battle_team_number(base.battle_team_number),
telepipe(base.telepipe),
unknown_a8(base.unknown_a8),
unknown_a9_final(base.unknown_a9),
death_flags(base.death_flags),
npc_talk_state(base.npc_talk_state),
area(base.area),
flags2_value(base.flags2),
flags2_is_v3(!is_v1_or_v2(from_version)),
game_flags(base.game_flags),
game_flags_is_v3(!is_v1_or_v2(from_version)),
technique_levels_v1(base.technique_levels_v1),
visual(base.visual) {}
@@ -1123,42 +1123,19 @@ G_6x70_Base_V1 Parsed6x70Data::base_v1(bool is_v3) const {
ret.unknown_a6 = this->unknown_a6;
ret.battle_team_number = this->battle_team_number;
ret.telepipe = this->telepipe;
ret.unknown_a8 = this->unknown_a8;
ret.unknown_a9 = this->unknown_a9_final;
ret.death_flags = this->death_flags;
ret.npc_talk_state = this->npc_talk_state;
ret.area = this->area;
ret.flags2 = this->get_flags2(is_v3);
ret.game_flags = this->get_game_flags(is_v3);
ret.technique_levels_v1 = this->technique_levels_v1;
ret.visual = this->visual;
return ret;
}
uint32_t Parsed6x70Data::get_flags2(bool is_v3) const {
// The format of flags2 was changed significantly between v2 and v3, and not
// accounting for this means that sometimes other characters won't appear
// when joining a game. Unfortunately, some bits were deleted on v3 and other
// bits were added, so it doesn't suffice to simply store the most complete
// format of this field - we have to be able to convert between the two.
// Bits on v2: ---CBAzy xwvutsrq ponmlkji hgfedcba
// Bits on v3: ---GFEDC BAzyxwvu srqponkj hgfedcba
// The bits ilmt were removed in v3 and the bits to their left were shifted
// right. The bits DEFG were added in v3 and do not exist on v2.
if (is_v3 == this->flags2_is_v3) {
return this->flags2_value;
} else if (!this->flags2_is_v3) { // Convert v2 -> v3
return (
(this->flags2_value & 0x000000FF) |
((this->flags2_value & 0x00000600) >> 1) |
((this->flags2_value & 0x0007E000) >> 3) |
((this->flags2_value & 0x1FF00000) >> 4));
} else { // Convert v3 -> v2
return (
(this->flags2_value & 0x000000FF) |
((this->flags2_value << 1) & 0x00000600) |
((this->flags2_value << 3) & 0x0007E000) |
((this->flags2_value << 4) & 0x1FF00000));
}
uint32_t Parsed6x70Data::get_game_flags(bool is_v3) const {
return (this->game_flags_is_v3 == is_v3)
? this->game_flags
: MapState::EnemyState::convert_game_flags(this->game_flags, is_v3);
}
static void on_sync_joining_player_disp_and_inventory(
@@ -3338,16 +3315,42 @@ static void on_trigger_set_event(shared_ptr<Client> c, uint8_t command, uint8_t
forward_subcommand(c, command, flag, data, size);
}
static void on_update_telepipe_state(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
static inline uint32_t bswap32_high16(uint32_t v) {
return ((v >> 8) & 0x00FF0000) | ((v << 8) & 0xFF000000) | (v & 0x0000FFFF);
}
static void on_update_telepipe_state(shared_ptr<Client> c, uint8_t command, uint8_t, void* data, size_t size) {
if (c->lobby_client_id > 3) {
throw logic_error("client ID is above 3");
}
if (command_is_private(command)) {
return;
}
auto l = c->require_lobby();
if (!l->is_game()) {
return;
}
c->telepipe_state = check_size_t<G_SetTelepipeState_6x68>(data, size);
auto& cmd = check_size_t<G_SetTelepipeState_6x68>(data, size);
c->telepipe_state = cmd.state;
c->telepipe_lobby_id = l->lobby_id;
forward_subcommand(c, command, flag, data, size);
// See the comments in G_SetTelepipeState_6x68 in CommandsFormats.hh for
// why we have to do this
if (is_big_endian(c->version())) {
c->telepipe_state.room_id = bswap32_high16(phosg::bswap32(c->telepipe_state.room_id));
}
for (auto lc : l->clients) {
if (lc && (lc != c)) {
if (is_big_endian(lc->version())) {
cmd.state.room_id = phosg::bswap32(bswap32_high16(c->telepipe_state.room_id));
} else {
cmd.state.room_id = c->telepipe_state.room_id;
}
send_command_t(lc, 0x60, 0x00, cmd);
}
}
}
static void on_update_enemy_state(shared_ptr<Client> c, uint8_t command, uint8_t, void* data, size_t size) {
@@ -3367,36 +3370,64 @@ static void on_update_enemy_state(shared_ptr<Client> c, uint8_t command, uint8_t
if ((cmd.enemy_index & 0xF000) || (cmd.header.entity_id != (cmd.enemy_index | 0x1000))) {
throw runtime_error("mismatched enemy id/index");
}
bool is_v3 = !is_v1_or_v2(c->version());
auto ene_st = l->map_state->enemy_state_for_index(c->version(), c->floor, cmd.enemy_index);
ene_st->game_flags = is_big_endian(c->version()) ? bswap32(cmd.flags) : cmd.flags.load();
uint32_t src_flags = is_big_endian(c->version()) ? bswap32(cmd.game_flags) : cmd.game_flags.load();
if (l->difficulty == 3) {
src_flags = (src_flags & 0xFFFFFFC0) | (ene_st->get_game_flags(is_v3) & 0x0000003F);
}
ene_st->set_game_flags(src_flags, is_v3);
ene_st->total_damage = cmd.total_damage;
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
l->log.info("E-%03zX updated to damage=%hu game_flags=%08" PRIX32,
ene_st->e_id, ene_st->total_damage, ene_st->game_flags);
l->log.info("E-%03zX updated to damage=%hu game_flags=%08" PRIX32 " (%s)",
ene_st->e_id,
ene_st->total_damage,
ene_st->game_flags,
(ene_st->server_flags & MapState::EnemyState::Flag::GAME_FLAGS_IS_V3) ? "v3" : "v2");
G_UpdateEnemyState_GC_6x0A sw_cmd = {
{cmd.header.subcommand, cmd.header.size, cmd.header.entity_id},
cmd.enemy_index, cmd.total_damage, cmd.flags.load()};
bool sender_is_be = is_big_endian(c->version());
for (auto lc : l->clients) {
if (lc && (lc != c)) {
if (is_big_endian(lc->version()) == sender_is_be) {
cmd.enemy_index = l->map_state->index_for_enemy_state(lc->version(), ene_st);
if (cmd.enemy_index != 0xFFFF) {
cmd.header.entity_id = 0x1000 | cmd.enemy_index;
send_command_t(lc, 0x60, 0x00, cmd);
}
} else {
sw_cmd.enemy_index = l->map_state->index_for_enemy_state(lc->version(), ene_st);
if (sw_cmd.enemy_index != 0xFFFF) {
sw_cmd.header.entity_id = 0x1000 | sw_cmd.enemy_index;
send_command_t(lc, 0x60, 0x00, sw_cmd);
}
cmd.enemy_index = l->map_state->index_for_enemy_state(lc->version(), ene_st);
if (cmd.enemy_index != 0xFFFF) {
cmd.header.entity_id = 0x1000 | cmd.enemy_index;
uint32_t game_flags = ene_st->get_game_flags(!is_v1_or_v2(lc->version()));
cmd.game_flags = is_big_endian(lc->version()) ? phosg::bswap32(game_flags) : game_flags;
send_command_t(lc, 0x60, 0x00, cmd);
}
}
}
}
static void on_set_enemy_low_game_flags_ultimate(
shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
auto& cmd = check_size_t<G_SetEnemyLowGameFlagsUltimate_6x9C>(data, size);
if (command_is_private(command) ||
(cmd.header.entity_id < 0x1000) ||
(cmd.header.entity_id >= 0x4000) ||
(cmd.low_game_flags & 0xFFFFFFC0) ||
(c->lobby_client_id > 3)) {
return;
}
auto l = c->require_lobby();
if (!l->is_game() || (l->difficulty != 3)) {
return;
}
bool is_v3 = !is_v1_or_v2(c->version());
auto ene_st = l->map_state->enemy_state_for_index(c->version(), c->floor, cmd.header.entity_id - 0x1000);
uint32_t game_flags = ene_st->get_game_flags(is_v3);
if (!(game_flags & cmd.low_game_flags)) {
ene_st->set_game_flags(game_flags | cmd.low_game_flags, is_v3);
l->log.info("E-%03zX updated to game_flags=%08" PRIX32 " (%s)",
ene_st->e_id,
ene_st->game_flags,
(ene_st->server_flags & MapState::EnemyState::Flag::GAME_FLAGS_IS_V3) ? "v3" : "v2");
}
forward_subcommand_with_entity_id_transcode_t<G_SetEnemyLowGameFlagsUltimate_6x9C>(c, command, flag, data, size);
}
template <typename CmdT>
static void on_update_object_state_t(shared_ptr<Client> c, uint8_t command, uint8_t, void* data, size_t size) {
auto& cmd = check_size_t<CmdT>(data, size);
@@ -3501,7 +3532,7 @@ static void on_battle_scores(shared_ptr<Client> c, uint8_t command, uint8_t, voi
}
}
static void on_dragon_actions(shared_ptr<Client> c, uint8_t command, uint8_t, void* data, size_t size) {
static void on_dragon_actions_6x12(shared_ptr<Client> c, uint8_t command, uint8_t, void* data, size_t size) {
auto& cmd = check_size_t<G_DragonBossActions_DC_PC_XB_BB_6x12>(data, size);
if (command_is_private(command)) {
@@ -5045,6 +5076,7 @@ static void on_write_quest_counter_bb(shared_ptr<Client> c, uint8_t, uint8_t, vo
constexpr uint8_t NONE = 0x00;
const SubcommandDefinition subcommand_definitions[0x100] = {
// {DC NTE, 11/2000, all other versions, handler}
/* 6x00 */ {0x00, 0x00, 0x00, on_invalid},
/* 6x01 */ {0x01, 0x01, 0x01, on_invalid},
/* 6x02 */ {0x02, 0x02, 0x02, forward_subcommand_m},
@@ -5054,18 +5086,18 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
/* 6x06 */ {0x06, 0x06, 0x06, on_send_guild_card},
/* 6x07 */ {0x07, 0x07, 0x07, on_symbol_chat, SDF::ALWAYS_FORWARD_TO_WATCHERS},
/* 6x08 */ {0x08, 0x08, 0x08, on_invalid},
/* 6x09 */ {0x09, 0x09, 0x09, forward_subcommand_with_entity_id_transcode_t<G_Unknown_6x09>},
/* 6x09 */ {0x09, 0x09, 0x09, on_invalid}, // See notes in CommandFormats.hh
/* 6x0A */ {0x0A, 0x0A, 0x0A, on_update_enemy_state},
/* 6x0B */ {0x0B, 0x0B, 0x0B, on_update_object_state_t<G_UpdateObjectState_6x0B>},
/* 6x0C */ {0x0C, 0x0C, 0x0C, on_received_condition},
/* 6x0D */ {NONE, NONE, 0x0D, on_forward_check_game},
/* 6x0E */ {NONE, NONE, 0x0E, on_forward_check_game},
/* 6x0E */ {NONE, NONE, 0x0E, forward_subcommand_with_entity_id_transcode_t<G_ClearNegativeStatusEffects_6x0E>},
/* 6x0F */ {NONE, NONE, 0x0F, on_invalid},
/* 6x10 */ {0x0E, 0x0E, 0x10, forward_subcommand_with_entity_id_transcode_t<G_Unknown_6x10_6x11_6x14>},
/* 6x11 */ {0x0F, 0x0F, 0x11, forward_subcommand_with_entity_id_transcode_t<G_Unknown_6x10_6x11_6x14>},
/* 6x12 */ {0x10, 0x10, 0x12, on_dragon_actions},
/* 6x10 */ {0x0E, 0x0E, 0x10, forward_subcommand_with_entity_id_transcode_t<G_DragonBossActions_6x10_6x11>},
/* 6x11 */ {0x0F, 0x0F, 0x11, forward_subcommand_with_entity_id_transcode_t<G_DragonBossActions_6x10_6x11>},
/* 6x12 */ {0x10, 0x10, 0x12, on_dragon_actions_6x12},
/* 6x13 */ {0x11, 0x11, 0x13, forward_subcommand_with_entity_id_transcode_t<G_DeRolLeBossActions_6x13>},
/* 6x14 */ {0x12, 0x12, 0x14, forward_subcommand_with_entity_id_transcode_t<G_Unknown_6x10_6x11_6x14>},
/* 6x14 */ {0x12, 0x12, 0x14, forward_subcommand_with_entity_id_transcode_t<G_DeRolLeBossActions_6x14>},
/* 6x15 */ {0x13, 0x13, 0x15, forward_subcommand_with_entity_id_transcode_t<G_VolOptBossActions_6x15>},
/* 6x16 */ {0x14, 0x14, 0x16, forward_subcommand_with_entity_id_transcode_t<G_VolOptBossActions_6x16>},
/* 6x17 */ {0x15, 0x15, 0x17, forward_subcommand_with_entity_id_transcode_t<G_VolOpt2BossActions_6x17>},
@@ -5179,7 +5211,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
/* 6x83 */ {NONE, NONE, 0x83, on_forward_check_game},
/* 6x84 */ {NONE, NONE, 0x84, on_forward_check_game},
/* 6x85 */ {NONE, NONE, 0x85, on_forward_check_game},
/* 6x86 */ {NONE, NONE, 0x86, forward_subcommand_with_entity_id_transcode_t<G_HitDestructibleObject_6x86>},
/* 6x86 */ {NONE, NONE, 0x86, on_update_object_state_t<G_HitDestructibleObject_6x86>},
/* 6x87 */ {NONE, NONE, 0x87, on_forward_check_game},
/* 6x88 */ {NONE, NONE, 0x88, on_forward_check_game},
/* 6x89 */ {NONE, NONE, 0x89, forward_subcommand_with_entity_id_transcode_t<G_SetKillerEntityID_6x89, false, offsetof(G_SetKillerEntityID_6x89, killer_entity_id)>},
@@ -5201,7 +5233,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
/* 6x99 */ {NONE, NONE, 0x99, on_forward_check_game},
/* 6x9A */ {NONE, NONE, 0x9A, on_forward_check_game_client},
/* 6x9B */ {NONE, NONE, 0x9B, on_forward_check_game},
/* 6x9C */ {NONE, NONE, 0x9C, forward_subcommand_with_entity_id_transcode_t<G_Unknown_6x9C>},
/* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_low_game_flags_ultimate},
/* 6x9D */ {NONE, NONE, 0x9D, on_forward_check_game},
/* 6x9E */ {NONE, NONE, 0x9E, forward_subcommand_m},
/* 6x9F */ {NONE, NONE, 0x9F, forward_subcommand_with_entity_id_transcode_t<G_GalGryphonBossActions_6x9F>},