#include "aoapplication.h" #include "courtroom.h" #include "debug_functions.h" #include "hardware_functions.h" #include "lobby.h" #include "networkmanager.h" #include "options.h" void AOApplication::append_to_demofile(QString packet_string) { if (Options::getInstance().logToDemoFileEnabled() && !log_filename.isEmpty()) { QString path = log_filename.left(log_filename.size()).replace(".log", ".demo"); if (!demo_timer.isValid()) { demo_timer.start(); } else { append_to_file("wait#" + QString::number(demo_timer.restart()) + "#%", path, true); } append_to_file(packet_string, path, true); } } void AOApplication::server_packet_received(AOPacket packet) { QString header = packet.header(); QStringList content = packet.content(); bool log_to_demo = true; #ifdef DEBUG_NETWORK if (header != "checkconnection") { qDebug() << "R:" << f_packet; } #endif if (header == "decryptor") { if (content.size() == 0) { return; } // default(legacy) values m_serverdata.set_features(QStringList()); QString f_hdid; f_hdid = get_hdid(); QStringList f_contents = {f_hdid}; AOPacket hi_packet("HI", f_contents); send_server_packet(hi_packet); log_to_demo = false; } else if (header == "ID") { if (content.size() < 2) { return; } client_id = content.at(0).toInt(); m_serverdata.set_server_software(content.at(1)); net_manager->server_connected(true); QStringList f_contents = {"AO2", get_version_string()}; send_server_packet(AOPacket("ID", f_contents)); } else if (header == "CT") { if (!is_courtroom_constructed() || content.size() < 2) { return; } if (content.size() == 3) { w_courtroom->append_server_chatmessage(content.at(0), content.at(1), content.at(2)); } else { w_courtroom->append_server_chatmessage(content.at(0), content.at(1), "0"); } } else if (header == "FL") { m_serverdata.set_features(content); w_courtroom->set_widgets(); log_to_demo = false; } else if (header == "PN") { if (!is_lobby_constructed() || content.size() < 2) { return; } w_lobby->set_player_count(content.at(0).toInt(), content.at(1).toInt()); if (content.size() >= 3) { w_lobby->set_server_description(content.at(2)); } log_to_demo = false; } else if (header == "SI") { if (!is_lobby_constructed() || content.size() != 3) { return; } generated_chars = 0; int selected_server = w_lobby->get_selected_server(); QString server_address; QString server_name; switch (w_lobby->pageSelected()) { case 0: if (selected_server >= 0 && selected_server < server_list.size()) { auto info = server_list.at(selected_server); server_name = info.name; server_address = QString("%1:%2").arg(info.address, QString::number(info.port)); window_title = server_name; } break; case 1: { QVector favorite_list = Options::getInstance().favorites(); if (selected_server >= 0 && selected_server < favorite_list.size()) { auto info = favorite_list.at(selected_server); server_name = info.name; server_address = QString("%1:%2").arg(info.address, QString::number(info.port)); window_title = server_name; } } break; case 2: window_title = "Local Demo Recording"; break; default: break; } if (is_courtroom_constructed()) { w_courtroom->set_window_title(window_title); } send_server_packet(AOPacket("RC")); // Remove any characters not accepted in folder names for the server_name // here if (Options::getInstance().logToDemoFileEnabled() && server_name != "Demo playback") { this->log_filename = QDateTime::currentDateTime().toUTC().toString("'logs/" + server_name.remove(QRegularExpression("[\\\\/:*?\"<>|\']")) + "/'yyyy-MM-dd hh-mm-ss t'.log'"); this->write_to_file("Joined server " + server_name + " hosted on address " + server_address + " on " + QDateTime::currentDateTime().toUTC().toString(), log_filename, true); } else { this->log_filename = ""; } QCryptographicHash hash(QCryptographicHash::Algorithm::Sha256); hash.addData(server_address.toUtf8()); if (Options::getInstance().discordEnabled()) { discord->state_server(server_name.toStdString(), hash.result().toBase64().toStdString()); } log_to_demo = false; } else if (header == "CharsCheck") { if (!is_courtroom_constructed()) { return; } for (int n_char = 0; n_char < content.size(); ++n_char) { if (content.at(n_char) == "-1") { w_courtroom->set_taken(n_char, true); } else { w_courtroom->set_taken(n_char, false); } } log_to_demo = false; } else if (header == "SC") { if (!is_courtroom_constructed()) { return; } w_courtroom->clear_chars(); for (int n_element = 0; n_element < content.size(); ++n_element) { QStringList sub_elements = content.at(n_element).split("&"); for (QString &sub_element : sub_elements) { sub_element = AOPacket::decode(sub_element); } CharacterSlot f_char; f_char.name = sub_elements.at(0); if (sub_elements.size() >= 2) { f_char.description = sub_elements.at(1); } // temporary. the CharsCheck packet sets this properly f_char.taken = false; w_courtroom->append_char(f_char); } if (!courtroom_loaded) { send_server_packet(AOPacket("RM")); } else { w_courtroom->character_loading_finished(); } } else if (header == "SM") { if (!is_courtroom_constructed() || courtroom_loaded) { return; } bool musics_time = false; int areas = 0; for (int n_element = 0; n_element < content.size(); ++n_element) { if (musics_time) { w_courtroom->append_music(content.at(n_element)); } else { if (content.at(n_element).endsWith(".wav") || content.at(n_element).endsWith(".mp3") || content.at(n_element).endsWith(".mp4") || content.at(n_element).endsWith(".ogg") || content.at(n_element).endsWith(".opus")) { musics_time = true; w_courtroom->fix_last_area(); w_courtroom->append_music(content.at(n_element)); areas--; } else { w_courtroom->append_area(content.at(n_element)); areas++; } } } for (int area_n = 0; area_n < areas; area_n++) { w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown"); } send_server_packet(AOPacket("RD")); log_to_demo = false; } else if (header == "FM") // Fetch music ONLY { if (!is_courtroom_constructed()) { return; } w_courtroom->clear_music(); for (int n_element = 0; n_element < content.size(); ++n_element) { w_courtroom->append_music(content.at(n_element)); } w_courtroom->list_music(); log_to_demo = false; } else if (header == "FA") // Fetch areas ONLY { if (!is_courtroom_constructed()) { return; } w_courtroom->clear_areas(); w_courtroom->arup_clear(); for (int n_element = 0; n_element < content.size(); ++n_element) { w_courtroom->append_area(content.at(n_element)); w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown"); } w_courtroom->list_areas(); log_to_demo = false; } else if (header == "DONE") { if (!is_courtroom_constructed()) { return; } w_courtroom->character_loading_finished(); w_courtroom->done_received(); courtroom_loaded = true; destruct_lobby(); log_to_demo = false; } else if (header == "BN") { if (!is_courtroom_constructed() || content.isEmpty()) { return; } if (content.size() >= 2) { // We have a pos included in the background packet! if (!content.at(1).isEmpty()) { // Not touching it when its empty. w_courtroom->set_side(content.at(1)); } } w_courtroom->set_background(content.at(0), content.size() >= 2); } else if (header == "SP") { if (!is_courtroom_constructed() || content.isEmpty()) { return; } // We were sent a "set position" packet w_courtroom->set_side(content.at(0)); } else if (header == "SD") // Send pos dropdown { if (!is_courtroom_constructed() || content.isEmpty()) { return; } w_courtroom->set_pos_dropdown(content.at(0).split("*")); } // server accepting char request(CC) packet else if (header == "PV") { if (!is_courtroom_constructed() || content.size() < 3) { return; } // For some reason, args 0 and 1 are not used (from tsu3 they're client ID and a string "CID") w_courtroom->enter_courtroom(); w_courtroom->set_courtroom_size(); w_courtroom->update_character(content.at(2).toInt()); } else if (header == "MS") { if (is_courtroom_constructed() && courtroom_loaded) { w_courtroom->chatmessage_enqueue(packet.content()); } } else if (header == "MC") { if (is_courtroom_constructed() && courtroom_loaded) { w_courtroom->handle_song(&packet.content()); } } else if (header == "RT") { if (content.isEmpty()) { return; } if (is_courtroom_constructed()) { if (content.size() == 1) { w_courtroom->handle_wtce(content.at(0), 0); } else if (content.size() >= 2) { w_courtroom->handle_wtce(content.at(0), content.at(1).toInt()); } } } else if (header == "HP") { if (is_courtroom_constructed() && content.size() >= 2) { w_courtroom->set_hp_bar(content.at(0).toInt(), content.at(1).toInt()); } } else if (header == "LE") { if (is_courtroom_constructed()) { QVector f_evi_list; for (QString f_string : packet.content()) { QStringList sub_contents = f_string.split("&"); if (sub_contents.size() < 3) { continue; } // decoding has to be done here instead of on reception // because this packet uses & as a delimiter for some reason for (QString &data : sub_contents) { data = AOPacket::decode(data); } EvidenceItem f_evi; f_evi.name = sub_contents.at(0); f_evi.description = sub_contents.at(1); f_evi.image = sub_contents.at(2); f_evi_list.append(f_evi); } w_courtroom->set_evidence_list(f_evi_list); } } else if (header == "ARUP") { if (is_courtroom_constructed() && !content.isEmpty()) { int arup_type = content.at(0).toInt(); for (int n_element = 1; n_element < content.size(); n_element++) { w_courtroom->arup_modify(arup_type, n_element - 1, content.at(n_element)); } w_courtroom->list_areas(); } log_to_demo = false; } else if (header == "IL") { if (is_courtroom_constructed() && !content.isEmpty()) { w_courtroom->set_ip_list(content.at(0)); } log_to_demo = false; } else if (header == "MU") { if (is_courtroom_constructed() && !content.isEmpty()) { w_courtroom->set_mute(true, content.at(0).toInt()); } log_to_demo = false; } else if (header == "UM") { if (is_courtroom_constructed() && !content.isEmpty()) { w_courtroom->set_mute(false, content.at(0).toInt()); log_to_demo = false; } } else if (header == "BB") { if (is_courtroom_constructed() && !content.isEmpty()) { call_notice(content.at(0)); } log_to_demo = false; } else if (header == "KK") { if (is_courtroom_constructed() && !content.isEmpty()) { call_notice(tr("You have been kicked from the server.\nReason: %1").arg(content.at(0))); construct_lobby(); destruct_courtroom(); } log_to_demo = false; } else if (header == "KB") { if (is_courtroom_constructed() && !content.isEmpty()) { call_notice(tr("You have been banned from the server.\nReason: %1").arg(content.at(0))); construct_lobby(); destruct_courtroom(); } log_to_demo = false; } else if (header == "BD") { if (content.isEmpty()) { return; } call_notice(tr("You are banned on this server.\nReason: %1").arg(content.at(0))); log_to_demo = false; } else if (header == "ZZ") { if (content.size() < 1) { return; } if (is_courtroom_constructed() && !content.isEmpty()) { w_courtroom->mod_called(content.at(0)); } } else if (header == "TI") { // Timer packet if (!is_courtroom_constructed() || content.size() < 2) { return; } // Timer ID is reserved as argument 0 int id = content.at(0).toInt(); // Type 0 = start/resume/sync timer at time // Type 1 = pause timer at time // Type 2 = show timer // Type 3 = hide timer int type = content.at(1).toInt(); if (type == 0 || type == 1) { if (content.size() < 3) { return; } // The time as displayed on the clock, in milliseconds. // If the number received is negative, stop the timer. qint64 timer_value = content.at(2).toLongLong(); if (timer_value > 0) { if (type == 0) { timer_value -= latency / 2; w_courtroom->start_clock(id, timer_value); } else { w_courtroom->pause_clock(id); w_courtroom->set_clock(id, timer_value); } } else { w_courtroom->stop_clock(id); } } else if (type == 2) { w_courtroom->set_clock_visibility(id, true); } else if (type == 3) { w_courtroom->set_clock_visibility(id, false); } } else if (header == "CHECK") { if (!is_courtroom_constructed()) { return; } qint64 ping_time = w_courtroom->pong(); qDebug() << "ping:" << ping_time; if (ping_time != -1) { latency = ping_time; } log_to_demo = false; } // Subtheme packet else if (header == "ST") { if (!is_courtroom_constructed() || content.isEmpty()) { return; } // Subtheme reserved as argument 0 subtheme = content.at(0); // Check if we have subthemes set to "server" if (Options::getInstance().settingsSubTheme().toLower() != "server") { // We don't. Simply acknowledge the subtheme sent by the server, but don't do anything else. return; } // Reload theme request if (content.size() > 1 && content.at(1) == "1") { Options::getInstance().setServerSubTheme(subtheme); w_courtroom->on_reload_theme_clicked(); } } // Auth packet else if (header == "AUTH") { if (!is_courtroom_constructed() || !m_serverdata.get_feature(server::BASE_FEATURE_SET::AUTH_PACKET) || content.isEmpty()) { return; } bool ok; int authenticated = content.at(0).toInt(&ok); if (!ok) { qWarning() << "Malformed AUTH packet! Contents:" << content.at(0); } w_courtroom->on_authentication_state_received(authenticated); log_to_demo = false; } else if (header == "JD") { if (!is_courtroom_constructed() || content.isEmpty()) { return; } bool ok; Courtroom::JudgeState state = static_cast(content.at(0).toInt(&ok)); if (!ok) { return; // ignore malformed packet } w_courtroom->set_judge_state(state); if (w_courtroom->get_judge_state() != Courtroom::POS_DEPENDENT) { // If we receive JD -1, it means the server asks us to fall back to client-side judge buttons behavior w_courtroom->show_judge_controls(w_courtroom->get_judge_state() == Courtroom::SHOW_CONTROLS); } else { w_courtroom->set_judge_buttons(); // client-side judge behavior } } // AssetURL Packet else if (header == "ASS") { if (content.size() > 1 || content.isEmpty()) { // This can never be more than one link. return; } m_serverdata.set_asset_url(content.at(0)); } else if (header == "PR") { if (content.size() < 2 || !is_courtroom_constructed()) { return; } PlayerRegister update{content.at(0).toInt(), PlayerRegister::REGISTER_TYPE(content.at(1).toInt())}; w_courtroom->playerList()->registerPlayer(update); } else if (header == "PU") { if (content.size() < 3 || !is_courtroom_constructed()) { return; } PlayerUpdate update{content.at(0).toInt(), PlayerUpdate::DATA_TYPE(content.at(1).toInt()), content.at(2)}; w_courtroom->playerList()->updatePlayer(update); } if (log_to_demo) { append_to_demofile(packet.toString(true)); } } void AOApplication::send_server_packet(AOPacket p_packet) { QString f_packet = p_packet.toString(); #ifdef DEBUG_NETWORK qDebug() << "S:" << p_packet.to_string(); #endif net_manager->ship_server_packet(p_packet); }