diff --git a/include/area_data.h b/include/area_data.h index e20ddcd..63f7ce2 100644 --- a/include/area_data.h +++ b/include/area_data.h @@ -20,14 +20,22 @@ #include #include +#include +#include class AreaData { public: - AreaData(QStringList characters); + AreaData(QStringList p_characters, QString p_name, int p_index); QString name; + int index; QMap characters_taken; int player_count; + + QString background; + bool showname_allowed; + bool locking_allowed; + bool iniswap_allowed; }; #endif // AREA_DATA_H diff --git a/include/server.h b/include/server.h index 6c2e904..80b6364 100644 --- a/include/server.h +++ b/include/server.h @@ -42,11 +42,13 @@ class Server : public QObject { void start(); AOClient* getClient(QString ipid); void updateCharsTaken(AreaData* area); - void broadcast(AOPacket packet); + void broadcast(AOPacket packet, int area_index); int player_count; QStringList characters; QVector areas; + QStringList area_names; + QStringList music_list; signals: diff --git a/src/aoclient.cpp b/src/aoclient.cpp index 130aebd..14ea605 100644 --- a/src/aoclient.cpp +++ b/src/aoclient.cpp @@ -32,7 +32,7 @@ AOClient::AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent) void AOClient::clientData() { QString data = QString::fromUtf8(socket->readAll()); - // qDebug() << "From" << client->peerAddress() << ":" << data; + qDebug() << "From" << remote_ip << ":" << data; if (is_partial) { data = partial_packet + data; @@ -89,7 +89,7 @@ void AOClient::handlePacket(AOPacket packet) "noencryption", "yellowtext", "prezoom", "flipping", "customobjections", "fastloading", "deskmod", "evidence", "cccc_ic_support", - "arup", "casing_alserts", "modcall_reason", + "arup", "casing_alerts", "modcall_reason", "looping_sfx", "additive", "effects"}; AOPacket response_pn( "PN", {QString::number(server->player_count), max_players}); @@ -101,7 +101,7 @@ void AOClient::handlePacket(AOPacket packet) // TODO: add user configurable content // For testing purposes, we will just send enough to get things working AOPacket response( - "SI", {QString::number(server->characters.length()), "0", "1"}); + "SI", {QString::number(server->characters.length()), "0", QString::number(server->area_names.length() + server->music_list.length())}); sendPacket(response); } else if (packet.header == "RC") { @@ -109,16 +109,21 @@ void AOClient::handlePacket(AOPacket packet) sendPacket(response); } else if (packet.header == "RM") { - AOPacket response("SM", {"~stop.mp3"}); + AOPacket response("SM", server->area_names + server->music_list); sendPacket(response); } else if (packet.header == "RD") { server->player_count++; joined = true; - server->updateCharsTaken(area); + + QSettings areas_ini("areas.ini", QSettings::IniFormat); + QStringList areas = areas_ini.childGroups(); + + AOPacket response_fa("FA", areas); AOPacket response_op("OPPASS", {"DEADBEEF"}); AOPacket response_done("DONE", {}); + sendPacket(response_fa); sendPacket(response_op); sendPacket(response_done); } @@ -157,15 +162,17 @@ void AOClient::handlePacket(AOPacket packet) } else if (packet.header == "MS") { // TODO: validate, validate, validate - server->broadcast(packet); + server->broadcast(packet, current_area); } else if (packet.header == "CT") { // TODO: commands // TODO: zalgo strip - server->broadcast(packet); + server->broadcast(packet, current_area); } else if (packet.header == "CH") { // Why does this packet exist + // At least Crystal made it useful + // It is now used for ping measurement AOPacket response("CHECK", {}); sendPacket(response); } @@ -174,16 +181,53 @@ void AOClient::handlePacket(AOPacket packet) "CT", {"Made with love", "by scatterflower and windrammer"}); sendPacket(response); } + else if (packet.header == "MC") { + // Due to historical reasons, this + // packet has two functions: + // Change area, and set music. + + // First, we check if the provided + // argument is a valid song + QString argument = packet.contents[0]; + + bool is_song = false; + for (QString song : server->music_list) { + if (song == argument) { + is_song = true; + break; + } + } + + if (is_song) { + // If we have a song, retransmit as-is + server->broadcast(packet, current_area); + return; + } + + bool is_area = false; + for (QString area : server->area_names) { + if(area == argument) { + is_area = true; + break; + } + } + + if (is_area) { + // TODO: change area function that resends all area data and sets user stuff + // For now, we pretend + AOPacket user_message("CT", {"Server", "Changed to area " + argument}); + sendPacket(user_message); + } + } else { qDebug() << "Unimplemented packet:" << packet.header; qDebug() << packet.contents; } - socket->flush(); } void AOClient::sendPacket(AOPacket packet) { - //qDebug() << "Sent packet:" << packet.header << ":" << packet.contents; + qDebug() << "Sent packet:" << packet.header << ":" << packet.contents; socket->write(packet.toUtf8()); socket->flush(); } @@ -192,6 +236,11 @@ QString AOClient::getHwid() { return hwid; } void AOClient::setHwid(QString p_hwid) { + // TODO: add support for longer hwids? + // This reduces the (fairly high) chance of + // birthday paradox issues arising. However, + // typing more than 8 characters might be a + // bit cumbersome. hwid = p_hwid; QCryptographicHash hash( diff --git a/src/area_data.cpp b/src/area_data.cpp index c85c6a6..adf4a8a 100644 --- a/src/area_data.cpp +++ b/src/area_data.cpp @@ -17,9 +17,15 @@ ////////////////////////////////////////////////////////////////////////////////////// #include "include/area_data.h" -AreaData::AreaData(QStringList characters) +AreaData::AreaData(QStringList characters, QString p_name, int p_index) { + name = p_name; + index = p_index; for (QString cur_char : characters) { characters_taken.insert(cur_char, false); } + QSettings areas_ini("areas.ini", QSettings::IniFormat); + areas_ini.beginGroup(p_name); + background = areas_ini.value("background", "gs4").toString(); + areas_ini.endGroup(); } diff --git a/src/config_manager.cpp b/src/config_manager.cpp index 555c3db..1d2f06f 100644 --- a/src/config_manager.cpp +++ b/src/config_manager.cpp @@ -91,6 +91,69 @@ bool ConfigManager::initConfig() char_list.close(); } + QFileInfo music_list_info("music.txt"); + if (!(music_list_info.exists() && music_list_info.isFile())) { + QFile music_list("music.txt"); + if (!music_list.open(QIODevice::WriteOnly | QIODevice::Text)) + qDebug() << "Couldn't create music list"; + QTextStream file_stream(&music_list); + + qDebug() << "Creating vanilla music list"; + + file_stream << "Announce The Truth (AA).opus\n"; + file_stream << "Announce The Truth (AJ).opus\n"; + file_stream << "Announce The Truth (JFA).opus\n"; + file_stream << "Announce The Truth (Miles).opus\n"; + file_stream << "Announce The Truth (T&T).opus\n"; + file_stream << "Confrontation ~ Presto 2009.opus\n"; + file_stream << "Crises of Fate.opus\n"; + file_stream << "Forgotten Legend.opus\n"; + file_stream << "Godot - The Fragrance of Dark Coffee.opus\n"; + file_stream << "Great Revival ~ Franziska von Karma.opus\n"; + file_stream << "Great Revival ~ Miles Edgeworth.opus\n"; + file_stream << "Hotline of Fate.opus\n"; + file_stream << "Interesting People.opus\n"; + file_stream << "Logic and Trick.opus\n"; + file_stream << "Luke Atmey ~ I Just Want Love.opus\n"; + file_stream << "Noisy People.opus\n"; + file_stream << "OBJECTION (AA).opus\n"; + file_stream << "Objection (AJ).opus\n"; + file_stream << "OBJECTION (JFA).opus\n"; + file_stream << "Objection (Miles).opus\n"; + file_stream << "OBJECTION (T&T).opus\n"; + file_stream << "Others ~ Guilty love.opus\n"; + file_stream << "Prelude (AA).opus\n"; + file_stream << "Prelude (AJ).opus\n"; + file_stream << "Prologue (AA).opus\n"; + file_stream << "Pursuit (AA) - variation.opus\n"; + file_stream << "Pursuit (AA).opus\n"; + file_stream << "Pursuit (AJ).opus\n"; + file_stream << "Pursuit (DS).opus\n"; + file_stream << "Pursuit (JFA) - variation.opus\n"; + file_stream << "Pursuit (JFA).opus\n"; + file_stream << "Pursuit (Miles).opus\n"; + file_stream << "Pursuit (T&T) - variation.opus\n"; + file_stream << "Pursuit (T&T).opus\n"; + file_stream << "Pursuit ~ I Want to Find the Truth (Orchestra).opus\n"; + file_stream << "Questioning AA (Allegro).opus\n"; + file_stream << "Questioning AA (Moderato).opus\n"; + file_stream << "Questioning AJ (Allegro).opus\n"; + file_stream << "Questioning AJ (Moderato).opus\n"; + file_stream << "Questioning JFA (Allegro).opus\n"; + file_stream << "Questioning JFA (Moderato).opus\n"; + file_stream << "Questioning T&T (Allegro).opus\n"; + file_stream << "Questioning T&T (Moderato).opus\n"; + file_stream << "Speak up Pup.opus\n"; + file_stream << "Suspense (AA).opus\n"; + file_stream << "The Great Truth Burglar.opus\n"; + file_stream << "Trial (AA).opus\n"; + file_stream << "Trial (AJ).opus\n"; + file_stream << "Trial (Miles).opus\n"; + + music_list.flush(); + music_list.close(); + } + config->beginGroup("Info"); QString config_version = config->value("version", "none").toString(); config->endGroup(); diff --git a/src/server.cpp b/src/server.cpp index 95e15cd..9367d41 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -30,19 +30,6 @@ Server::Server(int p_port, int p_ws_port, QObject* parent) : QObject(parent) void Server::start() { - // TODO: websockets lul - // Maybe websockets should be handled by a separate intermediate part of the - // code? The idea being that it is a websocket server, and all it does is - // create a local connection to the raw tcp server. The main issue with this - // is that it will cause problems with bans, ipids, etc But perhaps this can - // be negotiated by sending some extra data over? No idea. I'll wait for - // long to read this massive comment and DM me on discord - // - // Upon thinking about this a bit more, I realized basically all of the - // communication only happens via QTcpSocket* pointers. - // If the Qt WebSocket server gives me QTcpSockets to work with, - // then they can all go into the same object. I doubt this is the case, - // though if (!server->listen(QHostAddress::Any, port)) { // TODO: signal server start failed qDebug() << "Server error:" << server->errorString(); @@ -61,10 +48,25 @@ void Server::start() while (!char_list.atEnd()) { characters.append(char_list.readLine().trimmed()); } + char_list.close(); - // TODO: actually read areas from config - areas.append(new AreaData(characters)); - areas[0]->name = "basement lol"; + QFile music_file("music.txt"); + music_file.open(QIODevice::ReadOnly | QIODevice::Text); + while (!music_file.atEnd()) { + music_list.append(music_file.readLine().trimmed()); + } + music_file.close(); + if(music_list[0].contains(".")) // Add a default category if none exists + music_list.insert(0, "Music"); + + // TODO: add verification that this exists + QSettings areas_ini("areas.ini", QSettings::IniFormat); + area_names = areas_ini.childGroups(); + for (int i = 0; i < area_names.length(); i++) { + QString area_name = area_names[i]; + areas.insert(i, new AreaData(characters, area_name, i)); + qDebug() << "Added area" << area_name; + } } void Server::clientConnected() @@ -100,14 +102,15 @@ void Server::updateCharsTaken(AreaData* area) } AOPacket response_cc("CharsCheck", chars_taken); - broadcast(response_cc); + broadcast(response_cc, area->index); } -void Server::broadcast(AOPacket packet) +void Server::broadcast(AOPacket packet, int area_index) { // TODO: make this selective to the current area only for (AOClient* client : clients) { - client->sendPacket(packet); + if (client->current_area == area_index) + client->sendPacket(packet); } }