add enemy, object, and event tracking for persistence

This commit is contained in:
Martin Michelsen
2024-02-28 19:42:45 -08:00
parent b81385efdb
commit 34bac4c5b5
85 changed files with 1004 additions and 481 deletions
+125 -23
View File
@@ -2396,30 +2396,44 @@ void send_ep3_change_music(Channel& ch, uint32_t song) {
ch.send(0x60, 0x00, cmd);
}
static void send_game_join_sync_command(
void send_game_join_sync_command(
shared_ptr<Client> c, const void* data, size_t size, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc) {
string compressed_data = bc0_compress(data, size);
send_game_join_sync_command_compressed(c, compressed_data.data(), compressed_data.size(), size, dc_nte_sc, dc_11_2000_sc, sc);
}
void send_game_join_sync_command(shared_ptr<Client> c, const string& data, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc) {
send_game_join_sync_command(c, data.data(), data.size(), dc_nte_sc, dc_11_2000_sc, sc);
}
void send_game_join_sync_command_compressed(
shared_ptr<Client> c,
const void* data,
size_t size,
size_t decompressed_size,
uint8_t dc_nte_sc,
uint8_t dc_11_2000_sc,
uint8_t sc) {
StringWriter w;
if (is_pre_v1(c->version())) {
G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E compressed_header;
compressed_header.header.basic_header.subcommand = (c->version() == Version::DC_NTE) ? dc_nte_sc : dc_11_2000_sc;
compressed_header.header.basic_header.size = 0x00;
compressed_header.header.basic_header.unused = 0x0000;
compressed_header.header.size = (compressed_data.size() + sizeof(G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E) + 3) & (~3);
compressed_header.decompressed_size = size;
compressed_header.header.size = (size + sizeof(G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E) + 3) & (~3);
compressed_header.decompressed_size = decompressed_size;
w.put(compressed_header);
} else {
G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E compressed_header;
compressed_header.header.basic_header.subcommand = sc;
compressed_header.header.basic_header.size = 0x00;
compressed_header.header.basic_header.unused = 0x0000;
compressed_header.header.size = (compressed_data.size() + sizeof(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E) + 3) & (~3);
compressed_header.decompressed_size = size;
compressed_header.compressed_size = compressed_data.size();
compressed_header.header.size = (size + sizeof(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E) + 3) & (~3);
compressed_header.decompressed_size = decompressed_size;
compressed_header.compressed_size = size;
w.put(compressed_header);
}
w.write(compressed_data);
w.write(data, size);
while (w.size() & 3) {
w.put_u8(0x00);
}
@@ -2525,31 +2539,119 @@ void send_game_object_state(shared_ptr<Client> c) {
send_game_join_sync_command(c, entries.data(), entries.size() * sizeof(entries[0]), 0x5D, 0x64, 0x6C);
}
void send_game_flag_state(shared_ptr<Client> c) {
void send_game_set_state(shared_ptr<Client> c) {
auto l = c->require_lobby();
if (!l->map) {
return;
}
size_t num_object_sets = l->map->objects.size();
size_t num_enemy_sets = l->map->enemy_set_flags.size();
G_SyncSetFlagState_6x6E_Decompressed::EntitySetFlags entity_set_flags_header;
entity_set_flags_header.object_set_flags_offset = sizeof(entity_set_flags_header);
entity_set_flags_header.num_object_sets = num_object_sets;
entity_set_flags_header.enemy_set_flags_offset = sizeof(entity_set_flags_header) + num_object_sets * sizeof(le_uint16_t);
entity_set_flags_header.num_enemy_sets = num_enemy_sets;
G_SyncSetFlagState_6x6E_Decompressed header;
header.entity_set_flags_size = sizeof(entity_set_flags_header) + (num_object_sets + num_enemy_sets) * sizeof(le_uint16_t);
header.event_set_flags_size = sizeof(le_uint16_t) * l->map->events.size();
header.unused_size = is_v1(c->version()) ? 0x200 : 0x240;
header.total_size = header.entity_set_flags_size + header.event_set_flags_size + header.unused_size;
StringWriter w;
w.put(header);
w.put(entity_set_flags_header);
for (const auto& obj : l->map->objects) {
w.put_u16l(obj.set_flags);
}
for (uint16_t enemy_set_flags : l->map->enemy_set_flags) {
w.put_u16l(enemy_set_flags);
}
for (const auto& event : l->map->events) {
w.put_u16l(event.flags);
}
w.extend_by(header.unused_size, 0x00);
send_game_join_sync_command(c, w.str(), 0x5F, 0x66, 0x6E);
}
template <typename CmdT>
void send_game_flag_state_t(shared_ptr<Client> c) {
auto l = c->require_lobby();
G_SetQuestFlags_6x6F cmd;
cmd.header.subcommand = 0x6F;
cmd.header.size = sizeof(G_SetQuestFlags_6x6F) >> 2;
cmd.header.unused = 0x0000;
cmd.quest_flags = c->character()->quest_flags;
for (const auto& lc : l->clients) {
if (!lc) {
continue;
if (l->quest_flags_known) { // Not all flags known; send multiple 6x75s
StringWriter w;
bool use_v3_cmd = !is_v1_or_v2(c->version()) || (c->version() == Version::GC_NTE);
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
if ((difficulty != l->difficulty) && !use_v3_cmd) {
continue;
}
const auto& diff_flags = l->quest_flag_values->data.at(difficulty);
const auto& diff_known_flags = l->quest_flags_known->data.at(difficulty);
for (uint8_t z = 0; z < diff_known_flags.data.size(); z++) {
uint8_t known_flags = diff_known_flags.data[z];
if (!known_flags) {
continue;
}
uint8_t flag_values = diff_flags.data[z];
for (uint8_t sh = 0; sh < 8; sh++) {
if ((known_flags << sh) & 0x80) {
uint16_t flag_num = ((z << 3) | sh);
if (use_v3_cmd) {
w.put(G_UpdateQuestFlag_V3_BB_6x75{
{{0x75, 0x03, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)}, difficulty, 0});
} else {
w.put(G_UpdateQuestFlag_DC_PC_6x75{
{0x75, 0x02, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)});
}
}
}
}
}
if (lc->game_join_command_queue) {
lc->log.info("Client not ready to receive join commands; adding to queue");
auto& cmd = lc->game_join_command_queue->emplace_back();
cmd.command = 0x0060;
cmd.flag = 0x00000000;
if (w.size() > 0) {
if (c->game_join_command_queue) {
c->log.info("Client not ready to receive join commands; adding to queue");
auto& cmd = c->game_join_command_queue->emplace_back();
cmd.command = 0x006D;
cmd.flag = c->lobby_client_id;
cmd.data = std::move(w.str());
} else {
send_command(c, 0x6D, c->lobby_client_id, w.str());
}
}
} else { // All flags known; send 6x6F
CmdT cmd;
cmd.header.subcommand = 0x6F;
cmd.header.size = sizeof(CmdT) >> 2;
cmd.header.unused = 0x0000;
cmd.quest_flags = (l && !l->quest_flags_known) ? *l->quest_flag_values : c->character()->quest_flags;
if (c->game_join_command_queue) {
c->log.info("Client not ready to receive join commands; adding to queue");
auto& cmd = c->game_join_command_queue->emplace_back();
cmd.command = 0x0062;
cmd.flag = c->lobby_client_id;
cmd.data.assign(reinterpret_cast<const char*>(&cmd), sizeof(cmd));
} else {
send_command_t(lc, 0x60, 0x00, cmd);
send_command_t(c, 0x62, c->lobby_client_id, cmd);
}
}
}
void send_game_flag_state(shared_ptr<Client> c) {
// DC NTE and 11/2000 don't have this command at all; v1 has it but it doesn't
// include flags for Ultimate.
if (!is_v1(c->version())) {
send_game_flag_state_t<G_SetQuestFlagsV2V3V4_6x6F>(c);
} else if (!is_pre_v1(c->version())) {
send_game_flag_state_t<G_SetQuestFlagsV1_6x6F>(c);
}
}
void send_drop_item_to_channel(shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
bool from_enemy, uint8_t floor, float x, float z, uint16_t entity_id) {
uint8_t subcommand = get_pre_v1_subcommand(ch.version, 0x51, 0x58, 0x5F);