diff --git a/akashi.pro b/akashi.pro index f176d52..28d80f4 100644 --- a/akashi.pro +++ b/akashi.pro @@ -31,8 +31,14 @@ SOURCES += src/advertiser.cpp \ src/aopacket.cpp \ src/area_data.cpp \ src/commands.cpp \ - src/commands/areas.cpp \ + src/commands/area.cpp \ src/commands/authentication.cpp \ + src/commands/casing.cpp \ + src/commands/command_helper.cpp \ + src/commands/messaging.cpp \ + src/commands/moderation.cpp \ + src/commands/music.cpp \ + src/commands/roleplay.cpp \ src/config_manager.cpp \ src/db_manager.cpp \ src/logger.cpp \ diff --git a/include/aoclient.h b/include/aoclient.h index 16b4cfd..5035b6b 100644 --- a/include/aoclient.h +++ b/include/aoclient.h @@ -1331,16 +1331,6 @@ class AOClient : public QObject { */ void cmdLM(int argc, QStringList argv); - ///@} - - /** - * @name Fun - * - * @brief All functions that detail the actions of commands, - * that are related to various kinds of fun moderator commands. - */ - ///@{ - /** * @brief Replaces a target client's in-character messages with strings randomly selected from gimp.txt. * diff --git a/src/commands.cpp b/src/commands.cpp index 4742d2a..9824cfa 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -18,1449 +18,3 @@ #include "include/aoclient.h" // Be sure to register the command in the header before adding it here! - -void AOClient::cmdDefault(int argc, QStringList argv) -{ - sendServerMessage("Invalid command."); - return; -} - -void AOClient::cmdGetAreas(int argc, QStringList argv) -{ - QStringList entries; - entries.append("== Area List =="); - for (int i = 0; i < server->area_names.length(); i++) { - QStringList cur_area_lines = buildAreaList(i); - entries.append(cur_area_lines); - } - sendServerMessage(entries.join("\n")); -} - -void AOClient::cmdGetArea(int argc, QStringList argv) -{ - QStringList entries = buildAreaList(current_area); - sendServerMessage(entries.join("\n")); -} - -void AOClient::cmdBan(int argc, QStringList argv) -{ - QString args_str = argv[1]; - if (argc > 2) { - for (int i = 2; i < argc; i++) - args_str += " " + argv[i]; - } - - DBManager::BanInfo ban; - - QRegularExpression quoteMatcher("['\"](.+?)[\"']"); - QRegularExpressionMatchIterator matches = quoteMatcher.globalMatch(args_str); - QList unquoted_args; - while (matches.hasNext()) { - QRegularExpressionMatch match = matches.next(); - unquoted_args.append(match.captured(1)); - } - - QString duration = "perma"; - - if (unquoted_args.length() < 1) { - sendServerMessage("Invalid syntax. Usage:\n/ban \"\" \"\""); - return; - } - - ban.reason = unquoted_args.at(0); - if (unquoted_args.length() > 1) - duration = unquoted_args.at(1); - - long long duration_seconds = 0; - if (duration == "perma") - duration_seconds = -2; - else - duration_seconds = parseTime(duration); - - if (duration_seconds == -1) { - sendServerMessage("Invalid time format. Format example: 1h30m"); - return; - } - - ban.duration = duration_seconds; - - ban.ipid = argv[0]; - ban.time = QDateTime::currentDateTime().toSecsSinceEpoch(); - bool ban_logged = false; - int kick_counter = 0; - - if (argc > 2) { - for (int i = 2; i < argv.length(); i++) { - ban.reason += " " + argv[i]; - } - } - - for (AOClient* client : server->getClientsByIpid(ban.ipid)) { - if (!ban_logged) { - ban.ip = client->remote_ip; - ban.hdid = client->hwid; - server->db_manager->addBan(ban); - sendServerMessage("Banned user with ipid " + ban.ipid + " for reason: " + ban.reason); - ban_logged = true; - } - client->sendPacket("KB", {ban.reason + "\nID: " + QString::number(server->db_manager->getBanID(ban.ip)) + "\nUntil: " + QDateTime::fromSecsSinceEpoch(ban.time).addSecs(ban.duration).toString("dd.MM.yyyy, hh:mm")}); - client->socket->close(); - kick_counter++; - } - - if (kick_counter > 1) - sendServerMessage("Kicked " + QString::number(kick_counter) + " clients with matching ipids."); - if (!ban_logged) - sendServerMessage("User with ipid not found!"); -} - -void AOClient::cmdKick(int argc, QStringList argv) -{ - QString target_ipid = argv[0]; - QString reason = argv[1]; - int kick_counter = 0; - - if (argc > 2) { - for (int i = 2; i < argv.length(); i++) { - reason += " " + argv[i]; - } - } - - for (AOClient* client : server->getClientsByIpid(target_ipid)) { - client->sendPacket("KK", {reason}); - client->socket->close(); - kick_counter++; - } - - if (kick_counter > 0) - sendServerMessage("Kicked " + QString::number(kick_counter) + " client(s) with ipid " + target_ipid + " for reason: " + reason); - else - sendServerMessage("User with ipid not found!"); -} - -void AOClient::cmdSetBackground(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (authenticated || !area->bg_locked) { - if (server->backgrounds.contains(argv[0])) { - area->background = argv[0]; - server->broadcast(AOPacket("BN", {argv[0]}), current_area); - sendServerMessageArea(current_char + " changed the background to " + argv[0]); - } - else { - sendServerMessage("Invalid background name."); - } - } - else { - sendServerMessage("This area's background is locked."); - } -} - -void AOClient::cmdBgLock(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - area->bg_locked = true; - server->broadcast(AOPacket("CT", {"Server", current_char + " locked the background.", "1"}), current_area); -} - -void AOClient::cmdBgUnlock(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - area->bg_locked = false; - server->broadcast(AOPacket("CT", {"Server", current_char + " unlocked the background.", "1"}), current_area); -} - - - -void AOClient::cmdPos(int argc, QStringList argv) -{ - changePosition(argv[0]); - updateEvidenceList(server->areas[current_area]); -} - -void AOClient::cmdForcePos(int argc, QStringList argv) -{ - bool ok; - QList targets; - AreaData* area = server->areas[current_area]; - int target_id = argv[1].toInt(&ok); - int forced_clients = 0; - if (!ok && argv[1] != "*") { - sendServerMessage("That does not look like a valid ID."); - return; - } - else if (ok) { - AOClient* target_client = server->getClientByID(target_id); - if (target_client != nullptr) - targets.append(target_client); - else { - sendServerMessage("Target ID not found!"); - return; - } - } - - else if (argv[1] == "*") { // force all clients in the area - for (AOClient* client : server->clients) { - if (client->current_area == current_area) - targets.append(client); - } - } - for (AOClient* target : targets) { - target->sendServerMessage("Position forcibly changed by CM."); - target->changePosition(argv[0]); - forced_clients++; - } - sendServerMessage("Forced " + QString::number(forced_clients) + " into pos " + argv[0] + "."); -} - -void AOClient::cmdG(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - QString sender_area = server->area_names.value(current_area); - QString sender_message = argv.join(" "); - for (AOClient* client : server->clients) { - if (client->global_enabled) - client->sendPacket("CT", {"[G][" + sender_area + "]" + sender_name, sender_message}); - } - return; -} - -void AOClient::cmdNeed(int argc, QStringList argv) -{ - QString sender_area = server->area_names.value(current_area); - QString sender_message = argv.join(" "); - sendServerBroadcast({"=== Advert ===\n[" + sender_area + "] needs " + sender_message+ "."}); -} - -void AOClient::cmdFlip(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - QStringList faces = {"heads","tails"}; - QString face = faces[AOClient::genRand(0,1)]; - sendServerMessage(sender_name + " flipped a coin and got " + face + "."); -} - -void AOClient::cmdRoll(int argc, QStringList argv) -{ - diceThrower(argc, argv, RollType::ROLL); -} - -void AOClient::cmdRollP(int argc, QStringList argv) -{ - diceThrower(argc, argv, RollType::ROLLP); -} - -void AOClient::cmdDoc(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - AreaData* area = server->areas[current_area]; - if (argc == 0) { - sendServerMessage("Document: " + area->document); - } - else { - area->document = argv.join(" "); - sendServerMessageArea(sender_name + " changed the document."); - } -} - -void AOClient::cmdClearDoc(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - AreaData* area = server->areas[current_area]; - area->document = "No document."; - sendServerMessageArea(sender_name + " cleared the document."); -} - -void AOClient::cmdCM(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - AreaData* area = server->areas[current_area]; - if (area->is_protected) { - sendServerMessage("This area is protected, you may not become CM."); - return; - } - else if (area->owners.isEmpty()) { // no one owns this area, and it's not protected - area->owners.append(id); - area->invited.append(id); - sendServerMessageArea(sender_name + " is now CM in this area."); - arup(ARUPType::CM, true); - } - else if (!area->owners.contains(id)) { // there is already a CM, and it isn't us - sendServerMessage("You cannot become a CM in this area."); - } - else if (argc == 1) { // we are CM, and we want to make ID argv[0] also CM - bool ok; - AOClient* owner_candidate = server->getClientByID(argv[0].toInt(&ok)); - if (!ok) { - sendServerMessage("That doesn't look like a valid ID."); - return; - } - if (owner_candidate == nullptr) { - sendServerMessage("Unable to find client with ID " + argv[0] + "."); - return; - } - area->owners.append(owner_candidate->id); - sendServerMessageArea(owner_candidate->ooc_name + " is now CM in this area."); - arup(ARUPType::CM, true); - } - else { - sendServerMessage("You are already a CM in this area."); - } -} - -void AOClient::cmdUnCM(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - int removed = area->owners.removeAll(id); - area->invited.removeAll(id); - sendServerMessage("You are no longer CM in this area."); - arup(ARUPType::CM, true); - if (area->owners.isEmpty()) { - area->invited.clear(); - if (area->locked != AreaData::FREE) { - area->locked = AreaData::FREE; - arup(ARUPType::LOCKED, true); - } - } -} - -void AOClient::cmdInvite(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - bool ok; - int invited_id = argv[0].toInt(&ok); - if (!ok) { - sendServerMessage("That does not look like a valid ID."); - return; - } - else if (server->getClientByID(invited_id) == nullptr) { - sendServerMessage("No client with that ID found."); - return; - } - else if (area->invited.contains(invited_id)) { - sendServerMessage("That ID is already on the invite list."); - return; - } - area->invited.append(invited_id); - sendServerMessage("You invited ID " + argv[0]); -} - -void AOClient::cmdUnInvite(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - bool ok; - int uninvited_id = argv[0].toInt(&ok); - if (!ok) { - sendServerMessage("That does not look like a valid ID."); - return; - } - else if (server->getClientByID(uninvited_id) == nullptr) { - sendServerMessage("No client with that ID found."); - return; - } - else if (area->owners.contains(uninvited_id)) { - sendServerMessage("You cannot uninvite a CM!"); - return; - } - else if (!area->invited.contains(uninvited_id)) { - sendServerMessage("That ID is not on the invite list."); - return; - } - area->invited.removeAll(uninvited_id); - sendServerMessage("You uninvited ID " + argv[0]); -} - -void AOClient::cmdLock(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->locked == AreaData::LockStatus::LOCKED) { - sendServerMessage("This area is already locked."); - return; - } - sendServerMessageArea("This area is now locked."); - area->locked = AreaData::LockStatus::LOCKED; - for (AOClient* client : server->clients) { - if (client->current_area == current_area && client->joined) { - area->invited.append(client->id); - } - } - arup(ARUPType::LOCKED, true); -} - -void AOClient::cmdSpectatable(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->locked == AreaData::LockStatus::SPECTATABLE) { - sendServerMessage("This area is already in spectate mode."); - return; - } - sendServerMessageArea("This area is now spectatable."); - area->locked = AreaData::LockStatus::SPECTATABLE; - for (AOClient* client : server->clients) { - if (client->current_area == current_area && client->joined) { - area->invited.append(client->id); - } - } - arup(ARUPType::LOCKED, true); -} - -void AOClient::cmdUnLock(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->locked == AreaData::LockStatus::FREE) { - sendServerMessage("This area is not locked."); - return; - } - sendServerMessageArea("This area is now unlocked."); - area->locked = AreaData::LockStatus::FREE; - arup(ARUPType::LOCKED, true); -} - -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:"); - 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) { - sendServerMessage("Invalid timer ID. Timer ID must be a whole number between 0 and 4."); - return; - } - - // Called with one argument - // Shows the status of one timer - if (argc == 1) { - 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"))) { - sendServerMessage("You are not authorized to alter the global timer."); - return; - } - requested_timer = server->timer; - } - 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] + "."); - 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) + "."); - 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) + "."); - 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) + "."); - // Hide the timer - is_global ? server->broadcast(hide_timer) : server->broadcast(hide_timer, current_area); - } - } -} - -void AOClient::cmdEvidenceMod(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - argv[0] = argv[0].toLower(); - if (argv[0] == "cm") - area->evi_mod = AreaData::EvidenceMod::CM; - else if (argv[0] == "mod") - area->evi_mod = AreaData::EvidenceMod::MOD; - else if (argv[0] == "hiddencm") - area->evi_mod = AreaData::EvidenceMod::HIDDEN_CM; - else if (argv[0] == "ffa") - area->evi_mod = AreaData::EvidenceMod::FFA; - else { - sendServerMessage("Invalid evidence mod."); - return; - } - sendServerMessage("Changed evidence mod."); - - // Resend evidence lists to everyone in the area - sendEvidenceList(area); -} - -void AOClient::cmdArea(int argc, QStringList argv) -{ - bool ok; - int new_area = argv[0].toInt(&ok); - if (!ok || new_area >= server->areas.size() || new_area < 0) { - sendServerMessage("That does not look like a valid area ID."); - return; - } - changeArea(new_area); -} - -void AOClient::cmdPlay(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - QString song = argv.join(" "); - area->current_music = song; - area->music_played_by = showname; - AOPacket music_change("MC", {song, QString::number(server->getCharID(current_char)), showname, "1", "0"}); - server->broadcast(music_change, current_area); -} - -void AOClient::cmdAreaKick(int argc, QStringList argv) -{ - bool ok; - int idx = argv[0].toInt(&ok); - if (!ok) { - sendServerMessage("That does not look like a valid ID."); - return; - } - AOClient* client_to_kick = server->getClientByID(idx); - if (client_to_kick == nullptr) { - sendServerMessage("No client with that ID found."); - return; - } - client_to_kick->changeArea(0); - sendServerMessage("Client " + argv[0] + " kicked back to area 0."); -} - -void AOClient::cmdSwitch(int argc, QStringList argv) -{ - int char_id = server->getCharID(argv.join(" ")); - if (char_id == -1) { - sendServerMessage("That does not look like a valid character."); - return; - } - changeCharacter(char_id); -} - -void AOClient::cmdRandomChar(int argc, QStringList argv) -{ - int char_id = genRand(0, server->characters.size() - 1); - changeCharacter(char_id); -} - -void AOClient::cmdToggleGlobal(int argc, QStringList argv) -{ - global_enabled = !global_enabled; - QString str_en = global_enabled ? "shown" : "hidden"; - sendServerMessage("Global chat set to " + str_en); -} - -void AOClient::cmdMods(int argc, QStringList argv) -{ - QStringList entries; - int online_count = 0; - for (AOClient* client : server->clients) { - if (client->authenticated) { - entries << "---"; - if (server->auth_type != "simple") - entries << "Moderator: " + client->moderator_name; - entries << "OOC name: " + client->ooc_name; - entries << "ID: " + QString::number(client->id); - entries << "Area: " + QString::number(client->current_area); - entries << "Character: " + client->current_char; - online_count++; - } - } - entries << "---"; - entries << "Total online: " << QString::number(online_count); - sendServerMessage(entries.join("\n")); -} - -void AOClient::cmdHelp(int argc, QStringList argv) -{ - QStringList entries; - entries << "Allowed commands:"; - QMap::const_iterator i; - for (i = commands.constBegin(); i!= commands.constEnd(); ++i) { - CommandInfo info = i.value(); - if (checkAuth(info.acl_mask)) { // if we are allowed to use this command - entries << "/" + i.key(); - } - } - sendServerMessage(entries.join("\n")); -} - -void AOClient::cmdStatus(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - QString arg = argv[0].toLower(); - if (arg == "idle") - area->status = AreaData::IDLE; - else if (arg == "rp") - area->status = AreaData::RP; - else if (arg == "casing") - area->status = AreaData::CASING; - else if (arg == "looking-for-players" || arg == "lfp") - area->status = AreaData::LOOKING_FOR_PLAYERS; - else if (arg == "recess") - area->status = AreaData::RECESS; - else if (arg == "gaming") - area->status = AreaData::GAMING; - else { - sendServerMessage("That does not look like a valid status. Valid statuses are idle, rp, casing, lfp, recess, gaming"); - return; - } - arup(ARUPType::STATUS, true); -} - -void AOClient::cmdCurrentMusic(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->current_music != "" && area->current_music != "~stop.mp3") // dummy track for stopping music - sendServerMessage("The current song is " + area->current_music + " played by " + area->music_played_by); - else - sendServerMessage("There is no music playing."); -} - -void AOClient::cmdPM(int arc, QStringList argv) -{ - bool ok; - int target_id = argv.takeFirst().toInt(&ok); // using takeFirst removes the ID from our list of arguments... - if (!ok) { - sendServerMessage("That does not look like a valid ID."); - return; - } - AOClient* target_client = server->getClientByID(target_id); - if (target_client == nullptr) { - sendServerMessage("No client with that ID found."); - return; - } - QString message = argv.join(" "); //...which means it will not end up as part of the message - target_client->sendServerMessage("Message from " + ooc_name + " (" + QString::number(id) + "): " + message); -} - -void AOClient::cmdMOTD(int argc, QStringList argv) -{ - if (argc == 0) { - sendServerMessage("=== MOTD ===\r\n" + server->MOTD + "\r\n============="); - } - else if (argc > 0) { - if (checkAuth(ACLFlags.value("MOTD"))) { - QString MOTD = argv.join(" "); - server->MOTD = MOTD; - sendServerMessage("MOTD has been changed."); - } - else { - sendServerMessage("You do not have permission to change the MOTD"); - } - } -} - -void AOClient::cmdAnnounce(int argc, QStringList argv) -{ - sendServerBroadcast("=== Announcement ===\r\n" + argv.join(" ") + "\r\n============="); -} - -void AOClient::cmdM(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - QString sender_message = argv.join(" "); - for (AOClient* client : server->clients) { - if (client->checkAuth(ACLFlags.value("MODCHAT"))) - client->sendPacket("CT", {"[M]" + sender_name, sender_message}); - } - return; -} - -void AOClient::cmdGM(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - QString sender_area = server->area_names.value(current_area); - QString sender_message = argv.join(" "); - for (AOClient* client : server->clients) { - if (client->global_enabled) { - client->sendPacket("CT", {"[G][" + sender_area + "]" + "["+sender_name+"][M]", sender_message}); - } - } -} - -void AOClient::cmdLM(int argc, QStringList argv) -{ - QString sender_name = ooc_name; - QString sender_message = argv.join(" "); - server->broadcast(AOPacket("CT", {"["+sender_name+"][M]", sender_message}), current_area); -} - -void AOClient::cmdBans(int argc, QStringList argv) -{ - QStringList recent_bans; - recent_bans << "Last 5 bans:"; - recent_bans << "-----"; - for (DBManager::BanInfo ban : server->db_manager->getRecentBans()) { - 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"); - recent_bans << "Affected IPID: " + ban.ipid; - recent_bans << "Affected HDID: " + ban.hdid; - recent_bans << "Reason for ban: " + ban.reason; - recent_bans << "Date of ban: " + QDateTime::fromSecsSinceEpoch(ban.time).toString("dd.MM.yyyy, hh:mm"); - recent_bans << "Ban lasts until: " + banned_until; - recent_bans << "-----"; - } - sendServerMessage(recent_bans.join("\n")); -} - -void AOClient::cmdUnBan(int argc, QStringList argv) -{ - bool ok; - int target_ban = argv[0].toInt(&ok); - if (!ok) { - sendServerMessage("Invalid ban ID."); - return; - } - else if (server->db_manager->invalidateBan(target_ban)) - sendServerMessage("Successfully invalidated ban " + argv[0] + "."); - else - sendServerMessage("Couldn't invalidate ban " + argv[0] + ", are you sure it exists?"); -} - -void AOClient::cmdSubTheme(int argc, QStringList argv) -{ - QString subtheme = argv.join(" "); - for (AOClient* client : server->clients) { - if (client->current_area == current_area) - client->sendPacket("ST", {subtheme, "1"}); - } - sendServerMessageArea("Subtheme was set to " + subtheme); -} - -void AOClient::cmdAbout(int argc, QStringList argv) -{ - sendPacket("CT", {"The akashi dev team", "Thank you for using akashi! Made with love by scatterflower, with help from in1tiate and Salanto. akashi " + QCoreApplication::applicationVersion()}); -} - -void AOClient::cmdEvidence_Swap(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - int ev_size = area->evidence.size() -1; - - if (ev_size < 0) { - sendServerMessage("No evidence in area."); - return; - } - - bool ok, ok2; - int ev_id1 = argv[0].toInt(&ok), ev_id2 = argv[1].toInt(&ok2); - - if ((!ok || !ok2)) { - sendServerMessage("Invalid evidence ID."); - return; - } - if ((ev_id1 < 0) || (ev_id2 < 0)) { - sendServerMessage("Evidence ID can't be negative."); - return; - } - if ((ev_id2 <= ev_size) && (ev_id1 <= ev_size)) { -#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) - //swapItemsAt does not exist in Qt older than 5.13 - area->evidence.swap(ev_id1, ev_id2); -#else - area->evidence.swapItemsAt(ev_id1, ev_id2); -#endif - sendEvidenceList(area); - sendServerMessage("The evidence " + QString::number(ev_id1) + " and " + QString::number(ev_id2) + " have been swapped."); - } - else { - sendServerMessage("Unable to swap evidence. Evidence ID out of range."); - } -} - -void AOClient::cmdMute(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_muted) - sendServerMessage("That player is already muted!"); - else { - sendServerMessage("Muted player."); - target->sendServerMessage("You were muted by a moderator. " + getReprimand()); - } - target->is_muted = true; -} - -void AOClient::cmdUnMute(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!target->is_muted) - sendServerMessage("That player is not muted!"); - else { - sendServerMessage("Unmuted player."); - target->sendServerMessage("You were unmuted by a moderator. " + getReprimand(true)); - } - target->is_muted = false; -} - -void AOClient::cmdOocMute(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_ooc_muted) - sendServerMessage("That player is already OOC muted!"); - else { - sendServerMessage("OOC muted player."); - target->sendServerMessage("You were OOC muted by a moderator. " + getReprimand()); - } - target->is_ooc_muted = true; -} - -void AOClient::cmdOocUnMute(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!target->is_ooc_muted) - sendServerMessage("That player is not OOC muted!"); - else { - sendServerMessage("OOC unmuted player."); - target->sendServerMessage("You were OOC unmuted by a moderator. " + getReprimand(true)); - } - target->is_ooc_muted = false; -} - -void AOClient::cmdBlockDj(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_dj_blocked) - sendServerMessage("That player is already DJ blocked!"); - else { - sendServerMessage("DJ blocked player."); - target->sendServerMessage("You were blocked from changing the music by a moderator. " + getReprimand()); - } - target->is_dj_blocked = true; -} - -void AOClient::cmdUnBlockDj(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!target->is_dj_blocked) - sendServerMessage("That player is not DJ blocked!"); - else { - sendServerMessage("DJ permissions restored to player."); - target->sendServerMessage("A moderator restored your music permissions. " + getReprimand(true)); - } - target->is_dj_blocked = false; -} - -void AOClient::cmdBlockWtce(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_wtce_blocked) - sendServerMessage("That player is already judge blocked!"); - else { - sendServerMessage("Revoked player's access to judge controls."); - target->sendServerMessage("A moderator revoked your judge controls access. " + getReprimand()); - } - target->is_wtce_blocked = true; -} - -void AOClient::cmdUnBlockWtce(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!target->is_wtce_blocked) - sendServerMessage("That player is not judge blocked!"); - else { - sendServerMessage("Restored player's access to judge controls."); - target->sendServerMessage("A moderator restored your judge controls access. " + getReprimand(true)); - } - target->is_wtce_blocked = false; -} - -void AOClient::cmdNoteCard(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->notecards.keys().contains(current_char)) - area->notecards.remove(current_char); - QString notecard = argv.join(" "); - area->notecards[current_char] = notecard; - sendServerMessageArea(current_char + " wrote a note card."); -} - -void AOClient::cmdNoteCardClear(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->notecards.keys().contains(current_char)) { - area->notecards.remove(current_char); - sendServerMessageArea(current_char + " erased their note card."); - } - else - sendServerMessage("You do not have a note card."); -} - -void AOClient::cmdNoteCardReveal(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->notecards.isEmpty()) { - sendServerMessage("There are no cards to reveal in this area."); - return; - } - QStringList message; - message << "Note cards have been revealed."; - QMap::iterator i; - for (i = area->notecards.begin(); i != area->notecards.end(); ++i) - message << i.key() + ": " + i.value(); - sendServerMessageArea(message.join("\n")); - area->notecards.clear(); -} - -void AOClient::cmd8Ball(int argc, QStringList argv) -{ - if (server->magic_8ball_answers.isEmpty()) { - qWarning() << "8ball.txt is empty!"; - sendServerMessage("8ball.txt is empty."); - } - else { - QString response = server->magic_8ball_answers[(genRand(1, server->magic_8ball_answers.size() - 1))]; - QString sender_name = ooc_name; - QString sender_message = argv.join(" "); - - sendServerMessageArea(sender_name + " asked the magic 8-ball, \"" + sender_message + "\" and the answer is: " + response); - } -} - -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; - AreaData* area = server->areas[current_area]; - area->blankposting_allowed = !area->blankposting_allowed; - if (area->blankposting_allowed == false) { - sendServerMessageArea(sender_name + " has set blankposting in the area to forbidden."); - } - else { - sendServerMessageArea(sender_name + " has set blankposting in the area to allowed."); - } -} - -void AOClient::cmdGimp(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_gimped) - sendServerMessage("That player is already gimped!"); - else { - sendServerMessage("Gimped player."); - target->sendServerMessage("You have been gimped! " + getReprimand()); - } - target->is_gimped = true; -} - -void AOClient::cmdUnGimp(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!(target->is_gimped)) - sendServerMessage("That player is not gimped!"); - else { - sendServerMessage("Ungimped player."); - target->sendServerMessage("A moderator has ungimped you! " + getReprimand(true)); - } - 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::cmdTestify(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->test_rec == AreaData::TestimonyRecording::RECORDING) { - sendServerMessage("Testimony recording is already in progress. Please stop it before starting a new one."); - } - else { - clearTestimony(); - area->statement = 0; - area->test_rec = AreaData::TestimonyRecording::RECORDING; - sendServerMessage("Started testimony recording."); - } -} - -void AOClient::cmdExamine(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - if (area->testimony.size() -1 > 0) - { - area->test_rec = AreaData::TestimonyRecording::PLAYBACK; - server->broadcast(AOPacket("RT",{"testimony2"}), current_area); - server->broadcast(AOPacket("MS", {area->testimony[0]}), current_area); - area->statement = 0; - return; - } - if (area->test_rec == AreaData::TestimonyRecording::PLAYBACK) - sendServerMessage("Unable to examine while another examination is running"); - else - sendServerMessage("Unable to start replay without prior examination."); -} - -void AOClient::cmdDeleteStatement(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - int c_statement = area->statement; - if (area->testimony.size() - 1 == 0) { - sendServerMessage("Unable to delete statement. No statements saved in this area."); - } - if (c_statement > 0 && area->testimony.size() > 2) { - area->testimony.remove(c_statement); - sendServerMessage("The statement with id " + QString::number(c_statement) + " has been deleted from the testimony."); - } -} - -void AOClient::cmdUpdateStatement(int argc, QStringList argv) -{ - server->areas[current_area]->test_rec = AreaData::TestimonyRecording::UPDATE; - sendServerMessage("The next IC-Message will replace the last displayed replay message."); -} - -void AOClient::cmdPauseTestimony(int argc, QStringList argv) -{ - AreaData* area = server->areas[current_area]; - area->test_rec = AreaData::TestimonyRecording::STOPPED; - sendServerMessage("Testimony has been stopped."); -} - -void AOClient::cmdAddStatement(int argc, QStringList argv) -{ - if (server->areas[current_area]->statement < server->maximum_statements) { - server->areas[current_area]->test_rec = AreaData::TestimonyRecording::ADD; - sendServerMessage("The next IC-Message will be inserted into the testimony."); - } - else - sendServerMessage("Unable to add anymore statements. Please remove any unused ones."); -} - -void AOClient::cmdReload(int argc, QStringList argv) -{ - server->loadServerConfig(); - server->loadCommandConfig(); - emit server->reloadRequest(server->server_name, server->server_desc); - sendServerMessage("Reloaded configurations"); -} - -void AOClient::cmdDisemvowel(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_disemvoweled) - sendServerMessage("That player is already disemvoweled!"); - else { - sendServerMessage("Disemvoweled player."); - target->sendServerMessage("You have been disemvoweled! " + getReprimand()); - } - target->is_disemvoweled = true; -} - -void AOClient::cmdUnDisemvowel(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!(target->is_disemvoweled)) - sendServerMessage("That player is not disemvoweled!"); - else { - sendServerMessage("Undisemvoweled player."); - target->sendServerMessage("A moderator has undisemvoweled you! " + getReprimand(true)); - } - target->is_disemvoweled = false; -} - -void AOClient::cmdShake(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (target->is_shaken) - sendServerMessage("That player is already shaken!"); - else { - sendServerMessage("Shook player."); - target->sendServerMessage("A moderator has shaken your words! " + getReprimand()); - } - target->is_shaken = true; -} - -void AOClient::cmdUnShake(int argc, QStringList argv) -{ - bool conv_ok = false; - int uid = argv[0].toInt(&conv_ok); - if (!conv_ok) { - sendServerMessage("Invalid user ID."); - return; - } - - AOClient* target = server->getClientByID(uid); - - if (!(target->is_shaken)) - sendServerMessage("That player is not shaken!"); - else { - sendServerMessage("Unshook player."); - target->sendServerMessage("A moderator has unshook you! " + getReprimand(true)); - } - target->is_shaken = false; -} - -QStringList AOClient::buildAreaList(int area_idx) -{ - QStringList entries; - QString area_name = server->area_names[area_idx]; - AreaData* area = server->areas[area_idx]; - entries.append("=== " + area_name + " ==="); - switch (area->locked) { - case AreaData::LockStatus::LOCKED: - entries.append("[LOCKED]"); - break; - case AreaData::LockStatus::SPECTATABLE: - entries.append("[SPECTATABLE]"); - break; - case AreaData::LockStatus::FREE: - default: - break; - } - entries.append("[" + QString::number(area->player_count) + " users][" + QVariant::fromValue(area->status).toString().replace("_", "-") + "]"); - for (AOClient* client : server->clients) { - if (client->current_area == area_idx && client->joined) { - QString char_entry = "[" + QString::number(client->id) + "] " + client->current_char; - if (client->current_char == "") - char_entry += "Spectator"; - if (area->owners.contains(client->id)) - char_entry.insert(0, "[CM] "); - if (authenticated) - char_entry += " (" + client->getIpid() + "): " + client->ooc_name; - entries.append(char_entry); - } - } - return entries; -} - -int AOClient::genRand(int min, int max) -{ -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - qsrand(QDateTime::currentMSecsSinceEpoch()); - quint32 random_number = (qrand() % (max - min + 1)) + min; - return random_number; - -#else - quint32 random_number = QRandomGenerator::system()->bounded(min, max + 1); - return random_number; -#endif -} - -void AOClient::diceThrower(int argc, QStringList argv, RollType type) -{ - QString sender_name = ooc_name; - int max_value = server->dice_value; - int max_dice = server->max_dice; - int bounded_value; - int bounded_amount; - QString dice_results; - - if (argc == 0) { - dice_results = QString::number(genRand(1, 6)); // Self-explanatory - } - else if (argc == 1) { - bounded_value = qBound(1, argv[0].toInt(), max_value); // faces, max faces - dice_results = QString::number(genRand(1, bounded_value)); - } - else if (argc == 2) { - bounded_value = qBound(1, argv[0].toInt(), max_value); // 1, faces, max faces - bounded_amount = qBound(1, argv[1].toInt(), max_dice); // 1, amount, max amount - - for (int i = 1; i <= bounded_amount ; i++) // Loop as multiple dices are thrown - { - QString dice_result = QString::number(genRand(1, bounded_value)); - if (i == bounded_amount) { - dice_results = dice_results.append(dice_result); - } - else { - dice_results = dice_results.append(dice_result + ","); - } - } - } - // Switch to change message behaviour, isEmpty check or the entire server crashes due to an out of range issue in the QStringList - switch(type) - { - case ROLL: - if (argv.isEmpty()) { - sendServerMessageArea(sender_name + " rolled " + dice_results + " out of 6"); - } - else { - sendServerMessageArea(sender_name + " rolled " + dice_results + " out of " + QString::number(bounded_value)); - } - break; - case ROLLP: - if (argv.isEmpty()) { - sendServerMessage(sender_name + " rolled " + dice_results + " out of 6"); - sendServerMessageArea((sender_name + " rolled in secret.")); - } - else { - sendServerMessageArea(sender_name + " rolled " + dice_results + " out of " + QString::number(bounded_value)); - sendServerMessageArea((sender_name + " rolled in secret.")); - } - break; - case ROLLA: - //Not implemented yet - default : break; - } -} - -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_name + " is at " + current_time.toString("hh:mm:ss.zzz"); - } - else { - return timer_name + " is inactive."; - } -} - -long long AOClient::parseTime(QString input) -{ - QRegularExpression regex("(?:(?:(?.*?)y)*(?:(?.*?)w)*(?:(?.*?)d)*(?:(?
.*?)h)*(?:(?.*?)m)*(?:(?.*?)s)*)"); - QRegularExpressionMatch match = regex.match(input); - QString str_year, str_week, str_hour, str_day, str_minute, str_second; - int year, week, day, hour, minute, second; - - str_year = match.captured("year"); - str_week = match.captured("week"); - str_day = match.captured("day"); - str_hour = match.captured("hr"); - str_minute = match.captured("min"); - str_second = match.captured("sec"); - - bool is_well_formed = false; - QString concat_str(str_year + str_week + str_day + str_hour + str_minute + str_second); - concat_str.toInt(&is_well_formed); - - if (!is_well_formed) { - return -1; - } - - year = str_year.toInt(); - week = str_week.toInt(); - day = str_day.toInt(); - hour = str_hour.toInt(); - minute = str_minute.toInt(); - second = str_second.toInt(); - - long long total = 0; - total += 31622400 * year; - total += 604800 * week; - total += 86400 * day; - total += 3600 * hour; - total += 60 * minute; - total += second; - - if (total < 0) - return -1; - - return total; -} - -QString AOClient::getReprimand(bool positive) -{ - if (positive) { - return server->praise_list[genRand(0, server->praise_list.size() - 1)]; - } - else { - return server->reprimands_list[genRand(0, server->reprimands_list.size() - 1)]; - } -} diff --git a/src/commands/area.cpp b/src/commands/area.cpp new file mode 100644 index 0000000..d471ef6 --- /dev/null +++ b/src/commands/area.cpp @@ -0,0 +1,285 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for commands under the area category in aoclient.h +// Be sure to register the command in the header before adding it here! + + +void AOClient::cmdCM(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + AreaData* area = server->areas[current_area]; + if (area->is_protected) { + sendServerMessage("This area is protected, you may not become CM."); + return; + } + else if (area->owners.isEmpty()) { // no one owns this area, and it's not protected + area->owners.append(id); + area->invited.append(id); + sendServerMessageArea(sender_name + " is now CM in this area."); + arup(ARUPType::CM, true); + } + else if (!area->owners.contains(id)) { // there is already a CM, and it isn't us + sendServerMessage("You cannot become a CM in this area."); + } + else if (argc == 1) { // we are CM, and we want to make ID argv[0] also CM + bool ok; + AOClient* owner_candidate = server->getClientByID(argv[0].toInt(&ok)); + if (!ok) { + sendServerMessage("That doesn't look like a valid ID."); + return; + } + if (owner_candidate == nullptr) { + sendServerMessage("Unable to find client with ID " + argv[0] + "."); + return; + } + area->owners.append(owner_candidate->id); + sendServerMessageArea(owner_candidate->ooc_name + " is now CM in this area."); + arup(ARUPType::CM, true); + } + else { + sendServerMessage("You are already a CM in this area."); + } +} + +void AOClient::cmdUnCM(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + int removed = area->owners.removeAll(id); + area->invited.removeAll(id); + sendServerMessage("You are no longer CM in this area."); + arup(ARUPType::CM, true); + if (area->owners.isEmpty()) { + area->invited.clear(); + if (area->locked != AreaData::FREE) { + area->locked = AreaData::FREE; + arup(ARUPType::LOCKED, true); + } + } +} + +void AOClient::cmdInvite(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + bool ok; + int invited_id = argv[0].toInt(&ok); + if (!ok) { + sendServerMessage("That does not look like a valid ID."); + return; + } + else if (server->getClientByID(invited_id) == nullptr) { + sendServerMessage("No client with that ID found."); + return; + } + else if (area->invited.contains(invited_id)) { + sendServerMessage("That ID is already on the invite list."); + return; + } + area->invited.append(invited_id); + sendServerMessage("You invited ID " + argv[0]); +} + +void AOClient::cmdUnInvite(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + bool ok; + int uninvited_id = argv[0].toInt(&ok); + if (!ok) { + sendServerMessage("That does not look like a valid ID."); + return; + } + else if (server->getClientByID(uninvited_id) == nullptr) { + sendServerMessage("No client with that ID found."); + return; + } + else if (area->owners.contains(uninvited_id)) { + sendServerMessage("You cannot uninvite a CM!"); + return; + } + else if (!area->invited.contains(uninvited_id)) { + sendServerMessage("That ID is not on the invite list."); + return; + } + area->invited.removeAll(uninvited_id); + sendServerMessage("You uninvited ID " + argv[0]); +} + +void AOClient::cmdLock(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->locked == AreaData::LockStatus::LOCKED) { + sendServerMessage("This area is already locked."); + return; + } + sendServerMessageArea("This area is now locked."); + area->locked = AreaData::LockStatus::LOCKED; + for (AOClient* client : server->clients) { + if (client->current_area == current_area && client->joined) { + area->invited.append(client->id); + } + } + arup(ARUPType::LOCKED, true); +} + +void AOClient::cmdSpectatable(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->locked == AreaData::LockStatus::SPECTATABLE) { + sendServerMessage("This area is already in spectate mode."); + return; + } + sendServerMessageArea("This area is now spectatable."); + area->locked = AreaData::LockStatus::SPECTATABLE; + for (AOClient* client : server->clients) { + if (client->current_area == current_area && client->joined) { + area->invited.append(client->id); + } + } + arup(ARUPType::LOCKED, true); +} + +void AOClient::cmdUnLock(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->locked == AreaData::LockStatus::FREE) { + sendServerMessage("This area is not locked."); + return; + } + sendServerMessageArea("This area is now unlocked."); + area->locked = AreaData::LockStatus::FREE; + arup(ARUPType::LOCKED, true); +} + +void AOClient::cmdGetAreas(int argc, QStringList argv) +{ + QStringList entries; + entries.append("== Area List =="); + for (int i = 0; i < server->area_names.length(); i++) { + QStringList cur_area_lines = buildAreaList(i); + entries.append(cur_area_lines); + } + sendServerMessage(entries.join("\n")); +} + +void AOClient::cmdGetArea(int argc, QStringList argv) +{ + QStringList entries = buildAreaList(current_area); + sendServerMessage(entries.join("\n")); +} + +void AOClient::cmdArea(int argc, QStringList argv) +{ + bool ok; + int new_area = argv[0].toInt(&ok); + if (!ok || new_area >= server->areas.size() || new_area < 0) { + sendServerMessage("That does not look like a valid area ID."); + return; + } + changeArea(new_area); +} + +void AOClient::cmdAreaKick(int argc, QStringList argv) +{ + bool ok; + int idx = argv[0].toInt(&ok); + if (!ok) { + sendServerMessage("That does not look like a valid ID."); + return; + } + AOClient* client_to_kick = server->getClientByID(idx); + if (client_to_kick == nullptr) { + sendServerMessage("No client with that ID found."); + return; + } + client_to_kick->changeArea(0); + sendServerMessage("Client " + argv[0] + " kicked back to area 0."); +} + +void AOClient::cmdSetBackground(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (authenticated || !area->bg_locked) { + if (server->backgrounds.contains(argv[0])) { + area->background = argv[0]; + server->broadcast(AOPacket("BN", {argv[0]}), current_area); + sendServerMessageArea(current_char + " changed the background to " + argv[0]); + } + else { + sendServerMessage("Invalid background name."); + } + } + else { + sendServerMessage("This area's background is locked."); + } +} + +void AOClient::cmdBgLock(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + area->bg_locked = true; + server->broadcast(AOPacket("CT", {"Server", current_char + " locked the background.", "1"}), current_area); +} + +void AOClient::cmdBgUnlock(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + area->bg_locked = false; + server->broadcast(AOPacket("CT", {"Server", current_char + " unlocked the background.", "1"}), current_area); +} + +void AOClient::cmdStatus(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + QString arg = argv[0].toLower(); + if (arg == "idle") + area->status = AreaData::IDLE; + else if (arg == "rp") + area->status = AreaData::RP; + else if (arg == "casing") + area->status = AreaData::CASING; + else if (arg == "looking-for-players" || arg == "lfp") + area->status = AreaData::LOOKING_FOR_PLAYERS; + else if (arg == "recess") + area->status = AreaData::RECESS; + else if (arg == "gaming") + area->status = AreaData::GAMING; + else { + sendServerMessage("That does not look like a valid status. Valid statuses are idle, rp, casing, lfp, recess, gaming"); + return; + } + arup(ARUPType::STATUS, true); +} + +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); + } +} diff --git a/src/commands/authentication.cpp b/src/commands/authentication.cpp index fa0dd4d..1aeb24d 100644 --- a/src/commands/authentication.cpp +++ b/src/commands/authentication.cpp @@ -17,6 +17,7 @@ ////////////////////////////////////////////////////////////////////////////////////// #include "include/aoclient.h" +// This file is for commands under the authentication category in aoclient.h // Be sure to register the command in the header before adding it here! void AOClient::cmdLogin(int argc, QStringList argv) diff --git a/src/commands/casing.cpp b/src/commands/casing.cpp new file mode 100644 index 0000000..db4ce40 --- /dev/null +++ b/src/commands/casing.cpp @@ -0,0 +1,167 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for commands under the casing category in aoclient.h +// Be sure to register the command in the header before adding it here! + +void AOClient::cmdDoc(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + AreaData* area = server->areas[current_area]; + if (argc == 0) { + sendServerMessage("Document: " + area->document); + } + else { + area->document = argv.join(" "); + sendServerMessageArea(sender_name + " changed the document."); + } +} + +void AOClient::cmdClearDoc(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + AreaData* area = server->areas[current_area]; + area->document = "No document."; + sendServerMessageArea(sender_name + " cleared the document."); +} + +void AOClient::cmdEvidenceMod(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + argv[0] = argv[0].toLower(); + if (argv[0] == "cm") + area->evi_mod = AreaData::EvidenceMod::CM; + else if (argv[0] == "mod") + area->evi_mod = AreaData::EvidenceMod::MOD; + else if (argv[0] == "hiddencm") + area->evi_mod = AreaData::EvidenceMod::HIDDEN_CM; + else if (argv[0] == "ffa") + area->evi_mod = AreaData::EvidenceMod::FFA; + else { + sendServerMessage("Invalid evidence mod."); + return; + } + sendServerMessage("Changed evidence mod."); + + // Resend evidence lists to everyone in the area + sendEvidenceList(area); +} + +void AOClient::cmdEvidence_Swap(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + int ev_size = area->evidence.size() -1; + + if (ev_size < 0) { + sendServerMessage("No evidence in area."); + return; + } + + bool ok, ok2; + int ev_id1 = argv[0].toInt(&ok), ev_id2 = argv[1].toInt(&ok2); + + if ((!ok || !ok2)) { + sendServerMessage("Invalid evidence ID."); + return; + } + if ((ev_id1 < 0) || (ev_id2 < 0)) { + sendServerMessage("Evidence ID can't be negative."); + return; + } + if ((ev_id2 <= ev_size) && (ev_id1 <= ev_size)) { +#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) + //swapItemsAt does not exist in Qt older than 5.13 + area->evidence.swap(ev_id1, ev_id2); +#else + area->evidence.swapItemsAt(ev_id1, ev_id2); +#endif + sendEvidenceList(area); + sendServerMessage("The evidence " + QString::number(ev_id1) + " and " + QString::number(ev_id2) + " have been swapped."); + } + else { + sendServerMessage("Unable to swap evidence. Evidence ID out of range."); + } +} + +void AOClient::cmdTestify(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->test_rec == AreaData::TestimonyRecording::RECORDING) { + sendServerMessage("Testimony recording is already in progress. Please stop it before starting a new one."); + } + else { + clearTestimony(); + area->statement = 0; + area->test_rec = AreaData::TestimonyRecording::RECORDING; + sendServerMessage("Started testimony recording."); + } +} + +void AOClient::cmdExamine(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->testimony.size() -1 > 0) + { + area->test_rec = AreaData::TestimonyRecording::PLAYBACK; + server->broadcast(AOPacket("RT",{"testimony2"}), current_area); + server->broadcast(AOPacket("MS", {area->testimony[0]}), current_area); + area->statement = 0; + return; + } + if (area->test_rec == AreaData::TestimonyRecording::PLAYBACK) + sendServerMessage("Unable to examine while another examination is running"); + else + sendServerMessage("Unable to start replay without prior examination."); +} + +void AOClient::cmdDeleteStatement(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + int c_statement = area->statement; + if (area->testimony.size() - 1 == 0) { + sendServerMessage("Unable to delete statement. No statements saved in this area."); + } + if (c_statement > 0 && area->testimony.size() > 2) { + area->testimony.remove(c_statement); + sendServerMessage("The statement with id " + QString::number(c_statement) + " has been deleted from the testimony."); + } +} + +void AOClient::cmdUpdateStatement(int argc, QStringList argv) +{ + server->areas[current_area]->test_rec = AreaData::TestimonyRecording::UPDATE; + sendServerMessage("The next IC-Message will replace the last displayed replay message."); +} + +void AOClient::cmdPauseTestimony(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + area->test_rec = AreaData::TestimonyRecording::STOPPED; + sendServerMessage("Testimony has been stopped."); +} + +void AOClient::cmdAddStatement(int argc, QStringList argv) +{ + if (server->areas[current_area]->statement < server->maximum_statements) { + server->areas[current_area]->test_rec = AreaData::TestimonyRecording::ADD; + sendServerMessage("The next IC-Message will be inserted into the testimony."); + } + else + sendServerMessage("Unable to add anymore statements. Please remove any unused ones."); +} diff --git a/src/commands/command_helper.cpp b/src/commands/command_helper.cpp new file mode 100644 index 0000000..908f82b --- /dev/null +++ b/src/commands/command_helper.cpp @@ -0,0 +1,207 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for functions used by various commands, defined in the command helper function category in aoclient.h +// Be sure to register the command in the header before adding it here! + +void AOClient::cmdDefault(int argc, QStringList argv) +{ + sendServerMessage("Invalid command."); + return; +} + +QStringList AOClient::buildAreaList(int area_idx) +{ + QStringList entries; + QString area_name = server->area_names[area_idx]; + AreaData* area = server->areas[area_idx]; + entries.append("=== " + area_name + " ==="); + switch (area->locked) { + case AreaData::LockStatus::LOCKED: + entries.append("[LOCKED]"); + break; + case AreaData::LockStatus::SPECTATABLE: + entries.append("[SPECTATABLE]"); + break; + case AreaData::LockStatus::FREE: + default: + break; + } + entries.append("[" + QString::number(area->player_count) + " users][" + QVariant::fromValue(area->status).toString().replace("_", "-") + "]"); + for (AOClient* client : server->clients) { + if (client->current_area == area_idx && client->joined) { + QString char_entry = "[" + QString::number(client->id) + "] " + client->current_char; + if (client->current_char == "") + char_entry += "Spectator"; + if (area->owners.contains(client->id)) + char_entry.insert(0, "[CM] "); + if (authenticated) + char_entry += " (" + client->getIpid() + "): " + client->ooc_name; + entries.append(char_entry); + } + } + return entries; +} + +int AOClient::genRand(int min, int max) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + qsrand(QDateTime::currentMSecsSinceEpoch()); + quint32 random_number = (qrand() % (max - min + 1)) + min; + return random_number; + +#else + quint32 random_number = QRandomGenerator::system()->bounded(min, max + 1); + return random_number; +#endif +} + +void AOClient::diceThrower(int argc, QStringList argv, RollType type) +{ + QString sender_name = ooc_name; + int max_value = server->dice_value; + int max_dice = server->max_dice; + int bounded_value; + int bounded_amount; + QString dice_results; + + if (argc == 0) { + dice_results = QString::number(genRand(1, 6)); // Self-explanatory + } + else if (argc == 1) { + bounded_value = qBound(1, argv[0].toInt(), max_value); // faces, max faces + dice_results = QString::number(genRand(1, bounded_value)); + } + else if (argc == 2) { + bounded_value = qBound(1, argv[0].toInt(), max_value); // 1, faces, max faces + bounded_amount = qBound(1, argv[1].toInt(), max_dice); // 1, amount, max amount + + for (int i = 1; i <= bounded_amount ; i++) // Loop as multiple dices are thrown + { + QString dice_result = QString::number(genRand(1, bounded_value)); + if (i == bounded_amount) { + dice_results = dice_results.append(dice_result); + } + else { + dice_results = dice_results.append(dice_result + ","); + } + } + } + // Switch to change message behaviour, isEmpty check or the entire server crashes due to an out of range issue in the QStringList + switch(type) + { + case ROLL: + if (argv.isEmpty()) { + sendServerMessageArea(sender_name + " rolled " + dice_results + " out of 6"); + } + else { + sendServerMessageArea(sender_name + " rolled " + dice_results + " out of " + QString::number(bounded_value)); + } + break; + case ROLLP: + if (argv.isEmpty()) { + sendServerMessage(sender_name + " rolled " + dice_results + " out of 6"); + sendServerMessageArea((sender_name + " rolled in secret.")); + } + else { + sendServerMessageArea(sender_name + " rolled " + dice_results + " out of " + QString::number(bounded_value)); + sendServerMessageArea((sender_name + " rolled in secret.")); + } + break; + case ROLLA: + //Not implemented yet + default : break; + } +} + +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_name + " is at " + current_time.toString("hh:mm:ss.zzz"); + } + else { + return timer_name + " is inactive."; + } +} + +long long AOClient::parseTime(QString input) +{ + QRegularExpression regex("(?:(?:(?.*?)y)*(?:(?.*?)w)*(?:(?.*?)d)*(?:(?
.*?)h)*(?:(?.*?)m)*(?:(?.*?)s)*)"); + QRegularExpressionMatch match = regex.match(input); + QString str_year, str_week, str_hour, str_day, str_minute, str_second; + int year, week, day, hour, minute, second; + + str_year = match.captured("year"); + str_week = match.captured("week"); + str_day = match.captured("day"); + str_hour = match.captured("hr"); + str_minute = match.captured("min"); + str_second = match.captured("sec"); + + bool is_well_formed = false; + QString concat_str(str_year + str_week + str_day + str_hour + str_minute + str_second); + concat_str.toInt(&is_well_formed); + + if (!is_well_formed) { + return -1; + } + + year = str_year.toInt(); + week = str_week.toInt(); + day = str_day.toInt(); + hour = str_hour.toInt(); + minute = str_minute.toInt(); + second = str_second.toInt(); + + long long total = 0; + total += 31622400 * year; + total += 604800 * week; + total += 86400 * day; + total += 3600 * hour; + total += 60 * minute; + total += second; + + if (total < 0) + return -1; + + return total; +} + +QString AOClient::getReprimand(bool positive) +{ + if (positive) { + return server->praise_list[genRand(0, server->praise_list.size() - 1)]; + } + else { + return server->reprimands_list[genRand(0, server->reprimands_list.size() - 1)]; + } +} diff --git a/src/commands/messaging.cpp b/src/commands/messaging.cpp new file mode 100644 index 0000000..9a852a6 --- /dev/null +++ b/src/commands/messaging.cpp @@ -0,0 +1,276 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for commands under the messaging category in aoclient.h +// Be sure to register the command in the header before adding it here! + +void AOClient::cmdPos(int argc, QStringList argv) +{ + changePosition(argv[0]); + updateEvidenceList(server->areas[current_area]); +} + +void AOClient::cmdForcePos(int argc, QStringList argv) +{ + bool ok; + QList targets; + AreaData* area = server->areas[current_area]; + int target_id = argv[1].toInt(&ok); + int forced_clients = 0; + if (!ok && argv[1] != "*") { + sendServerMessage("That does not look like a valid ID."); + return; + } + else if (ok) { + AOClient* target_client = server->getClientByID(target_id); + if (target_client != nullptr) + targets.append(target_client); + else { + sendServerMessage("Target ID not found!"); + return; + } + } + + else if (argv[1] == "*") { // force all clients in the area + for (AOClient* client : server->clients) { + if (client->current_area == current_area) + targets.append(client); + } + } + for (AOClient* target : targets) { + target->sendServerMessage("Position forcibly changed by CM."); + target->changePosition(argv[0]); + forced_clients++; + } + sendServerMessage("Forced " + QString::number(forced_clients) + " into pos " + argv[0] + "."); +} + +void AOClient::cmdG(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + QString sender_area = server->area_names.value(current_area); + QString sender_message = argv.join(" "); + for (AOClient* client : server->clients) { + if (client->global_enabled) + client->sendPacket("CT", {"[G][" + sender_area + "]" + sender_name, sender_message}); + } + return; +} + +void AOClient::cmdNeed(int argc, QStringList argv) +{ + QString sender_area = server->area_names.value(current_area); + QString sender_message = argv.join(" "); + sendServerBroadcast({"=== Advert ===\n[" + sender_area + "] needs " + sender_message+ "."}); +} + +void AOClient::cmdSwitch(int argc, QStringList argv) +{ + int char_id = server->getCharID(argv.join(" ")); + if (char_id == -1) { + sendServerMessage("That does not look like a valid character."); + return; + } + changeCharacter(char_id); +} + +void AOClient::cmdRandomChar(int argc, QStringList argv) +{ + int char_id = genRand(0, server->characters.size() - 1); + changeCharacter(char_id); +} + +void AOClient::cmdToggleGlobal(int argc, QStringList argv) +{ + global_enabled = !global_enabled; + QString str_en = global_enabled ? "shown" : "hidden"; + sendServerMessage("Global chat set to " + str_en); +} + +void AOClient::cmdPM(int arc, QStringList argv) +{ + bool ok; + int target_id = argv.takeFirst().toInt(&ok); // using takeFirst removes the ID from our list of arguments... + if (!ok) { + sendServerMessage("That does not look like a valid ID."); + return; + } + AOClient* target_client = server->getClientByID(target_id); + if (target_client == nullptr) { + sendServerMessage("No client with that ID found."); + return; + } + QString message = argv.join(" "); //...which means it will not end up as part of the message + target_client->sendServerMessage("Message from " + ooc_name + " (" + QString::number(id) + "): " + message); +} + +void AOClient::cmdAnnounce(int argc, QStringList argv) +{ + sendServerBroadcast("=== Announcement ===\r\n" + argv.join(" ") + "\r\n============="); +} + +void AOClient::cmdM(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + QString sender_message = argv.join(" "); + for (AOClient* client : server->clients) { + if (client->checkAuth(ACLFlags.value("MODCHAT"))) + client->sendPacket("CT", {"[M]" + sender_name, sender_message}); + } + return; +} + +void AOClient::cmdGM(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + QString sender_area = server->area_names.value(current_area); + QString sender_message = argv.join(" "); + for (AOClient* client : server->clients) { + if (client->global_enabled) { + client->sendPacket("CT", {"[G][" + sender_area + "]" + "["+sender_name+"][M]", sender_message}); + } + } +} + +void AOClient::cmdLM(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + QString sender_message = argv.join(" "); + server->broadcast(AOPacket("CT", {"["+sender_name+"][M]", sender_message}), current_area); +} + +void AOClient::cmdGimp(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_gimped) + sendServerMessage("That player is already gimped!"); + else { + sendServerMessage("Gimped player."); + target->sendServerMessage("You have been gimped! " + getReprimand()); + } + target->is_gimped = true; +} + +void AOClient::cmdUnGimp(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!(target->is_gimped)) + sendServerMessage("That player is not gimped!"); + else { + sendServerMessage("Ungimped player."); + target->sendServerMessage("A moderator has ungimped you! " + getReprimand(true)); + } + target->is_gimped = false; +} + +void AOClient::cmdDisemvowel(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_disemvoweled) + sendServerMessage("That player is already disemvoweled!"); + else { + sendServerMessage("Disemvoweled player."); + target->sendServerMessage("You have been disemvoweled! " + getReprimand()); + } + target->is_disemvoweled = true; +} + +void AOClient::cmdUnDisemvowel(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!(target->is_disemvoweled)) + sendServerMessage("That player is not disemvoweled!"); + else { + sendServerMessage("Undisemvoweled player."); + target->sendServerMessage("A moderator has undisemvoweled you! " + getReprimand(true)); + } + target->is_disemvoweled = false; +} + +void AOClient::cmdShake(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_shaken) + sendServerMessage("That player is already shaken!"); + else { + sendServerMessage("Shook player."); + target->sendServerMessage("A moderator has shaken your words! " + getReprimand()); + } + target->is_shaken = true; +} + +void AOClient::cmdUnShake(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!(target->is_shaken)) + sendServerMessage("That player is not shaken!"); + else { + sendServerMessage("Unshook player."); + target->sendServerMessage("A moderator has unshook you! " + getReprimand(true)); + } + target->is_shaken = false; +} diff --git a/src/commands/moderation.cpp b/src/commands/moderation.cpp new file mode 100644 index 0000000..09e8b66 --- /dev/null +++ b/src/commands/moderation.cpp @@ -0,0 +1,389 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for commands under the moderation category in aoclient.h +// Be sure to register the command in the header before adding it here! + +void AOClient::cmdBan(int argc, QStringList argv) +{ + QString args_str = argv[1]; + if (argc > 2) { + for (int i = 2; i < argc; i++) + args_str += " " + argv[i]; + } + + DBManager::BanInfo ban; + + QRegularExpression quoteMatcher("['\"](.+?)[\"']"); + QRegularExpressionMatchIterator matches = quoteMatcher.globalMatch(args_str); + QList unquoted_args; + while (matches.hasNext()) { + QRegularExpressionMatch match = matches.next(); + unquoted_args.append(match.captured(1)); + } + + QString duration = "perma"; + + if (unquoted_args.length() < 1) { + sendServerMessage("Invalid syntax. Usage:\n/ban \"\" \"\""); + return; + } + + ban.reason = unquoted_args.at(0); + if (unquoted_args.length() > 1) + duration = unquoted_args.at(1); + + long long duration_seconds = 0; + if (duration == "perma") + duration_seconds = -2; + else + duration_seconds = parseTime(duration); + + if (duration_seconds == -1) { + sendServerMessage("Invalid time format. Format example: 1h30m"); + return; + } + + ban.duration = duration_seconds; + + ban.ipid = argv[0]; + ban.time = QDateTime::currentDateTime().toSecsSinceEpoch(); + bool ban_logged = false; + int kick_counter = 0; + + if (argc > 2) { + for (int i = 2; i < argv.length(); i++) { + ban.reason += " " + argv[i]; + } + } + + for (AOClient* client : server->getClientsByIpid(ban.ipid)) { + if (!ban_logged) { + ban.ip = client->remote_ip; + ban.hdid = client->hwid; + server->db_manager->addBan(ban); + sendServerMessage("Banned user with ipid " + ban.ipid + " for reason: " + ban.reason); + ban_logged = true; + } + client->sendPacket("KB", {ban.reason + "\nID: " + QString::number(server->db_manager->getBanID(ban.ip)) + "\nUntil: " + QDateTime::fromSecsSinceEpoch(ban.time).addSecs(ban.duration).toString("dd.MM.yyyy, hh:mm")}); + client->socket->close(); + kick_counter++; + } + + if (kick_counter > 1) + sendServerMessage("Kicked " + QString::number(kick_counter) + " clients with matching ipids."); + if (!ban_logged) + sendServerMessage("User with ipid not found!"); +} + +void AOClient::cmdKick(int argc, QStringList argv) +{ + QString target_ipid = argv[0]; + QString reason = argv[1]; + int kick_counter = 0; + + if (argc > 2) { + for (int i = 2; i < argv.length(); i++) { + reason += " " + argv[i]; + } + } + + for (AOClient* client : server->getClientsByIpid(target_ipid)) { + client->sendPacket("KK", {reason}); + client->socket->close(); + kick_counter++; + } + + if (kick_counter > 0) + sendServerMessage("Kicked " + QString::number(kick_counter) + " client(s) with ipid " + target_ipid + " for reason: " + reason); + else + sendServerMessage("User with ipid not found!"); +} + +void AOClient::cmdMods(int argc, QStringList argv) +{ + QStringList entries; + int online_count = 0; + for (AOClient* client : server->clients) { + if (client->authenticated) { + entries << "---"; + if (server->auth_type != "simple") + entries << "Moderator: " + client->moderator_name; + entries << "OOC name: " + client->ooc_name; + entries << "ID: " + QString::number(client->id); + entries << "Area: " + QString::number(client->current_area); + entries << "Character: " + client->current_char; + online_count++; + } + } + entries << "---"; + entries << "Total online: " << QString::number(online_count); + sendServerMessage(entries.join("\n")); +} + +void AOClient::cmdHelp(int argc, QStringList argv) +{ + QStringList entries; + entries << "Allowed commands:"; + QMap::const_iterator i; + for (i = commands.constBegin(); i!= commands.constEnd(); ++i) { + CommandInfo info = i.value(); + if (checkAuth(info.acl_mask)) { // if we are allowed to use this command + entries << "/" + i.key(); + } + } + sendServerMessage(entries.join("\n")); +} + +void AOClient::cmdMOTD(int argc, QStringList argv) +{ + if (argc == 0) { + sendServerMessage("=== MOTD ===\r\n" + server->MOTD + "\r\n============="); + } + else if (argc > 0) { + if (checkAuth(ACLFlags.value("MOTD"))) { + QString MOTD = argv.join(" "); + server->MOTD = MOTD; + sendServerMessage("MOTD has been changed."); + } + else { + sendServerMessage("You do not have permission to change the MOTD"); + } + } +} + +void AOClient::cmdBans(int argc, QStringList argv) +{ + QStringList recent_bans; + recent_bans << "Last 5 bans:"; + recent_bans << "-----"; + for (DBManager::BanInfo ban : server->db_manager->getRecentBans()) { + 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"); + recent_bans << "Affected IPID: " + ban.ipid; + recent_bans << "Affected HDID: " + ban.hdid; + recent_bans << "Reason for ban: " + ban.reason; + recent_bans << "Date of ban: " + QDateTime::fromSecsSinceEpoch(ban.time).toString("dd.MM.yyyy, hh:mm"); + recent_bans << "Ban lasts until: " + banned_until; + recent_bans << "-----"; + } + sendServerMessage(recent_bans.join("\n")); +} + +void AOClient::cmdUnBan(int argc, QStringList argv) +{ + bool ok; + int target_ban = argv[0].toInt(&ok); + if (!ok) { + sendServerMessage("Invalid ban ID."); + return; + } + else if (server->db_manager->invalidateBan(target_ban)) + sendServerMessage("Successfully invalidated ban " + argv[0] + "."); + else + sendServerMessage("Couldn't invalidate ban " + argv[0] + ", are you sure it exists?"); +} + +void AOClient::cmdAbout(int argc, QStringList argv) +{ + sendPacket("CT", {"The akashi dev team", "Thank you for using akashi! Made with love by scatterflower, with help from in1tiate and Salanto. akashi " + QCoreApplication::applicationVersion()}); +} + +void AOClient::cmdMute(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_muted) + sendServerMessage("That player is already muted!"); + else { + sendServerMessage("Muted player."); + target->sendServerMessage("You were muted by a moderator. " + getReprimand()); + } + target->is_muted = true; +} + +void AOClient::cmdUnMute(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!target->is_muted) + sendServerMessage("That player is not muted!"); + else { + sendServerMessage("Unmuted player."); + target->sendServerMessage("You were unmuted by a moderator. " + getReprimand(true)); + } + target->is_muted = false; +} + +void AOClient::cmdOocMute(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_ooc_muted) + sendServerMessage("That player is already OOC muted!"); + else { + sendServerMessage("OOC muted player."); + target->sendServerMessage("You were OOC muted by a moderator. " + getReprimand()); + } + target->is_ooc_muted = true; +} + +void AOClient::cmdOocUnMute(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!target->is_ooc_muted) + sendServerMessage("That player is not OOC muted!"); + else { + sendServerMessage("OOC unmuted player."); + target->sendServerMessage("You were OOC unmuted by a moderator. " + getReprimand(true)); + } + target->is_ooc_muted = false; +} + +void AOClient::cmdBlockWtce(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_wtce_blocked) + sendServerMessage("That player is already judge blocked!"); + else { + sendServerMessage("Revoked player's access to judge controls."); + target->sendServerMessage("A moderator revoked your judge controls access. " + getReprimand()); + } + target->is_wtce_blocked = true; +} + +void AOClient::cmdUnBlockWtce(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!target->is_wtce_blocked) + sendServerMessage("That player is not judge blocked!"); + else { + sendServerMessage("Restored player's access to judge controls."); + target->sendServerMessage("A moderator restored your judge controls access. " + getReprimand(true)); + } + target->is_wtce_blocked = false; +} + +void AOClient::cmdAllow_Blankposting(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + AreaData* area = server->areas[current_area]; + area->blankposting_allowed = !area->blankposting_allowed; + if (area->blankposting_allowed == false) { + sendServerMessageArea(sender_name + " has set blankposting in the area to forbidden."); + } + else { + sendServerMessageArea(sender_name + " has set blankposting in the area to allowed."); + } +} + +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"); +} + diff --git a/src/commands/music.cpp b/src/commands/music.cpp new file mode 100644 index 0000000..4bde396 --- /dev/null +++ b/src/commands/music.cpp @@ -0,0 +1,80 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for commands under the music category in aoclient.h +// Be sure to register the command in the header before adding it here! + +void AOClient::cmdPlay(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + QString song = argv.join(" "); + area->current_music = song; + area->music_played_by = showname; + AOPacket music_change("MC", {song, QString::number(server->getCharID(current_char)), showname, "1", "0"}); + server->broadcast(music_change, current_area); +} + +void AOClient::cmdCurrentMusic(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->current_music != "" && area->current_music != "~stop.mp3") // dummy track for stopping music + sendServerMessage("The current song is " + area->current_music + " played by " + area->music_played_by); + else + sendServerMessage("There is no music playing."); +} + +void AOClient::cmdBlockDj(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (target->is_dj_blocked) + sendServerMessage("That player is already DJ blocked!"); + else { + sendServerMessage("DJ blocked player."); + target->sendServerMessage("You were blocked from changing the music by a moderator. " + getReprimand()); + } + target->is_dj_blocked = true; +} + +void AOClient::cmdUnBlockDj(int argc, QStringList argv) +{ + bool conv_ok = false; + int uid = argv[0].toInt(&conv_ok); + if (!conv_ok) { + sendServerMessage("Invalid user ID."); + return; + } + + AOClient* target = server->getClientByID(uid); + + if (!target->is_dj_blocked) + sendServerMessage("That player is not DJ blocked!"); + else { + sendServerMessage("DJ permissions restored to player."); + target->sendServerMessage("A moderator restored your music permissions. " + getReprimand(true)); + } + target->is_dj_blocked = false; +} diff --git a/src/commands/roleplay.cpp b/src/commands/roleplay.cpp new file mode 100644 index 0000000..ea31a64 --- /dev/null +++ b/src/commands/roleplay.cpp @@ -0,0 +1,190 @@ +////////////////////////////////////////////////////////////////////////////////////// +// akashi - a server for Attorney Online 2 // +// Copyright (C) 2020 scatterflower // +// // +// This program is free software: you can redistribute it and/or modify // +// it under the terms of the GNU Affero General Public License as // +// published by the Free Software Foundation, either version 3 of the // +// License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU Affero General Public License for more details. // +// // +// You should have received a copy of the GNU Affero General Public License // +// along with this program. If not, see . // +////////////////////////////////////////////////////////////////////////////////////// +#include "include/aoclient.h" + +// This file is for commands under the roleplay category in aoclient.h +// Be sure to register the command in the header before adding it here! + +void AOClient::cmdFlip(int argc, QStringList argv) +{ + QString sender_name = ooc_name; + QStringList faces = {"heads","tails"}; + QString face = faces[AOClient::genRand(0,1)]; + sendServerMessage(sender_name + " flipped a coin and got " + face + "."); +} + +void AOClient::cmdRoll(int argc, QStringList argv) +{ + diceThrower(argc, argv, RollType::ROLL); +} + +void AOClient::cmdRollP(int argc, QStringList argv) +{ + diceThrower(argc, argv, RollType::ROLLP); +} + +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:"); + 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) { + sendServerMessage("Invalid timer ID. Timer ID must be a whole number between 0 and 4."); + return; + } + + // Called with one argument + // Shows the status of one timer + if (argc == 1) { + 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"))) { + sendServerMessage("You are not authorized to alter the global timer."); + return; + } + requested_timer = server->timer; + } + 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] + "."); + 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) + "."); + 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) + "."); + 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) + "."); + // Hide the timer + is_global ? server->broadcast(hide_timer) : server->broadcast(hide_timer, current_area); + } + } +} + +void AOClient::cmdNoteCard(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->notecards.keys().contains(current_char)) + area->notecards.remove(current_char); + QString notecard = argv.join(" "); + area->notecards[current_char] = notecard; + sendServerMessageArea(current_char + " wrote a note card."); +} + +void AOClient::cmdNoteCardClear(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->notecards.keys().contains(current_char)) { + area->notecards.remove(current_char); + sendServerMessageArea(current_char + " erased their note card."); + } + else + sendServerMessage("You do not have a note card."); +} + +void AOClient::cmdNoteCardReveal(int argc, QStringList argv) +{ + AreaData* area = server->areas[current_area]; + if (area->notecards.isEmpty()) { + sendServerMessage("There are no cards to reveal in this area."); + return; + } + QStringList message; + message << "Note cards have been revealed."; + QMap::iterator i; + for (i = area->notecards.begin(); i != area->notecards.end(); ++i) + message << i.key() + ": " + i.value(); + sendServerMessageArea(message.join("\n")); + area->notecards.clear(); +} + +void AOClient::cmd8Ball(int argc, QStringList argv) +{ + if (server->magic_8ball_answers.isEmpty()) { + qWarning() << "8ball.txt is empty!"; + sendServerMessage("8ball.txt is empty."); + } + else { + QString response = server->magic_8ball_answers[(genRand(1, server->magic_8ball_answers.size() - 1))]; + QString sender_name = ooc_name; + QString sender_message = argv.join(" "); + + sendServerMessageArea(sender_name + " asked the magic 8-ball, \"" + sender_message + "\" and the answer is: " + response); + } +} + +void AOClient::cmdSubTheme(int argc, QStringList argv) +{ + QString subtheme = argv.join(" "); + for (AOClient* client : server->clients) { + if (client->current_area == current_area) + client->sendPacket("ST", {subtheme, "1"}); + } + sendServerMessageArea("Subtheme was set to " + subtheme); +}