diff --git a/include/advertiser.h b/include/advertiser.h index bdd834c..62ab1f6 100644 --- a/include/advertiser.h +++ b/include/advertiser.h @@ -89,6 +89,14 @@ class Advertiser : public QObject { */ void socketDisconnected(); + /** + * @brief Handles updating the advertiser and recontacting the master server. + * + * @param p_name The new server name. + * @param p_desc The new server description. + */ + void reloadRequested(QString p_name, QString p_desc); + private: /** * @copydoc ConfigManager::server_settings::ms_ip diff --git a/include/aoclient.h b/include/aoclient.h index d06c073..52cb9cc 100644 --- a/include/aoclient.h +++ b/include/aoclient.h @@ -53,7 +53,7 @@ class AOClient : public QObject { AOClient(Server* p_server, QTcpSocket* p_socket, QObject* parent = nullptr, int user_id = 0) : QObject(parent), id(user_id), remote_ip(p_socket->peerAddress()), password(""), joined(false), current_area(0), current_char(""), socket(p_socket), server(p_server), - is_partial(false), last_wtce_time(0), last_message("") {}; + is_partial(false), last_wtce_time(0) {}; /** * @brief Destructor for the AOClient instance. @@ -1452,6 +1452,36 @@ class AOClient : public QObject { * @iscommand */ void cmdUnShake(int argc, QStringList argv); + + /** + * @brief Reloads all server configuration files. + * + * @details No arguments. + * + * @iscommand + */ + void cmdReload(int argc, QStringList argv); + + /** + * @brief Sends an out-of-character message with the judgelog of an area. + * + * @details No arguments. + * + * @iscommand + */ + void cmdJudgeLog(int argc, QStringList argv); + + /** + * @brief Looks up info on a ban. + * + * @details If it is called with **one argument**, that argument is the ban ID to look up. + * + * If it is called with **two arguments**, then the first argument is either a ban ID, an IPID, + * or an HDID, and the the second argument specifies the ID type. + * + * @iscommand + */ + void cmdBanInfo(int argc, QStringList argv); ///@} @@ -1468,12 +1498,12 @@ class AOClient : public QObject { * @brief Returns a textual representation of the time left in an area's Timer. * * @param area_idx The ID of the area whose timer to grab. - * @param timer The pointer to the area's timer. + * @param timer_idx The ID of the timer to grab * * @return A textual representation of the time left over on the Timer, * or `"Timer is inactive"` if the timer wasn't started. */ - QString getAreaTimer(int area_idx, QTimer* timer); + QString getAreaTimer(int area_idx, int timer_idx); /** * @brief Generates a tsuserver3-style area list to be displayed to the user in the out-of-character chat. @@ -1577,78 +1607,81 @@ class AOClient : public QObject { * See @ref CommandInfo "the type's documentation" for more details. */ const QMap commands { - {"login", {ACLFlags.value("NONE"), 1, &AOClient::cmdLogin}}, - {"getareas", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetAreas}}, - {"getarea", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetArea}}, - {"ban", {ACLFlags.value("BAN"), 2, &AOClient::cmdBan}}, - {"kick", {ACLFlags.value("KICK"), 2, &AOClient::cmdKick}}, - {"changeauth", {ACLFlags.value("SUPER"), 0, &AOClient::cmdChangeAuth}}, - {"rootpass", {ACLFlags.value("SUPER"), 1, &AOClient::cmdSetRootPass}}, - {"background", {ACLFlags.value("NONE"), 1, &AOClient::cmdSetBackground}}, - {"bg", {ACLFlags.value("NONE"), 1, &AOClient::cmdSetBackground}}, - {"bglock", {ACLFlags.value("BGLOCK"), 0, &AOClient::cmdBgLock}}, - {"bgunlock", {ACLFlags.value("BGLOCK"), 0, &AOClient::cmdBgUnlock}}, - {"adduser", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddUser}}, - {"listperms", {ACLFlags.value("NONE"), 0, &AOClient::cmdListPerms}}, - {"addperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddPerms}}, - {"removeperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdRemovePerms}}, - {"listusers", {ACLFlags.value("MODIFY_USERS"), 0, &AOClient::cmdListUsers}}, - {"logout", {ACLFlags.value("NONE"), 0, &AOClient::cmdLogout}}, - {"pos", {ACLFlags.value("NONE"), 1, &AOClient::cmdPos}}, - {"g", {ACLFlags.value("NONE"), 1, &AOClient::cmdG}}, - {"need", {ACLFlags.value("NONE"), 1, &AOClient::cmdNeed}}, - {"coinflip", {ACLFlags.value("NONE"), 0, &AOClient::cmdFlip}}, - {"roll", {ACLFlags.value("NONE"), 0, &AOClient::cmdRoll}}, - {"rollp", {ACLFlags.value("NONE"), 0, &AOClient::cmdRollP}}, - {"doc", {ACLFlags.value("NONE"), 0, &AOClient::cmdDoc}}, - {"cleardoc", {ACLFlags.value("NONE"), 0, &AOClient::cmdClearDoc}}, - {"cm", {ACLFlags.value("NONE"), 0, &AOClient::cmdCM}}, - {"uncm", {ACLFlags.value("CM"), 0, &AOClient::cmdUnCM}}, - {"invite", {ACLFlags.value("CM"), 1, &AOClient::cmdInvite}}, - {"uninvite", {ACLFlags.value("CM"), 1, &AOClient::cmdUnInvite}}, - {"lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}}, - {"area_lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}}, - {"spectatable", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}}, - {"area_spectate", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}}, - {"unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}}, - {"area_unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}}, - {"timer", {ACLFlags.value("CM"), 0, &AOClient::cmdTimer}}, - {"area", {ACLFlags.value("NONE"), 1, &AOClient::cmdArea}}, - {"play", {ACLFlags.value("CM"), 1, &AOClient::cmdPlay}}, - {"areakick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}}, - {"area_kick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}}, - {"randomchar", {ACLFlags.value("NONE"), 0, &AOClient::cmdRandomChar}}, - {"switch", {ACLFlags.value("NONE"), 1, &AOClient::cmdSwitch}}, - {"toggleglobal", {ACLFlags.value("NONE"), 0, &AOClient::cmdToggleGlobal}}, - {"mods", {ACLFlags.value("NONE"), 0, &AOClient::cmdMods}}, - {"help", {ACLFlags.value("NONE"), 0, &AOClient::cmdHelp}}, - {"status", {ACLFlags.value("NONE"), 1, &AOClient::cmdStatus}}, - {"forcepos", {ACLFlags.value("CM"), 2, &AOClient::cmdForcePos}}, - {"currentmusic", {ACLFlags.value("NONE"), 0, &AOClient::cmdCurrentMusic}}, - {"pm", {ACLFlags.value("NONE"), 2, &AOClient::cmdPM}}, - {"evidence_mod", {ACLFlags.value("EVI_MOD"), 1, &AOClient::cmdEvidenceMod}}, - {"motd", {ACLFlags.value("NONE"), 0, &AOClient::cmdMOTD}}, - {"announce", {ACLFlags.value("ANNOUNCE"), 1, &AOClient::cmdAnnounce}}, - {"m", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdM}}, - {"gm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdGM}}, - {"mute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdMute}}, - {"unmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnMute}}, - {"bans", {ACLFlags.value("BAN"), 0, &AOClient::cmdBans}}, - {"unban", {ACLFlags.value("BAN"), 1, &AOClient::cmdUnBan}}, - {"removeuser", {ACLFlags.value("MODIFY_USERS"), 1, &AOClient::cmdRemoveUser}}, - {"subtheme", {ACLFlags.value("CM"), 1, &AOClient::cmdSubTheme}}, - {"about", {ACLFlags.value("NONE"), 0, &AOClient::cmdAbout}}, - {"evidence_swap", {ACLFlags.value("CM"), 2, &AOClient::cmdEvidence_Swap}}, - {"notecard", {ACLFlags.value("NONE"), 1, &AOClient::cmdNoteCard}}, - {"notecardreveal", {ACLFlags.value("CM"), 0, &AOClient::cmdNoteCardReveal}}, - {"notecard_reveal", {ACLFlags.value("CM"), 0, &AOClient::cmdNoteCardReveal}}, - {"notecardclear", {ACLFlags.value("NONE"), 0, &AOClient::cmdNoteCardClear}}, - {"notecard_clear", {ACLFlags.value("NONE"), 0, &AOClient::cmdNoteCardClear}}, - {"8ball", {ACLFlags.value("NONE"), 1, &AOClient::cmd8Ball}}, - {"lm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdLM}}, + {"login", {ACLFlags.value("NONE"), 1, &AOClient::cmdLogin}}, + {"getareas", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetAreas}}, + {"getarea", {ACLFlags.value("NONE"), 0, &AOClient::cmdGetArea}}, + {"ban", {ACLFlags.value("BAN"), 2, &AOClient::cmdBan}}, + {"kick", {ACLFlags.value("KICK"), 2, &AOClient::cmdKick}}, + {"changeauth", {ACLFlags.value("SUPER"), 0, &AOClient::cmdChangeAuth}}, + {"rootpass", {ACLFlags.value("SUPER"), 1, &AOClient::cmdSetRootPass}}, + {"background", {ACLFlags.value("NONE"), 1, &AOClient::cmdSetBackground}}, + {"bg", {ACLFlags.value("NONE"), 1, &AOClient::cmdSetBackground}}, + {"bglock", {ACLFlags.value("BGLOCK"), 0, &AOClient::cmdBgLock}}, + {"bgunlock", {ACLFlags.value("BGLOCK"), 0, &AOClient::cmdBgUnlock}}, + {"adduser", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddUser}}, + {"listperms", {ACLFlags.value("NONE"), 0, &AOClient::cmdListPerms}}, + {"addperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdAddPerms}}, + {"removeperm", {ACLFlags.value("MODIFY_USERS"), 2, &AOClient::cmdRemovePerms}}, + {"listusers", {ACLFlags.value("MODIFY_USERS"), 0, &AOClient::cmdListUsers}}, + {"logout", {ACLFlags.value("NONE"), 0, &AOClient::cmdLogout}}, + {"pos", {ACLFlags.value("NONE"), 1, &AOClient::cmdPos}}, + {"g", {ACLFlags.value("NONE"), 1, &AOClient::cmdG}}, + {"need", {ACLFlags.value("NONE"), 1, &AOClient::cmdNeed}}, + {"coinflip", {ACLFlags.value("NONE"), 0, &AOClient::cmdFlip}}, + {"roll", {ACLFlags.value("NONE"), 0, &AOClient::cmdRoll}}, + {"rollp", {ACLFlags.value("NONE"), 0, &AOClient::cmdRollP}}, + {"doc", {ACLFlags.value("NONE"), 0, &AOClient::cmdDoc}}, + {"cleardoc", {ACLFlags.value("NONE"), 0, &AOClient::cmdClearDoc}}, + {"cm", {ACLFlags.value("NONE"), 0, &AOClient::cmdCM}}, + {"uncm", {ACLFlags.value("CM"), 0, &AOClient::cmdUnCM}}, + {"invite", {ACLFlags.value("CM"), 1, &AOClient::cmdInvite}}, + {"uninvite", {ACLFlags.value("CM"), 1, &AOClient::cmdUnInvite}}, + {"lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}}, + {"area_lock", {ACLFlags.value("CM"), 0, &AOClient::cmdLock}}, + {"spectatable", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}}, + {"area_spectate", {ACLFlags.value("CM"), 0, &AOClient::cmdSpectatable}}, + {"unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}}, + {"area_unlock", {ACLFlags.value("CM"), 0, &AOClient::cmdUnLock}}, + {"timer", {ACLFlags.value("CM"), 0, &AOClient::cmdTimer}}, + {"area", {ACLFlags.value("NONE"), 1, &AOClient::cmdArea}}, + {"play", {ACLFlags.value("CM"), 1, &AOClient::cmdPlay}}, + {"areakick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}}, + {"area_kick", {ACLFlags.value("CM"), 1, &AOClient::cmdAreaKick}}, + {"randomchar", {ACLFlags.value("NONE"), 0, &AOClient::cmdRandomChar}}, + {"switch", {ACLFlags.value("NONE"), 1, &AOClient::cmdSwitch}}, + {"toggleglobal", {ACLFlags.value("NONE"), 0, &AOClient::cmdToggleGlobal}}, + {"mods", {ACLFlags.value("NONE"), 0, &AOClient::cmdMods}}, + {"help", {ACLFlags.value("NONE"), 0, &AOClient::cmdHelp}}, + {"status", {ACLFlags.value("NONE"), 1, &AOClient::cmdStatus}}, + {"forcepos", {ACLFlags.value("CM"), 2, &AOClient::cmdForcePos}}, + {"currentmusic", {ACLFlags.value("NONE"), 0, &AOClient::cmdCurrentMusic}}, + {"pm", {ACLFlags.value("NONE"), 2, &AOClient::cmdPM}}, + {"evidence_mod", {ACLFlags.value("EVI_MOD"), 1, &AOClient::cmdEvidenceMod}}, + {"motd", {ACLFlags.value("NONE"), 0, &AOClient::cmdMOTD}}, + {"announce", {ACLFlags.value("ANNOUNCE"), 1, &AOClient::cmdAnnounce}}, + {"m", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdM}}, + {"gm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdGM}}, + {"mute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdMute}}, + {"unmute", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnMute}}, + {"bans", {ACLFlags.value("BAN"), 0, &AOClient::cmdBans}}, + {"unban", {ACLFlags.value("BAN"), 1, &AOClient::cmdUnBan}}, + {"removeuser", {ACLFlags.value("MODIFY_USERS"), 1, &AOClient::cmdRemoveUser}}, + {"subtheme", {ACLFlags.value("CM"), 1, &AOClient::cmdSubTheme}}, + {"about", {ACLFlags.value("NONE"), 0, &AOClient::cmdAbout}}, + {"evidence_swap", {ACLFlags.value("CM"), 2, &AOClient::cmdEvidence_Swap}}, + {"notecard", {ACLFlags.value("NONE"), 1, &AOClient::cmdNoteCard}}, + {"notecardreveal", {ACLFlags.value("CM"), 0, &AOClient::cmdNoteCardReveal}}, + {"notecard_reveal", {ACLFlags.value("CM"), 0, &AOClient::cmdNoteCardReveal}}, + {"notecardclear", {ACLFlags.value("NONE"), 0, &AOClient::cmdNoteCardClear}}, + {"notecard_clear", {ACLFlags.value("NONE"), 0, &AOClient::cmdNoteCardClear}}, + {"8ball", {ACLFlags.value("NONE"), 1, &AOClient::cmd8Ball}}, + {"lm", {ACLFlags.value("MODCHAT"), 1, &AOClient::cmdLM}}, + {"judgelog", {ACLFlags.value("CM"), 0, &AOClient::cmdJudgeLog}}, {"allow_blankposting", {ACLFlags.value("MODCHAT"), 0, &AOClient::cmdAllow_Blankposting}}, - {"gimp", {ACLFlags.value("MUTE"), 1, &AOClient::cmdGimp}}, - {"ungimp", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnGimp}}, + {"gimp", {ACLFlags.value("MUTE"), 1, &AOClient::cmdGimp}}, + {"ungimp", {ACLFlags.value("MUTE"), 1, &AOClient::cmdUnGimp}}, + {"baninfo", {ACLFlags.value("BAN"), 1, &AOClient::cmdBanInfo}}, + {"reload", {ACLFlags.value("SUPER"), 0, &AOClient::cmdReload}}, }; /** @@ -1696,6 +1729,17 @@ class AOClient : public QObject { * @details Used to determine if the incoming message is a duplicate. */ QString last_message; + + /** + * @brief A helper function to add recorded packets to an area's judgelog. + * + * @param area Pointer to the area where the packet was sent. + * + * @param client Pointer to the client that sent the packet. + * + * @param action String containing the info that is being recorded. + */ + void updateJudgeLog(AreaData* area, AOClient* client, QString action); }; #endif // AOCLIENT_H diff --git a/include/area_data.h b/include/area_data.h index 4ed538b..4a12ba9 100644 --- a/include/area_data.h +++ b/include/area_data.h @@ -278,10 +278,22 @@ class AreaData : public QObject { EvidenceMod evi_mod; QMap notecards; + /** + * @brief The judgelog of an area. + * + * @details This list contains up to 10 recorded packets of the most recent judge actions (WT/CE or penalty updates) in an area. + */ + QStringList judgelog; + /** * @brief The last IC packet sent in an area. */ QStringList last_ic_message; + + /** + * @brief The value of logger in config.ini. + */ + QString log_type; }; #endif // AREA_DATA_H diff --git a/include/db_manager.h b/include/db_manager.h index 575df07..b696dfe 100644 --- a/include/db_manager.h +++ b/include/db_manager.h @@ -239,6 +239,15 @@ public: */ QStringList getUsers(); + /** + * @brief Gets information on a ban. + * + * @param lookup_type The type of ID to search + * + * @param id A Ban ID, IPID, or HDID to search for + */ + QList getBanInfo(QString lookup_type, QString id); + private: /** * @brief The name of the database connection driver. diff --git a/include/server.h b/include/server.h index 31e0aa9..a02743e 100644 --- a/include/server.h +++ b/include/server.h @@ -125,24 +125,6 @@ class Server : public QObject { */ void broadcast(AOPacket packet); - /** - * @brief Returns the server's name according to the configuration file. - * - * @return See brief description. - */ - QString getServerName(); - - /** - * @brief Returns some value regarding the @ref AOClient::diceThrower "dice thrower commands". - * - * @param value_type `max_value` for the maximum amount of faces a die may have, - * `max_dice` for the maximum amount of dice that may be thrown at once. - * - * @return The associated value if it is found in the configuration file under the "Dice" section, - * or `100` if not. - */ - int getDiceValue(QString value_type); - /** * @brief Returns the character's character ID (= their index in the character list). * @@ -199,23 +181,62 @@ class Server : public QObject { */ DBManager* db_manager; + /** + * @brief The max amount of players on the server. + */ + QString max_players; /** * @brief The user-facing server name. - * - * @note Unused. getServerName() serves its purpose instead. */ QString server_name; + /** + * @brief The server description. + */ + QString server_desc; + /** * @brief The Message Of The Day of the server, shown upon entry to the server and on request. */ QString MOTD; + /** + * @brief The authorization type of the server. + * + * @details In simple mode, the modpass stored in config.ini is used for moderator logins. In advanced mode, logins found in the database are used. + */ + QString auth_type; + + /** + * @brief The modpass for moderator login with simple auth_type. + */ + QString modpass; + + /** + * @brief The amount of subscripts zalgo is stripped by. + */ + int zalgo_tolerance; + + /** + * @brief The highest value dice can have. + */ + uint dice_value; + + /** + * @brief The max amount of dice that can be rolled at once. + */ + int max_dice; + /** * @brief The server-wide global timer. */ QTimer* timer; + /** + * @brief Loads values from config.ini. + */ + void loadServerConfig(); + /** * @brief Loads the configuration files for commands into stringlists. */ @@ -257,6 +278,16 @@ class Server : public QObject { */ void clientConnected(); + signals: + + /** + * @brief Sends the server name and description, emitted by /reload. + * + * @param p_name The server name. + * @param p_desc The server description. + */ + void reloadRequest(QString p_name, QString p_desc); + private: /** * @brief The proxy used for WebSocket connections. diff --git a/src/advertiser.cpp b/src/advertiser.cpp index 5921b53..5bc8aec 100644 --- a/src/advertiser.cpp +++ b/src/advertiser.cpp @@ -64,6 +64,13 @@ void Advertiser::socketDisconnected() qDebug("Connection to master server lost"); } +void Advertiser::reloadRequested(QString p_name, QString p_desc) +{ + name = p_name; + description = p_desc; + socketConnected(); +} + Advertiser::~Advertiser() { socket->deleteLater(); diff --git a/src/aoclient.cpp b/src/aoclient.cpp index d9599b5..762d99f 100644 --- a/src/aoclient.cpp +++ b/src/aoclient.cpp @@ -276,17 +276,17 @@ void AOClient::calculateIpid() void AOClient::sendServerMessage(QString message) { - sendPacket("CT", {server->getServerName(), message, "1"}); + sendPacket("CT", {server->server_name, message, "1"}); } void AOClient::sendServerMessageArea(QString message) { - server->broadcast(AOPacket("CT", {server->getServerName(), message, "1"}), current_area); + server->broadcast(AOPacket("CT", {server->server_name, message, "1"}), current_area); } void AOClient::sendServerBroadcast(QString message) { - server->broadcast(AOPacket("CT", {server->getServerName(), message, "1"})); + server->broadcast(AOPacket("CT", {server->server_name, message, "1"})); } bool AOClient::checkAuth(unsigned long long acl_mask) @@ -300,14 +300,11 @@ bool AOClient::checkAuth(unsigned long long acl_mask) else if (!authenticated) { return false; } - QSettings settings("config/config.ini", QSettings::IniFormat); - settings.beginGroup("Options"); - QString auth_type = settings.value("auth", "simple").toString(); - if (auth_type == "advanced") { + if (server->auth_type == "advanced") { unsigned long long user_acl = server->db_manager->getACL(moderator_name); return (user_acl & acl_mask) != 0; } - else if (auth_type == "simple") { + else if (server->auth_type == "simple") { return authenticated; } } diff --git a/src/area_data.cpp b/src/area_data.cpp index 95926bb..c40271c 100644 --- a/src/area_data.cpp +++ b/src/area_data.cpp @@ -25,6 +25,7 @@ AreaData::AreaData(QString p_name, int p_index) : document("No document."), def_hp(10), pro_hp(10), + judgelog(), last_ic_message() { QStringList name_split = p_name.split(":"); @@ -42,6 +43,7 @@ AreaData::AreaData(QString p_name, int p_index) : QSettings config_ini("config/config.ini", QSettings::IniFormat); config_ini.beginGroup("Options"); int log_size = config_ini.value("logbuffer", 50).toInt(); + log_type = config_ini.value("logger","modcall").toString(); config_ini.endGroup(); if (log_size == 0) log_size = 500; diff --git a/src/commands.cpp b/src/commands.cpp index 2b2ba26..f9c8b22 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -27,21 +27,16 @@ void AOClient::cmdDefault(int argc, QStringList argv) void AOClient::cmdLogin(int argc, QStringList argv) { - QSettings config("config/config.ini", QSettings::IniFormat); - config.beginGroup("Options"); - QString modpass = config.value("modpass", "default").toString(); - QString auth_type = config.value("auth", "simple").toString(); - if (authenticated) { sendServerMessage("You are already logged in!"); return; } - if (auth_type == "simple") { - if (modpass == "") { + if (server->auth_type == "simple") { + if (server->modpass == "") { sendServerMessage("No modpass is set! Please set a modpass before authenticating."); } - else if(argv[0] == modpass) { + else if(argv[0] == server->modpass) { sendPacket("AUTH", {"1"}); // Client: "You were granted the Disable Modcalls button." sendServerMessage("Logged in as a moderator."); // pre-2.9.1 clients are hardcoded to display the mod UI when this string is sent in OOC authenticated = true; @@ -52,7 +47,7 @@ void AOClient::cmdLogin(int argc, QStringList argv) } server->areas.value(current_area)->logger->logLogin(this, authenticated, "moderator"); } - else if (auth_type == "advanced") { + else if (server->auth_type == "advanced") { if (argc < 2) { sendServerMessage("You must specify a username and a password"); return; @@ -194,11 +189,7 @@ void AOClient::cmdKick(int argc, QStringList argv) void AOClient::cmdChangeAuth(int argc, QStringList argv) { - QSettings settings("config/config.ini", QSettings::IniFormat); - settings.beginGroup("Options"); - QString auth_type = settings.value("auth", "simple").toString(); - - if (auth_type == "simple") { + if (server->auth_type == "simple") { change_auth_started = true; sendServerMessage("WARNING!\nThis command will change how logging in as a moderator works.\nOnly proceed if you know what you are doing\nUse the command /rootpass to set the password for your root account."); } @@ -214,6 +205,7 @@ void AOClient::cmdSetRootPass(int argc, QStringList argv) QSettings settings("config/config.ini", QSettings::IniFormat); settings.beginGroup("Options"); settings.setValue("auth", "advanced"); + server->auth_type = "advanced"; #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) qsrand(QDateTime::currentMSecsSinceEpoch()); @@ -664,20 +656,19 @@ void AOClient::cmdTimer(int argc, QStringList argv) { AreaData* area = server->areas[current_area]; + // Called without arguments + // Shows a brief of all timers if (argc == 0) { QStringList timers; timers.append("Currently active timers:"); - QTimer* global_timer = server->timer; - if (global_timer->isActive()) { - QTime current_time = QTime(0,0).addMSecs(global_timer->remainingTime()); - timers.append("Global timer is at " + current_time.toString("hh:mm:ss.zzz")); - } - for (QTimer* timer : area->timers) { - timers.append(getAreaTimer(area->index, timer)); + for (int i = 0; i <= 4; i++) { + timers.append(getAreaTimer(area->index, i)); } sendServerMessage(timers.join("\n")); return; } + + // Called with more than one argument bool ok; int timer_id = argv[0].toInt(&ok); if (!ok || timer_id < 0 || timer_id > 4) { @@ -685,22 +676,18 @@ void AOClient::cmdTimer(int argc, QStringList argv) return; } + // Called with one argument + // Shows the status of one timer if (argc == 1) { - if (timer_id == 0) { - QTimer* global_timer = server->timer; - if (global_timer->isActive()) { - QTime current_time = QTime(0, 0, 0, global_timer->remainingTime()); - sendServerMessage("Global timer is at " + current_time.toString("hh:mm:ss.zzz")); - return; - } - } - else { - QTimer* timer = area->timers[timer_id - 1]; - sendServerMessage(getAreaTimer(area->index, timer)); - return; - } + sendServerMessage(getAreaTimer(area->index, timer_id)); + return; } + // Called with more than one argument + // Updates the state of a timer + + // Select the proper timer + // Check against permissions if global timer is selected QTimer* requested_timer; if (timer_id == 0) { if (!checkAuth(ACLFlags.value("GLOBAL_TIMER"))) { @@ -711,33 +698,45 @@ void AOClient::cmdTimer(int argc, QStringList argv) } else requested_timer = area->timers[timer_id - 1]; + + AOPacket show_timer("TI", {QString::number(timer_id), "2"}); + AOPacket hide_timer("TI", {QString::number(timer_id), "3"}); + bool is_global = timer_id == 0; + + // Set the timer's time remaining if the second + // argument is a valid time QTime requested_time = QTime::fromString(argv[1], "hh:mm:ss"); if (requested_time.isValid()) { requested_timer->setInterval(QTime(0,0).msecsTo(requested_time)); requested_timer->start(); sendServerMessage("Set timer " + QString::number(timer_id) + " to " + argv[1] + "."); - sendPacket("TI", {QString::number(timer_id), "2"}); // Show the timer - sendPacket("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(requested_time))}); + AOPacket update_timer("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(requested_time))}); + is_global ? server->broadcast(show_timer) : server->broadcast(show_timer, current_area); // Show the timer + is_global ? server->broadcast(update_timer) : server->broadcast(update_timer, current_area); return; } + // Otherwise, update the state of the timer else { if (argv[1] == "start") { requested_timer->start(); sendServerMessage("Started timer " + QString::number(timer_id) + "."); - sendPacket("TI", {QString::number(timer_id), "2"}); // Show the timer - sendPacket("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->remainingTime())))}); + AOPacket update_timer("TI", {QString::number(timer_id), "0", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->remainingTime())))}); + is_global ? server->broadcast(show_timer) : server->broadcast(show_timer, current_area); + is_global ? server->broadcast(update_timer) : server->broadcast(update_timer, current_area); } else if (argv[1] == "pause" || argv[1] == "stop") { requested_timer->setInterval(requested_timer->remainingTime()); requested_timer->stop(); sendServerMessage("Stopped timer " + QString::number(timer_id) + "."); - sendPacket("TI", {QString::number(timer_id), "1", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->interval())))}); + AOPacket update_timer("TI", {QString::number(timer_id), "1", QString::number(QTime(0,0).msecsTo(QTime(0,0).addMSecs(requested_timer->interval())))}); + is_global ? server->broadcast(update_timer) : server->broadcast(update_timer, current_area); } else if (argv[1] == "hide" || argv[1] == "unset") { requested_timer->setInterval(0); requested_timer->stop(); sendServerMessage("Hid timer " + QString::number(timer_id) + "."); - sendPacket("TI", {QString::number(timer_id), "3"}); // Hide the timer + // Hide the timer + is_global ? server->broadcast(hide_timer) : server->broadcast(hide_timer, current_area); } } } @@ -828,14 +827,11 @@ void AOClient::cmdToggleGlobal(int argc, QStringList argv) void AOClient::cmdMods(int argc, QStringList argv) { QStringList entries; - QSettings config("config/config.ini", QSettings::IniFormat); - config.beginGroup("Options"); - QString auth_type = config.value("auth", "simple").toString(); int online_count = 0; for (AOClient* client : server->clients) { if (client->authenticated) { entries << "---"; - if (auth_type != "simple") + if (server->auth_type != "simple") entries << "Moderator: " + client->moderator_name; entries << "OOC name: " + client->ooc_name; entries << "ID: " + QString::number(client->id); @@ -1254,8 +1250,7 @@ void AOClient::cmd8Ball(int argc, QStringList argv) sendServerMessage("8ball.txt is empty."); } else { - int answerindex = server->magic_8ball_answers.size(); - QString response = server->magic_8ball_answers[(genRand(1, answerindex))]; + QString response = server->magic_8ball_answers[(genRand(1, server->magic_8ball_answers.size() - 1))]; QString sender_name = ooc_name; QString sender_message = argv.join(" "); @@ -1263,6 +1258,24 @@ void AOClient::cmd8Ball(int argc, QStringList argv) } } +void AOClient::cmdJudgeLog(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->judgelog.isEmpty()) { + sendServerMessage("There have been no judge actions in this area."); + return; + } + QString message = area->judgelog.join("\n"); + //Judgelog contains an IPID, so we shouldn't send that unless the caller has appropriate permissions + if (checkAuth(ACLFlags.value("KICK")) == 1 || checkAuth(ACLFlags.value("BAN")) == 1) { + sendServerMessage(message); + } + else { + QString filteredmessage = message.remove(QRegularExpression("[(].*[)]")); //Filter out anything between two parentheses. This should only ever be the IPID + sendServerMessage(filteredmessage); + } +} + void AOClient::cmdAllow_Blankposting(int argc, QStringList argv) { QString sender_name = ooc_name; @@ -1315,6 +1328,51 @@ void AOClient::cmdUnGimp(int argc, QStringList argv) } target->is_gimped = false; } +void AOClient::cmdBanInfo(int argc, QStringList argv) +{ + QStringList ban_info; + ban_info << ("Ban Info for " + argv[0]); + ban_info << "-----"; + QString lookup_type; + + if (argc == 1) { + lookup_type = "banid"; + } + else if (argc == 2) { + lookup_type = argv[1]; + if (!((lookup_type == "banid") || (lookup_type == "ipid") || (lookup_type == "hdid"))) { + sendServerMessage("Invalid ID type."); + return; + } + } + else { + sendServerMessage("Invalid command."); + return; + } + QString id = argv[0]; + for (DBManager::BanInfo ban : server->db_manager->getBanInfo(lookup_type, id)) { + QString banned_until; + if (ban.duration == -2) + banned_until = "The heat death of the universe"; + else + banned_until = QDateTime::fromSecsSinceEpoch(ban.time).addSecs(ban.duration).toString("dd.MM.yyyy, hh:mm"); + ban_info << "Affected IPID: " + ban.ipid; + ban_info << "Affected HDID: " + ban.hdid; + ban_info << "Reason for ban: " + ban.reason; + ban_info << "Date of ban: " + QDateTime::fromSecsSinceEpoch(ban.time).toString("dd.MM.yyyy, hh:mm"); + ban_info << "Ban lasts until: " + banned_until; + ban_info << "-----"; + } + sendServerMessage(ban_info.join("\n")); +} + +void AOClient::cmdReload(int argc, QStringList argv) +{ + server->loadServerConfig(); + server->loadCommandConfig(); + emit server->reloadRequest(server->server_name, server->server_desc); + sendServerMessage("Reloaded configurations"); +} QStringList AOClient::buildAreaList(int area_idx) { @@ -1365,8 +1423,8 @@ int AOClient::genRand(int min, int max) void AOClient::diceThrower(int argc, QStringList argv, RollType type) { QString sender_name = ooc_name; - int max_value = server->getDiceValue("max_value"); - int max_dice = server->getDiceValue("max_dice"); + int max_value = server->dice_value; + int max_dice = server->max_dice; int bounded_value; int bounded_amount; QString dice_results; @@ -1420,15 +1478,26 @@ void AOClient::diceThrower(int argc, QStringList argv, RollType type) } } -QString AOClient::getAreaTimer(int area_idx, QTimer* timer) +QString AOClient::getAreaTimer(int area_idx, int timer_idx) { AreaData* area = server->areas[area_idx]; + QTimer* timer; + QString timer_name = (timer_idx == 0) ? "Global timer" : "Timer " + QString::number(timer_idx); + + if (timer_idx == 0) + timer = server->timer; + else if (timer_idx > 0 && timer_idx <= 4) + timer = area->timers[timer_idx - 1]; + else + return "Invalid timer ID."; + if (timer->isActive()) { QTime current_time = QTime(0,0).addMSecs(timer->remainingTime()); - return "Timer " + QString::number(area->timers.indexOf(timer) + 1) + " is at " + current_time.toString("hh:mm:ss.zzz"); + + return timer_name + " is at " + current_time.toString("hh:mm:ss.zzz"); } else { - return "Timer " + QString::number(area->timers.indexOf(timer) + 1) + " is inactive."; + return timer_name + " is inactive."; } } diff --git a/src/db_manager.cpp b/src/db_manager.cpp index 217ca25..861ec1a 100644 --- a/src/db_manager.cpp +++ b/src/db_manager.cpp @@ -325,6 +325,42 @@ QStringList DBManager::getUsers() return users; } +QList DBManager::getBanInfo(QString lookup_type, QString id) +{ + QList return_list; + QSqlQuery query; + QList invalid; + if (lookup_type == "banid") { + query.prepare("SELECT * FROM BANS WHERE ID = ?"); + } + else if (lookup_type == "hdid") { + query.prepare("SELECT * FROM BANS WHERE HDID = ?"); + } + else if (lookup_type == "ipid") { + query.prepare("SELECT * FROM BANS WHERE IPID = ?"); + } + else { + qCritical("Invalid ban lookup type!"); + return invalid; + } + query.addBindValue(id); + query.setForwardOnly(true); + query.exec(); + + while (query.next()) { + BanInfo ban; + ban.ipid = query.value(0).toString(); + ban.hdid = query.value(1).toString(); + ban.ip = QHostAddress(query.value(2).toString()); + ban.time = static_cast(query.value(3).toULongLong()); + ban.reason = query.value(4).toString(); + ban.duration = query.value(5).toLongLong(); + return_list.append(ban); + } + std::reverse(return_list.begin(), return_list.end()); + return return_list; +} + DBManager::~DBManager() { db.close(); diff --git a/src/logger.cpp b/src/logger.cpp index 4dca853..96ea2c6 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -77,13 +77,10 @@ QString Logger::buildEntry(AOClient *client, QString type, QString message) void Logger::addEntry(QString entry) { - QSettings config("config/config.ini", QSettings::IniFormat); - config.beginGroup("Options"); - QString log_type = config.value("logging", "modcall").toString(); if (buffer.length() < max_length) { buffer.enqueue(entry); - if (log_type == "full") { - flush(); + if (area->log_type == "full") { + flush(); } } else { @@ -99,20 +96,20 @@ void Logger::flush() dir.mkpath("."); } - QSettings config("config/config.ini", QSettings::IniFormat); - config.beginGroup("Options"); - QString log_type = config.value("logging", "modcall").toString(); QFile logfile; - if (log_type == "modcall") { + if (area->log_type == "modcall") { logfile.setFileName(QString("logs/report_%1_%2.log").arg((area->name), (QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss")))); } - else if (log_type == "full") { + else if (area->log_type == "full") { logfile.setFileName(QString("logs/%1.log").arg(QDate::currentDate().toString("yyyy-MM-dd"))); } - if (logfile.open(QIODevice::WriteOnly | QIODevice::Append)) { - QTextStream file_stream(&logfile); - while (!buffer.isEmpty()) - file_stream << buffer.dequeue(); + else { + qCritical("Invalid logger set!"); + } + if (logfile.open(QIODevice::WriteOnly | QIODevice::Append)) { + QTextStream file_stream(&logfile); + while (!buffer.isEmpty()) + file_stream << buffer.dequeue(); } logfile.close(); } diff --git a/src/main.cpp b/src/main.cpp index defcf23..8579592 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,6 +62,7 @@ int main(int argc, char* argv[]) } server = new Server(settings.port, settings.ws_port); + QObject::connect(server, &Server::reloadRequest, advertiser, &Advertiser::reloadRequested); server->start(); } } else { diff --git a/src/packets.cpp b/src/packets.cpp index 99fcf70..cfa12ee 100644 --- a/src/packets.cpp +++ b/src/packets.cpp @@ -37,10 +37,7 @@ void AOClient::pktHardwareId(AreaData* area, int argc, QStringList argv, AOPacke void AOClient::pktSoftwareId(AreaData* area, int argc, QStringList argv, AOPacket packet) { - QSettings config("config/config.ini", QSettings::IniFormat); - config.beginGroup("Options"); - QString max_players = config.value("max_players").toString(); - config.endGroup(); + // Full feature list as of AO 2.8.5 // The only ones that are critical to ensuring the server works are @@ -64,7 +61,7 @@ void AOClient::pktSoftwareId(AreaData* area, int argc, QStringList argv, AOPacke version.minor = match.captured(3).toInt(); } - sendPacket("PN", {QString::number(server->player_count), max_players}); + sendPacket("PN", {QString::number(server->player_count), server->max_players}); sendPacket("FL", feature_list); } @@ -181,10 +178,12 @@ void AOClient::pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket p } ooc_name = dezalgo(argv[0]).replace(QRegExp("\\[|\\]|\\{|\\}|\\#|\\$|\\%|\\&"), ""); // no fucky wucky shit here - if (ooc_name.isEmpty() || ooc_name == server->getServerName()) // impersonation & empty name protection + if (ooc_name.isEmpty() || ooc_name == server->server_name) // impersonation & empty name protection return; QString message = dezalgo(argv[1]); + if (message.length() == 0) + return; AOPacket final_packet("CT", {ooc_name, message, "0"}); if(message.at(0) == '/') { QStringList cmd_argv = message.split(" ", QString::SplitBehavior::SkipEmptyParts); @@ -263,6 +262,7 @@ void AOClient::pktWtCe(AreaData* area, int argc, QStringList argv, AOPacket pack return; last_wtce_time = QDateTime::currentDateTime().toSecsSinceEpoch(); server->broadcast(packet, current_area); + updateJudgeLog(area, this, "WT/CE"); } void AOClient::pktHpBar(AreaData* area, int argc, QStringList argv, AOPacket packet) @@ -279,6 +279,7 @@ void AOClient::pktHpBar(AreaData* area, int argc, QStringList argv, AOPacket pac } server->broadcast(AOPacket("HP", {"1", QString::number(area->def_hp)}), area->index); server->broadcast(AOPacket("HP", {"2", QString::number(area->pro_hp)}), area->index); + updateJudgeLog(area, this, "updated the penalties"); } void AOClient::pktWebSocketIp(AreaData* area, int argc, QStringList argv, AOPacket packet) @@ -433,7 +434,9 @@ AOPacket AOClient::validateIcPacket(AOPacket packet) // message text QString incoming_msg = dezalgo(incoming_args[4].toString().trimmed()); - if (incoming_msg == last_message) + if (!area->last_ic_message.isEmpty() + && incoming_msg == area->last_ic_message[4] + && incoming_msg != "") return invalid; if (incoming_msg == "" && area->blankposting_allowed == false) { @@ -634,14 +637,7 @@ AOPacket AOClient::validateIcPacket(AOPacket packet) QString AOClient::dezalgo(QString p_text) { - QSettings config("config/config.ini", QSettings::IniFormat); - config.beginGroup("Options"); - bool zalgo_tolerance_conversion_success; - int zalgo_tolerance = config.value("zalgo_tolerance", "3").toInt(&zalgo_tolerance_conversion_success); - if (!zalgo_tolerance_conversion_success) - zalgo_tolerance = 3; - - QRegExp rxp("([\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f\u115f\u1160\u3164]{" + QRegExp::escape(QString::number(zalgo_tolerance)) + ",})"); + QRegExp rxp("([\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f\u115f\u1160\u3164]{" + QRegExp::escape(QString::number(server->zalgo_tolerance)) + ",})"); QString filtered = p_text.replace(rxp, ""); return filtered; } @@ -660,3 +656,21 @@ bool AOClient::checkEvidenceAccess(AreaData *area) return false; } } + +void AOClient::updateJudgeLog(AreaData* area, AOClient* client, QString action) +{ + QString timestamp = QTime::currentTime().toString("hh:mm:ss"); + QString uid = QString::number(client->id); + QString char_name = client->current_char; + QString ipid = client->getIpid(); + QString message = action; + QString logmessage = QString("[%1]: [%2] %3 (%4) %5").arg(timestamp, uid, char_name, ipid, message); + int size = area->judgelog.size(); + if (size == 10) { + area->judgelog.removeFirst(); + area->judgelog.append(logmessage); + } + else area->judgelog.append(logmessage); + + +} diff --git a/src/server.cpp b/src/server.cpp index a8aa299..9ac35c8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -51,7 +51,8 @@ void Server::start() qDebug() << "Server listening on" << port; } - MOTD = config.value("motd","MOTD is not set.").toString(); + loadServerConfig(); + loadCommandConfig(); proxy = new WSProxy(port, ws_port, this); if(ws_port != -1) @@ -96,7 +97,6 @@ void Server::start() QString area_name = raw_area_names[i]; areas.insert(i, new AreaData(area_name, i)); } - loadCommandConfig(); } void Server::clientConnected() @@ -169,24 +169,6 @@ void Server::broadcast(AOPacket packet) } } -QString Server::getServerName() -{ - QSettings settings("config/config.ini", QSettings::IniFormat); - settings.beginGroup("Options"); - QString server_name = settings.value("server_name", "Akashi").toString(); - return server_name; -} - -int Server::getDiceValue(QString value_type) -{ - QSettings settings("config/config.ini", QSettings::IniFormat); - - settings.beginGroup("Dice"); - int value = settings.value(value_type, "100").toUInt(); - settings.endGroup(); - return value; -} - QList Server::getClientsByIpid(QString ipid) { QList return_clients; @@ -218,10 +200,10 @@ int Server::getCharID(QString char_name) void Server::loadCommandConfig() { - magic_8ball_answers.append(loadConfigFile("8ball")); - praise_list.append(loadConfigFile("praise")); - reprimands_list.append(loadConfigFile("reprimands")); - gimp_list.append(loadConfigFile("gimp")); + magic_8ball_answers = (loadConfigFile("8ball")); + praise_list = (loadConfigFile("praise")); + reprimands_list = (loadConfigFile("reprimands")); + gimp_list = (loadConfigFile("gimp")); } QStringList Server::loadConfigFile(QString filename) @@ -236,6 +218,30 @@ QStringList Server::loadConfigFile(QString filename) return stringlist; } +void Server::loadServerConfig() +{ + QSettings config("config/config.ini", QSettings::IniFormat); + config.beginGroup("Options"); + //Load config.ini values + max_players = config.value("max_players","100").toString(); + server_name = config.value("server_name","An Unnamed Server").toString(); + server_desc = config.value("server_description","This is a placeholder server description. Tell the world of AO who you are here!").toString(); + MOTD = config.value("motd","MOTD is not set.").toString(); + auth_type = config.value("auth","simple").toString(); + modpass = config.value("modpass","").toString(); + bool zalgo_tolerance_conversion_success; + zalgo_tolerance = config.value("zalgo_tolerance", "3").toInt(&zalgo_tolerance_conversion_success); + if (!zalgo_tolerance_conversion_success) + zalgo_tolerance = 3; + config.endGroup(); + + //Load dice values + config.beginGroup("Dice"); + dice_value = config.value("value_type", "100").toInt(); + max_dice = config.value("max_dice","100").toInt(); + config.endGroup(); +} + Server::~Server() { for (AOClient* client : clients) {