diff options
| author | Markus Mittendrein <git@maxmitti.tk> | 2015-09-09 19:00:56 +0200 |
|---|---|---|
| committer | Markus Mittendrein <git@maxmitti.tk> | 2015-09-09 19:02:23 +0200 |
| commit | 8a6d4b06f2291c363f3dea17837ed20893852453 (patch) | |
| tree | c091375499e35eaa1810586454e0834c06e6c9b2 /src/crsm.cpp | |
| parent | f554a27046f203e56a07baaf214d90834942e3f5 (diff) | |
| download | manager-8a6d4b06f2291c363f3dea17837ed20893852453.tar.gz manager-8a6d4b06f2291c363f3dea17837ed20893852453.zip | |
Cleanup repo with some directories
Diffstat (limited to 'src/crsm.cpp')
| -rw-r--r-- | src/crsm.cpp | 2184 |
1 files changed, 2184 insertions, 0 deletions
diff --git a/src/crsm.cpp b/src/crsm.cpp new file mode 100644 index 0000000..9a48058 --- /dev/null +++ b/src/crsm.cpp @@ -0,0 +1,2184 @@ +#include "crsm.hpp" +#include <iostream> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <QThread> +#include <QTimer> +#include <QRegularExpression> + +#define CMD_SIGN "!" + +#define MGMT_BUFFER_FILENAME "CRSM-MGMT-Buffer" + +CRSM::CRSM(QObject *parent) : + QObject(parent) +{ + qsrand(QDateTime::currentMSecsSinceEpoch()); + codec = QTextCodec::codecForName("Windows-1252"); + qout = new QTextStream(stdout, QIODevice::WriteOnly | QIODevice::Unbuffered); + qin = new QTextStream(stdin, QIODevice::ReadOnly); + qout->setCodec(QTextCodec::codecForName("UTF-8")); + + outputBuffer.setFileName(MGMT_BUFFER_FILENAME); + outputBuffer.open(QFile::WriteOnly | QFile::Unbuffered); + + args << "/fullscreen" << "/config:config" << "/lobby:60" << "/nosignup"; + finish = false; + + readConfig(); + + connect(&managementServer, SIGNAL(newConnection()), this, SLOT(newManagementConnection())); + managementServer.listen(QHostAddress::LocalHostIPv6, settings["ManagementPort"].toUInt()); + + listC4Folders(); + readScenarios(); + + if(settings.contains("ReattachId")) + { + processManager = new ProcessManager("CRSM-Clonkserver-", settings["ReattachId"]); + } + else + { + processManager = new ProcessManager("CRSM-Clonkserver-"); + } + + + if(!processManager->isOk()) + { + out("Could not start Process Manager!"); + settings.remove("ReattachId"); + writeConfig(); + return; + } + + settings["ReattachId"] = processManager->ID(); + writeConfig(); + + if(processManager->isRunning()) + { + writeToServer("Server Manager läuft wieder.\nBis zur nächsten Runde könnte unerwartetes Verhalten auftreten.\n"); + } + + setupCmds(); + + connect(&greetMapper, SIGNAL(mapped(QString)), this, SLOT(greet(QString))); + + autoHost = settings["AutoHost"] == "true"; + + connect(processManager, SIGNAL(readyRead()), this, SLOT(readServerOutput())); + //connect(processManager, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError())); + connect(processManager, SIGNAL(finished(int)), this, SLOT(scenarioFinished())); + //QSocketNotifier *inNotifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read,this); + //connect(inNotifier, SIGNAL(activated(int)), this, SLOT(readInput())); + + QFile *reallog = new QFile(settings["ClonkDirectory"]+"CRSM.log"); + reallog->open(QIODevice::Append | QIODevice::Text); + logstream.setDevice(reallog); + + if(settings["UseIrc"] == "true") + { + prepareAndConnectIrc(); + } + else + { + settings["IrcUseIngameChat"] = "false"; + } + + ok = true; +} + +CRSM::~CRSM() +{ +} + +void CRSM::start() +{ + if(autoHost && !processManager->isRunning()) + nextScen(); +} + +bool CRSM::isOk() +{ + return ok; +} + +void CRSM::readServerOutput() +{ + static QRegExp timeRemover("^>?\\s*\\[(\\d\\d:\\d\\d:\\d\\d)\\]\\s+(.*)$"); + QString what(processManager->readLine()); + + if(settings["ServerUsesReadline"] == "true") + { + while(writtenToServer.length() > 0 && what.length() > 0 && writtenToServer.at(0) == what.at(0)) + { + writtenToServer.remove(0, 1); + what.remove(0, 1); + } + + if(what.at(0) == '>') + what.remove(0, 1); + } + + if(what.isEmpty()) + return; + + logstream << what.trimmed() << endl; + + if(settings["UseIrc"] == "true") + { + foreach(const QString &mess, what.split("\n", QString::SkipEmptyParts)) + foreach(const QString &mod, ircModIOList) + { + connection->sendCommand(IrcCommand::createNotice(mod, mess)); + } + } + out(what.trimmed() + "\n"); + if(!timeRemover.exactMatch(what)) + return; + what = timeRemover.cap(2).trimmed(); + + static QRegExp userexp("^\\s*(\\*?)\\s*<(.*)\\|(\\d+)\\|([^>]*)>\\s+(.*)$"); + if(userexp.exactMatch(what)) + { + bool isMeMessage = (userexp.cap(1) == "*"); + QString user = userexp.cap(4); + QString pcName = userexp.cap(2); + int cuid = userexp.cap(3).toInt(); + if(pcName != settings["ServerPCName"]) + { + ClientInfo& info = getClientInfo(pcName, cuid, user); + if(info.floodCheck(settings["AntiFloodCount"].toInt(), settings["AntiFloodTime"].toInt(), QDateTime(QDate::currentDate(), QTime::fromString(timeRemover.cap(1), "hh:mm:ss")))) + { + kick(pcName, "Flooding! Maximal " + settings["AntiFloodCount"] + " Nachrichten in " + settings["AntiFloodTime"] + "s"); + } + else if(!isMeMessage) + { + QString msg = userexp.cap(5).trimmed(); + QRegExp commandExp("^\\" CMD_SIGN "([^ ]+)(\\s+(.*)\\s*)?$"); + if(commandExp.exactMatch(msg)) + { + QString command = commandExp.cap(1).trimmed(); + QString commandArgs = commandExp.cap(2).trimmed(); + if(!cmd(command, commandArgs, info)) + { + respond(info, "Unbekannter Befehl: \"" + command + "\"!\n"); + } + + } + else if(session["IrcUseIngameChat"] == "true") + { + connection->sendCommand(IrcCommand::createMessage(settings["IrcIngameChannel"], "[Clonk]<" + user + "> " + msg)); + } + } + } + } + static QRegExp joinExp("^Client (.+) (?:verbunden|connected)\\.\\s*$"); + if(joinExp.exactMatch(what)) + { + static QRegExp infoMatch("^<(.*)\\|(\\d+)\\|(.*)>$"); + if(infoMatch.exactMatch(joinExp.cap(1))) + { + const ClientInfo& info = ClientInfo::clonkClient(infoMatch.cap(3), infoMatch.cap(1), infoMatch.cap(2).toInt()); + clients.insert(info.pcName, info); + QTimer *timer = new QTimer; + connect(timer, SIGNAL(timeout()), &greetMapper, SLOT(map())); + connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater())); + greetMapper.setMapping(timer, info.pcName); + timer->start(1000); + if(session["IrcUseIngameChat"] == "true") + { + connection->sendCommand(IrcCommand::createMessage(settings["IrcIngameChannel"], "[Clonk] " + info.toString() + " verbunden.")); + } + } + } + + static QRegExp activatedExp("^Client (.+) (?:aktiviert|activated)\\.\\s*$"); + if(activatedExp.exactMatch(what)) + { + QRegExp infoMatch("^<(.*)\\|(\\d+)\\|(.*)>$"); + if(infoMatch.exactMatch(joinExp.cap(1))) + { + ClientInfo &info = getClientInfo(infoMatch.cap(1), infoMatch.cap(2).toInt(), infoMatch.cap(3)); + info.activated = true; + } + } + + static QRegExp deactivatedExp("^Client (.+) (?:deaktiviert|deactivated)\\.\\s*$"); + if(deactivatedExp.exactMatch(what)) + { + QRegExp infoMatch("^<(.*)\\|(\\d+)\\|(.*)>$"); + if(infoMatch.exactMatch(joinExp.cap(1))) + { + ClientInfo &info = getClientInfo(infoMatch.cap(1), infoMatch.cap(2).toInt(), infoMatch.cap(3)); + info.activated = false; + } + } + + static QRegExp startExp("^Start!\\s*$"); + if(startExp.exactMatch(what)) + { + if(session["league"] != "true") + { + writeToServer(QString("/set maxplayer 0\n")); + } + session["running"] = "true"; + } + + + static QRegExp leaveExp("^Client (.+) (?:entfernt|removed)(.*)"); + if(leaveExp.exactMatch(what)) + { + QRegExp infoMatch("^<(.*)\\|(\\d+)\\|(.*)>$"); + if(infoMatch.exactMatch(leaveExp.cap(1))) + { + ClientInfo &info = getClientInfo(infoMatch.cap(1), infoMatch.cap(2).toInt(), infoMatch.cap(3)); + writeToServer(QString(info.nick +" ist ein L34V0R!\n")); + + if(session["IrcUseIngameChat"] == "true") + { + connection->sendCommand(IrcCommand::createMessage(settings["IrcIngameChannel"], "[Clonk] " + info.toString() + " entfernt" + leaveExp.cap(2))); + } + + if(info == sessionAdmin) + { + leaveAdmins.insert(info, QDateTime::currentDateTime()); + writeToServer("Rundenadmin wurde freigegeben.\n"); + sessionAdmin = ClientInfo(); + } + + clients.remove(info.pcName); + } + if(clients.size() == 0 && userlist.length() > 0 && session["userwish"] != "true") + { + processManager->closeProgFifos(); + } + else if(clients.size() == 0 && settings["EmptyTimer"] != "-1" && (session["CountDown"] == "false" || session["CountDown"].toInt() > settings["EmptyTimer"].toInt())) + { + writeToServer("/start " + settings["EmptyTimer"] + "\n"); + } + } + + static QRegExp countDownExp("^(?:Das Spiel beginnt in (\\d+) Sekunden\\!|The game will start in (\\d+) seconds\\.)"); + if(countDownExp.exactMatch(what)) + { + session["CountDown"] = countDownExp.cap(1); + } + + static QRegExp countDownStopExp("^(?:Spielstart abgebrochen\\.|Game start aborted\\.)"); + if(countDownStopExp.exactMatch(what)) + { + session["CountDown"] = "false"; + } +} + +/*void CRSM::processError() +{ + *qout << processManager->errorString() << endl; +}*/ + +void CRSM::readInput() +{ + QString what(qin->readLine()); + if(what == "/exitdetach" || what == "/exitupdate") + { + if(what == "/exitupdate") + { + writeToServer("Der Server Manager wird upgedatet. Befehle funktionieren temporär nicht.\n"); + } + settings["ReattachId"] = processManager->ID(); + writeConfig(); + QCoreApplication::quit(); + return; + } + if(what == "/exit") + { + processManager->closeProgFifos(); + settings.remove("ReattachId"); + finish = true; + if(session["hosting"] != "true") + scenarioFinished(); + return; + } + if(what == "/exitafter") + { + finish = true; + out("Will exit after this round."); + return; + } + if(what == "/next") + { + skipCurrent(); + return; + } + if(what == "/skip") + { + QString skipped; + if((skipped = skipScen()) != "") + { + out("Skipped \"" + skipped + "\".\n"); + } + else + out("User list is empty!\n"); + + return; + } + if(what == "/reload") + { + out("Reloading config...\n"); + lists.clear(); + settings.clear(); + ircModChecks.clear(); + ircMods.clear(); + ircModFifosOld.clear(); + ircModIOList.clear(); + ircStatusFifos.clear(); + readConfig(); + return; + } + + if(what == "/help") printAdditionalHelp(); + writeToServer(what+"\n"); +} + +void CRSM::nextScen() +{ + session["hosting"] = "true"; + if(userlist.length()>0) + { + startScen(userlist.at(0), args); + userlist.removeFirst(); + session["userwish"] = "true"; + } + else + { + startScen(nextAutoScens.first(), args); + nextAutoScens.removeFirst(); + updateNextAutoScens(); + } +} + +void CRSM::printAdditionalHelp() +{ + out("/exit stops the actual Clonk Server and exits the Server Manager.\n" + "/exitafter exits the Server Manager after the current round.\n" + "/next stops the actual Clonk Server and starts a new one.\n" + "/reload rereads the config file.\n" + "/skip removes the first scenario in user wish list.\n" + "Clonk commands following...\n"); +} + +void CRSM::readLog() +{ + QString temp(logfile->readLine().trimmed()); + if(temp != "") logstream << temp << endl; +} + +void CRSM::scenarioFinished() +{ + if(finish) + { + writeConfig(); + processManager->exit(); + if(connection != 0) + { + connection->quit(settings["IrcQuitMessage"]); + connect(connection, SIGNAL(disconnected()), QCoreApplication::instance(), SLOT(quit())); + QTimer::singleShot(500, QCoreApplication::instance(), SLOT(quit())); + } + else + QCoreApplication::quit(); + return; + } + session.clear(); + sessionAdmin = ClientInfo(); + ircAdmin = ClientInfo(); + clients.clear(); + leaveAdmins.clear(); + if((autoHost || userlist.length() > 0) && !finish) + nextScen(); + else if(!settings["IrcIngameChannel"].isEmpty()) + { + connection->sendCommand(IrcCommand::createTopic(settings["IrcIngameChannel"], "Kein laufendes Spiel.")); + QFile lastScenFile(LAST_SCEN_FILE_NAME); + QFile curScenFile(CUR_SCEN_FILE_NAME); + curScenFile.open(QFile::ReadOnly); + lastScenFile.open(QFile::WriteOnly); + lastScenFile.write(curScenFile.readAll()); + lastScenFile.close(); + curScenFile.close(); + curScenFile.open(QFile::WriteOnly); + curScenFile.close(); + } +} + +void CRSM::ircMessageReceived(IrcMessage *message) +{ + if(message->isOwn()) + return; + if(message->type() == IrcMessage::Notice) + { + if(message->nick() == "NickServ") + { + QStringList split = message->parameters().at(1).split(' ', QString::SkipEmptyParts); + if(((IrcNoticeMessage*)message)->content().contains("nickname is registered")) + { + connection->sendCommand(IrcCommand::createMessage("NickServ", "IDENTIFY " + settings["IrcNick"] + " " + settings["IrcPassword"])); + } + else if(split.first() == "STATUS") + { + QString statusNick = split.at(1); + int status = split.at(2).toInt(); + if(ircStatusFifos.contains(statusNick)) + { + QList<QPair<ClientInfo, IrcCheckCallback>> &fifo = ircStatusFifos[statusNick]; + while(fifo.size() > 0) + { + (this->*fifo.first().second)(fifo.first().first, status, ClientInfo::ircClient(statusNick)); + fifo.removeFirst(); + } + } + } + } + } + else if(message->type() == IrcMessage::Private) + { + QString target = message->parameters().at(0); + if(message->parameters().at(0) == connection->nickName()) + target = message->nick(); + QString mess = message->parameters().at(1).trimmed(); + if(target == settings["IrcIngameChannel"]) + { + writeToServer("[IRC]<" + message->nick() + "> " + mess + "\n"); + } + else if(mess.left(QString(CMD_SIGN).length()) == CMD_SIGN && mess.length() > QString(CMD_SIGN).length()) + { + QStringList args = mess.right(mess.length() - QString(CMD_SIGN).length()).split(" ", QString::SkipEmptyParts); + QString command = args.first().trimmed(); + args.removeFirst(); + QString arg = args.join(" ").trimmed(); + const ClientInfo& client = ClientInfo::ircClient(message->nick(), target); + if(!cmd(command, arg, client)) + { + respond(client, "Unbekannter Befehl: \"" + command + "\"!"); + } + } + else if(message->nick() == target && ircMods.contains(target)) + { + if(aliasWishEditor == target) + { + editAliasWishes(mess); + } + else if(ircModIOList.contains(message->nick())) + { + QString writeMessage; + if(mess.at(0) == '\\') + { + mess[0] = '/'; + } + else + { + writeMessage = "[IRC]<" + message->nick() + "> "; + } + writeMessage += mess; + writeToServer(writeMessage + "\n"); + } + } + } + else if(message->type() == IrcMessage::Join) + { + QString joinChannel = message->parameters().at(0); + connection->sendCommand(IrcCommand::createMessage(joinChannel, "Hallo " + message->nick() + "!")); + if(joinChannel == settings["IrcIngameChannel"]) + { + writeToServer("[IRC] " + message->nick() + " hat den Channel betreten." + "\n"); + } + else + ircCheckUserStatus(ClientInfo::ircClient(message->nick()), ClientInfo::ircClient(message->nick()), &CRSM::ircModCmd); + } + else if(message->type() == IrcMessage::Quit) + { + ircMods.removeAll(message->nick()); + ircModChecks.removeAll(message->nick()); + ircModIOList.removeAll(message->nick()); + if(aliasWishEditor == message->nick()) + { + stopAliasWishEditing(); + } + if(ircAdmin == ClientInfo::ircClient(message->nick())) + { + ircAdmin = ClientInfo(); + } + } + else if(message->type() == IrcMessage::Kick) + { + if(message->parameters().at(1) == connection->nickName()) + { + connection->sendCommand(IrcCommand::createJoin(message->parameters().at(0))); + } + } + else if(message->type() == IrcMessage::Part) + { + QString leaveChannel = message->parameters().at(0); + if(leaveChannel == settings["IrcIngameChannel"]) + { + writeToServer("[IRC] " + message->nick() + " hat den Channel verlassen." + "\n"); + } + } + else if(message->type() == IrcMessage::Mode) + { + QRegExp modeExp("^\\+[a-zA-Z]*(a|o)"); + if(message->parameters().size() >= 3 && message->parameters().at(0) == settings["IrcIngameChannel"] && modeExp.exactMatch(message->parameters().at(1)) && message->parameters().at(2) == settings["IrcNick"]) + { + if(settings["IrcUseIngameChat"] == "true" && !settings["IrcIngameChannel"].isEmpty()) + { + if(session["hosting"] != "true") + { + connection->sendCommand(IrcCommand::createTopic(settings["IrcIngameChannel"], "Kein laufendes Spiel.")); + } + else + { + connection->sendCommand(IrcCommand::createTopic(settings["IrcIngameChannel"], "Aktuelles Szenario: " + session["scenname"] + " | Ingamechat ist " + (session["IrcUseIngameChat"] == "true" ? "" : "de") + "aktviert.")); + } + } + } + } +} + +void CRSM::greet(QString pcName) +{ + if(!clients.contains(pcName)) + return; + const ClientInfo &info = clients.value(pcName); + writeToServer(QString("Hallo " + info.nick + "!\n")); + if(leaveAdmins.contains(info)) + { + int timeGone; + if((timeGone = leaveAdmins.value(info).secsTo(QDateTime::currentDateTime())) < settings["RegainAdminTime"].toInt() && sessionAdmin != ClientInfo()) + { + writeToServer(info.nick + "! Der Rundenadmin wurde freigegeben, weil du das Spiel verlassen hast.\nDu hast noch " + QString::number(settings["RegainAdminTime"].toInt() - timeGone) + "s Zeit um den Rundenadmin zurückzuholen.\n"); + } + else + { + leaveAdmins.remove(info); + } + } +} + +void CRSM::newManagementConnection() +{ + QTcpSocket* sock = managementServer.nextPendingConnection(); + connect(sock, SIGNAL(readyRead()), this, SLOT(newManagementData())); + connect(sock, SIGNAL(disconnected()), this, SLOT(managementConnectionDisconnected())); + sock->write(QString("Willkommen beim CRSM-Management-Interface!\nIhr Name: ").toUtf8()); +} + +void CRSM::newManagementData() +{ + QTcpSocket* sock = (QTcpSocket*)sender(); + QString allData = sock->readAll().trimmed(); + foreach(const QString& data, allData.split('\n')) + { + if(!managementConnections.contains(sock)) + { + if(data.isEmpty()) + { + sock->write("Ihr Name: "); + } + else + { + ManagementConnection conn; + conn.socket = sock; + conn.name = data; + managementConnections.insert(sock, conn); + out(conn.name + " logged in on Management-Interface.\n"); + replayOutputBuffer(sock); + } + } + else + { + if(data.isEmpty()) + { + continue; + } + ManagementConnection& conn = managementConnections[sock]; + if(data.at(0) == '/') + { + QStringList split = data.right(data.length() - 1).split(' '); + QString cmdName = split.first(); + split.removeFirst(); + if(cmd(cmdName, split.join(' '), ClientInfo::managementClient(conn))) + { + continue; + } + else + { + writeToServer(data + "\n"); + } + } + else + { + writeToServer("[CLI]<" + conn.name + "> " + data + "\n"); + } + } + } +} + +void CRSM::managementConnectionDisconnected() +{ + QTcpSocket* sock = (QTcpSocket*)sender(); + out(managementConnections.value(sock).name + "disconnected from Management-Interface."); + if(managementConnections.contains(sock)) + { + managementConnections.remove(sock); + } +} + +void CRSM::updateNextAutoScens() +{ + while(nextAutoScens.length() < settings["UserListLength"].toInt()) + { + ScenarioSettings next(""); + if(settings["RandomizeAutoHosting"] == "true") + { + next = autolist.at(qrand() % autolist.length()); + } + else + { + next = autolist.at(current); + if(++current >= autolist.length()) current = 0; + } + if(next.randomLeague) + { + next.league = qrand() % 2; + } + nextAutoScens.append(next); + } +} + +void CRSM::startScen(const ScenarioSettings &scen, QStringList argList) +{ + QString filename; + QFile lastScenFile(LAST_SCEN_FILE_NAME); + QFile curScenFile(CUR_SCEN_FILE_NAME); + curScenFile.open(QFile::ReadOnly); + lastScenFile.open(QFile::WriteOnly); + lastScenFile.write(curScenFile.readAll()); + lastScenFile.close(); + curScenFile.close(); + curScenFile.open(QFile::WriteOnly); + curScenFile.write(scen.name.toUtf8()); + curScenFile.close(); + QFile scoreboardFile(SCOREBOARD_FILE_NAME); + scoreboardFile.open(QFile::WriteOnly); + scoreboardFile.close(); + + session["scenname"] = scen.name; + session["IrcUseIngameChat"] = settings["IrcUseIngameChat"]; + if(settings["IrcUseIngameChat"] == "true" && !settings["IrcIngameChannel"].isEmpty()) + { + connection->sendCommand(IrcCommand::createTopic(settings["IrcIngameChannel"], "Aktuelles Szenario: " + session["scenname"] + " | Ingamechat ist " + (session["IrcUseIngameChat"] == "true" ? "" : "de") + "aktviert.")); + } + filename = scen.name; + while(maps["Alias"].contains(filename)) + filename = maps["Alias"].value(filename); + //processManager->setWorkingDirectory(QDir::currentPath()); + if(scen.league) + { + argList << "/league"; + session["league"] = "true"; + } + else + { + argList << "/noleague"; + } + argList << filename; + processManager->setWorkingDirectory(settings["ClonkDirectory"]); + processManager->start(settings["ServerExecutable"], argList); +} + +void CRSM::readConfig() +{ + lists.clear(); + settings.clear(); + settings["IrcReconnectDelay"] = "10"; + settings["EmptyTimer"] = "60"; + settings["MaxUserWishes"] = "2"; + settings["MaxScenWishes"] = "2"; + settings["AntiFloodCount"] = "5"; + settings["AntiFloodTime"] = "3"; + settings["RegainAdminTime"] = "120"; + settings["ServerExecutable"] = "clonk-server"; + settings["Arguments"] = "/fullscreen /lobby:300 /nosignup Objects.c4d"; + settings["ClonkConfig"] = "config"; + settings["UserListLength"] ="5"; + settings["ManagementPort"] = "9372"; + + QFile config(CONFIG_FILE_NAME); + if(!config.exists()) + { + config.open(QIODevice::WriteOnly | QIODevice::Text); + writeConfig(); + } + else if(config.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QRegExp confExp("^([^=]+)=(.*)$"); + QRegExp confPlusExp("^([^=]+)\\+=(.*)$"); + QRegExp confMapExp("^([^\\[]+)\\[([^\\]]+)\\]\\s*=\\s*(.*)$"); + QString line; + for(;;) + { + line = config.readLine().trimmed(); + if(confPlusExp.exactMatch(line)) + { + lists[confPlusExp.cap(1).trimmed()].push_back(confPlusExp.cap(2).trimmed()); + } + else if(confMapExp.exactMatch(line)) + { + maps[confMapExp.cap(1).trimmed()][confMapExp.cap(2).trimmed()] = confMapExp.cap(3).trimmed(); + } + else if(confExp.exactMatch(line)) + { + settings.insert(confExp.cap(1).trimmed(),confExp.cap(2).trimmed()); + } + if(config.atEnd()) + break; + } + } + + out("config:\n"); + foreach(const QString &key, settings.keys()) + { + out(key + " = " + settings.value(key) + "\n"); + } + out("\n"); + foreach(const QString &key, lists.keys()) + { + out(key + ":\n"); + foreach(const QString &val, lists.value(key)) + { + out("\t" + val + "\n"); + } + } + out("\n"); + foreach(const QString &key, maps.keys()) + { + out(key + ":\n"); + foreach(const QString &mapkey, maps.value(key).keys()) + { + out("\t[" + mapkey + "]" + " = " + maps.value(key).value(mapkey) + "\n"); + } + } + out("\n"); + args = settings["Arguments"].split(" "); + args << "/config:"+settings["ClonkConfig"]; + settings["ClonkDirectory"] = QFileInfo(settings["ServerExecutable"]).absoluteDir().absolutePath()+QDir::separator(); + + QFile clonkconfig(settings["ClonkConfig"]); + if(!clonkconfig.exists()) + out("WARNING: Clonk's config file is not existing!"); + else + { + clonkconfig.open(QFile::ReadOnly); + QRegExp nickExp("^\\s*Nick=\"(.*)\"\\s*$"); + QRegExp pcNameExp("^\\s*LocalName=\"(.*)\"\\s*$"); + foreach(const QString &line, QString(clonkconfig.readAll().trimmed()).split("\n")) + { + if(nickExp.exactMatch(line)) + { + settings["ServerNick"] = nickExp.cap(1); + break; + } + else if(pcNameExp.exactMatch(line)) + { + settings["ServerPCName"] = pcNameExp.cap(1); + break; + } + } + out("\n"); + out("ClonkDirectory = " + settings.value("ClonkDirectory") + "\n"); + out("ServerNick = " + settings.value("ServerNick") + "\n"); + } + out("\n"); + bool ok; + ushort mgmtPort = settings["ManagementPort"].toUShort(&ok); + if(!ok || mgmtPort == 0) + { + settings["ManagementPort"] = "9372"; + } +} + + +void CRSM::readScenarios() +{ + QFile scenfile("scenarios.lst"); + if(!scenfile.exists()) + { + out("No scenarios.lst found!"); + scenfile.open(QFile::WriteOnly); + scenfile.write("Worlds.c4f/Goldmine.c4s"); + scenfile.close(); + } + scenfile.open(QFile::ReadOnly); + QStringList scenlist = QString(scenfile.readAll()).trimmed().split("\n",QString::SkipEmptyParts); + out("Scenarios in list:\n"); + autolist.clear(); + nextAutoScens.clear(); + current = 0; + foreach(const QString &args, scenlist) + { + ScenarioSettings scen(args, ClientInfo::autoClient()); + QStringList argList = args.split(' ', QString::KeepEmptyParts); + if(argList.first().trimmed() == "--league") + { + argList.removeFirst(); + scen.league = true; + scen.name = argList.join(' '); + } + else if(argList.first().trimmed() == "--random-league") + { + argList.removeFirst(); + scen.randomLeague = true; + scen.name = argList.join(' '); + } + QString scenName = scen.name; + if(!(scen.name = scenPath(scen.name)).isEmpty()) + { + out(scen.name + "\n"); + autolist.append(scen); + } + else + { + out("WARNING: Scenario " + scenName + " not found!\n"); + } + } + + out("\n"); + + scenfile.close(); + updateNextAutoScens(); +} + +void CRSM::listC4Folders() +{ + out("Listing Contents of C4Folders..."); + QDirIterator it(settings["ClonkDirectory"], QDirIterator::FollowSymlinks); + for(; it.hasNext(); it.next()) + { + if(it.fileName() == ".." || it.fileName() == ".") + continue; + if((it.fileInfo().suffix() == "c4f" || (it.fileInfo().isDir() && !QDir(it.fileInfo().absoluteFilePath()).entryList(QStringList() << "*.c4f" << "*.c4s").isEmpty())) && it.fileName() != "." && it.fileName() != ".." && !lists["IgnoreFolders"].contains(it.fileInfo().baseName())) + { + const QStringList& list = listC4Folder(it.filePath()); + if(!list.isEmpty()) + { + QFile listFile(it.filePath()+".lst"); + listFile.open(QFile::WriteOnly); + foreach(const QString& scen, list) + { + listFile.write(scen.toUtf8() + "\n"); + } + listFile.close(); + } + } + } + out(QString(3, '\b') + ": Finished\n"); +} + +void CRSM::cleanUp() +{ + out("\nCleaning up Clonk Folder...\n"); + QDirIterator it(settings["ClonkDirectory"]+"Network/", QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + for(; it.hasNext(); it.next()) + if(it.fileInfo().exists()) QFile(it.fileInfo().absoluteFilePath()).remove(); + out("\n"); +} + +QString CRSM::scenPath(QString scenName) +{ + bool isAlias = false; + QString aliasName; + foreach(const QString& alias, maps["Alias"].keys()) + { + if(alias.compare(scenName, Qt::CaseInsensitive) == 0) + { + aliasName = scenName = alias; + break; + } + } + while(maps["Alias"].contains(scenName)) + { + scenName = maps["Alias"].value(scenName); + isAlias = true; + } + QFileInfo fileInfo(settings["ClonkDirectory"] + scenName); + if(fileInfo.suffix() != "c4s") + { + return QString(); + } + bool exists = fileInfo.exists(); + if(exists && isAlias) + { + return aliasName; + } + if(exists) + { + return scenName; + } + else + { + QStringList split = scenName.split('/'); + QString name = scenName.right(scenName.length() - split.first().length() - 1); + if(split.length() >= 2) + { + if(split.first().right(4) == ".c4f") + { + const QStringList& entryList = QDir(settings["ClonkDirectory"]).entryList(QStringList() << "*.c4f"); + QString folderName = split.first(); + foreach(const QString& entry, entryList) + { + if(entry.compare(folderName, Qt::CaseInsensitive) == 0) + { + folderName = entry; + break; + } + } + QFile lstFile(settings["ClonkDirectory"] + folderName + ".lst"); + if(lstFile.exists()) + { + lstFile.open(QFile::ReadOnly); + while(!lstFile.atEnd()) + { + const QString& line = lstFile.readLine().trimmed(); + if(line.compare(name, Qt::CaseInsensitive) == 0) + { + lstFile.close(); + if(isAlias) + { + return aliasName; + } + else + { + return folderName + '/' + line; + } + } + } + } + lstFile.close(); + } + } + } + return QString(); +} + +QString CRSM::listScenarios(QString commandArgs) +{ + QString ret; + if(commandArgs.isEmpty()) + { + ret += "Folgende Szenarien stehen zur Auswahl:\n"; + QDirIterator it(settings["ClonkDirectory"], QDirIterator::FollowSymlinks); + for(; it.hasNext(); it.next()) + { + if(it.fileInfo().suffix() == "c4s" && !it.fileInfo().absoluteFilePath().contains(settings["ClonkDirectory"]+"Network/")) + ret += QString(" "+it.fileInfo().absoluteFilePath().replace(settings["ClonkDirectory"],"")+"\n"); + } + ret += "-----------------------------------------------------------------\nFolgende Ordner stehen zur Auswahl:\n"; + QDirIterator folderIt(settings["ClonkDirectory"], QDirIterator::FollowSymlinks); + for(; folderIt.hasNext(); folderIt.next()) + { + if(folderIt.fileInfo().suffix() == "lst" && !folderIt.fileInfo().absoluteFilePath().contains(settings["ClonkDirectory"]+"Network/") && !lists["IgnoreFolders"].contains(folderIt.fileInfo().baseName())) + ret += QString(" "+folderIt.fileInfo().absoluteFilePath().left(folderIt.fileInfo().absoluteFilePath().length() - 4).replace(settings["ClonkDirectory"],"")+"\n"); + } + } + else if(commandArgs.toLower() == "aliase") + { + ret += "Vorhandene Aliase:\n"; + foreach(const QString &alias, maps["Alias"].keys()) + { + ret += QString(" " + alias + " = " + maps["Alias"].value(alias) + "\n"); + } + } + else + { + QFile file(settings["ClonkDirectory"] + commandArgs + ".lst"); + if(file.exists()) + { + ret += "Der Ordner \"" + commandArgs + QString("\" enthält folgende Szenarien:\n"); + file.open(QFile::ReadOnly); + while(!file.atEnd()) + ret += " " + QString::fromUtf8(file.readLine()).trimmed() + "\n"; + } + else + ret += "Der Ordner \"" + commandArgs + "\" wurde nicht gefunden!\n"; + } + return ret; +} + +QString CRSM::printQueue() +{ + QString ret; + if(userlist.length() == 0 && !autoHost) + { + return "Die Warteschlange ist leer.\n"; + } + ret = "Folgende Szenarien befinden sich in der Warteschlange:\n"; + for(int i = 0; i < settings["UserListLength"].toInt(); ++i) + { + const ScenarioSettings *scen; + if(i < userlist.length()) + { + scen = &userlist.at(i); + } + else if(!autoHost) + { + break; + } + else + { + scen = &nextAutoScens.at(i - userlist.length()); + } + ret += "\t" + QString::number(i + 1) + ". " + scen->name + (scen->league ? " in der Liga" : "") + " (" + scen->wishClient.toString() + ")\n"; + } + return ret; +} + +void CRSM::ircCheckModCmd(const QString &nick, CmdFunctionRef func, QString arg) +{ + ircModFifos[nick].append(qMakePair<CmdFunctionRef, QString>(func, arg)); + ircCheckUserStatus(ClientInfo::ircClient(nick), ClientInfo::ircClient(nick), &CRSM::ircModCmd); +} + +QString CRSM::skipScen() +{ + if(userlist.length() > 0) + { + QString skipped = userlist.first().name + " (" + userlist.first().wishClient.toString() + ")"; + userlist.removeFirst(); + return skipped; + } + else + return ""; +} + +bool CRSM::skipCurrent() +{ + if(processManager->isRunning()) + { + processManager->closeProgFifos(); + return true; + } + return false; +} + +void CRSM::writeToServer(const QString &message) +{ + if(!processManager->isWritable()) + return; + int i = 0; + QStringList split = message.split('\n', QString::KeepEmptyParts); + foreach(QString line, split) + { + if(!line.isEmpty()) + { + if(i < split.length() - 1) + line += "\n"; + while(line.length() > 240) + { + QString linePart = line.left(240); + int pos = linePart.lastIndexOf(QRegExp("\\s")); + bool wordSplit = false; + if(pos <= 0) + { + pos = 240; + wordSplit = true; + } + linePart = line.left(pos); + line = line.right(line.length() - pos); + if(wordSplit) + { + linePart += "-"; + } + linePart += "\n"; + processManager->write(codec->fromUnicode(linePart)); + if(settings["ServerUsesReadline"] == "true") + { + if(writtenToServer.length() > settings["ReadlineRereadLimit"].toInt()) + writtenToServer.clear(); + writtenToServer += codec->fromUnicode(linePart); + } + } + processManager->write(codec->fromUnicode(line)); + if(settings["ServerUsesReadline"] == "true") + { + if(writtenToServer.length() > settings["ReadlineRereadLimit"].toInt()) + writtenToServer.clear(); + writtenToServer += codec->fromUnicode(line); + } + } + ++i; + } +} + +void CRSM::writeConfig() +{ + QFile config(CONFIG_FILE_NAME); + config.open(QFile::WriteOnly); + + QTextStream configStream(&config); + + foreach(const QString &key, settings.keys()) + { + configStream << key << " = " << settings.value(key) << endl; + } + configStream << endl; + foreach(const QString &key, lists.keys()) + { + foreach(const QString &val, lists.value(key)) + { + configStream << key << " += " << val << endl; + } + } + configStream << endl; + foreach(const QString &key, maps.keys()) + { + foreach(const QString &mapkey, maps.value(key).keys()) + { + configStream << key << "[" << mapkey << "]" << " = " << maps.value(key).value(mapkey) << endl; + } + } + configStream << endl; + + config.close(); +} + +QString CRSM::addAliasWish(const QString ¶m) +{ + QRegExp aliasExp("^([^=]+)=(.*)$"); + if(aliasExp.exactMatch(param)) + { + const QString &alias = aliasExp.cap(1).trimmed(); + const QString &scen = aliasExp.cap(2).trimmed(); + QString scenP = scen; + if(maps["Alias"].contains(alias)) + { + return "Alias ist bereits vergeben!"; + } + else if(maps["AliasWishes"].contains(alias)) + { + return "Alias ist bereits als Wunsch vergeben!"; + } + else if(!(scenP = scenPath(scen)).isEmpty()) + { + maps["AliasWishes"].insert(alias, scenP); + informModsAboutAliasWish(); + return "Aliaswunsch ist hinterlegt!"; + } + else + { + return "Szenario \"" + scen + "\" wurde nicht gefunden!"; + } + } + else + { + return "Eingabefehler! Siehe !help für mehr Informationen."; + } +} + +void CRSM::informModsAboutAliasWish() +{ + foreach(const QString& mod, ircMods) + { + connection->sendCommand(IrcCommand::createNotice(mod, "Ein neuer Aliaswunsch ist verfügbar. Insgesamt verfügbar: " + QString::number(maps["AliasWishes"].size()))); + } +} + +void CRSM::editAliasWishes() +{ + if(maps["AliasWishes"].isEmpty()) + { + connection->sendCommand(IrcCommand::createMessage(aliasWishEditor, "Keine Aliaswünsche " + (currentAliasWish == "" ? QString("") : QString("mehr ")) + "vorhanden.")); + stopAliasWishEditing(); + } + else + { + currentAliasWish = maps["AliasWishes"].firstKey(); + connection->sendCommand(IrcCommand::createMessage(aliasWishEditor, currentAliasWish + " = " + maps["AliasWishes"][currentAliasWish] + " [Ja|Nein|Stop]")); + } +} + +void CRSM::editAliasWishes(const QString &message) +{ + if(message.toLower() == "j" || message.toLower() == "ja") + { + maps["Alias"][currentAliasWish] = maps["AliasWishes"][currentAliasWish]; + maps["AliasWishes"].remove(currentAliasWish); + } + else if(message.toLower() == "n" || message.toLower() == "nein") + { + maps["AliasWishes"].remove(currentAliasWish); + } + else if(message.toLower() == "s" || message.toLower() == "stop") + { + connection->sendCommand(IrcCommand::createMessage(aliasWishEditor, "Aliaswunsch-Bearbeitung gestoppt.")); + stopAliasWishEditing(); + return; + } + else + { + connection->sendCommand(IrcCommand::createMessage(aliasWishEditor, "\"" + message + "\" ist keine Antwortmöglichkeit. Antwortmöglichkeiten: [Ja|Nein|Stop]")); + } + editAliasWishes(); +} + +void CRSM::stopAliasWishEditing() +{ + aliasWishEditor = ""; + currentAliasWish = ""; +} + +QString CRSM::ircActivateIngameChat(bool activated) +{ + if(settings["IrcUseIngameChat"] == "true" && !settings["IrcIngameChannel"].isEmpty()) + { + session["IrcUseIngameChat"] = activated ? "true" : "false"; + connection->sendCommand(IrcCommand::createTopic(settings["IrcIngameChannel"], "Aktuelles Szenario: " + session["scenname"] + " | Ingamechat ist " + (session["IrcUseIngameChat"] == "true" ? "" : "de") + "aktviert.")); + return "Ingamechat wurde " + (session["IrcUseIngameChat"] == "true" ? QString("") : QString("de")) + "aktviert."; + } + else + return "Ingamechat ist administrativ deaktiviert!"; +} + +QStringList CRSM::listC4Folder(const QString &path) +{ + QStringList ret; + QFileInfo fileInfo(path); + if(fileInfo.isDir()) + { + QDir dir(path); + const QStringList folderList = dir.entryList(QStringList() << "*.c4f", QDir::NoFilter, QDir::Name | QDir::IgnoreCase); + ret.append(dir.entryList(QStringList() << "*.c4s", QDir::NoFilter, QDir::Name | QDir::IgnoreCase)); + foreach(const QString &folder, folderList) + { + const QStringList &folderList = listC4Folder(path + '/' + folder); + foreach (const QString &scen, folderList) { + ret.append(folder + '/' + scen); + } + } + } + else + { + QProcess c4group; + c4group.start(settings["ClonkDirectory"]+C4GROUP_EXECUTABLE, QStringList() << path << "-l", QProcess::ReadOnly); + c4group.waitForFinished(); + c4group.readLine(); + QRegExp finishExp("^\\d+ Entries, \\d+ Bytes$"); + QRegExp scenarioExp("^(.*\\.c4s)\\s+\\d+ Bytes\\s.*$"); + QRegExp folderExp("^(.*\\.c4f)\\s+\\d+ Bytes\\s.*$"); + QString line; + while(!c4group.atEnd()) + { + line = codec->toUnicode(c4group.readLine().trimmed()); + if(line.isEmpty()) + continue; + if(finishExp.exactMatch(line)) + break; + if(scenarioExp.exactMatch(line)) + { + ret.append(scenarioExp.cap(1)); + } + else if(folderExp.exactMatch(line)) + { + const QStringList &folderList = listC4Folder(path + '/' + folderExp.cap(1)); + foreach (const QString &scen, folderList) { + ret.append(folderExp.cap(1) + '/' + scen); + } + } + } + } + return ret; +} + +void CRSM::addCommand(const QString &name, CmdFunction func, int interfaces, UserType userType, const QString &shortDescription, const QString &argList, const QString &longDescription) +{ + cmds.insert(name.trimmed(), CmdFunctionRef(name.trimmed(), func, interfaces, userType, shortDescription, argList, longDescription)); +} + +bool CRSM::cmdExists(const QString &name, ClientInterface interface) +{ + return cmds.contains(name) && cmds.value(name).interfaces & interface; +} + +bool CRSM::cmd(const QString &name, const QString &args, const ClientInfo &client) +{ + if(cmdExists(name, client.interface)) + { + const CmdFunctionRef& cmdRef = cmds.value(name); + switch(client.interface) + { + case Clonk: + if(clientUserType(client) >= cmdRef.userType) + { + (this->*cmdRef.func)(name, args, client, clientUserType(client)); + } + else + { + rightsFailMessage(client, cmdRef.userType); + } + break; + case IRC: + if(cmdRef.userType == User || ((cmdRef.userType == Admin && (clientUserType(client) >= Admin)) || (cmdRef.userType == Moderator && clientUserType(client) >= Moderator))) + { + (this->*cmdRef.func)(name, args, client, clientUserType(client)); + } + else if(cmdRef.userType >= Admin) + { + ircCheckModCmd(client.nick, cmdRef, args); + } + break; + case Management: + (this->*cmdRef.func)(name, args, client, UserType::Max); + break; + case Auto: //just to avoid the compiler warning, there can't be a command from this interface + break; + } + + return true; + } + else + { + return false; + } +} + +void CRSM::rightsFailMessage(const ClientInfo &info, UserType minUserType) +{ + respond(info, "Nur ein " + userTypeStrings.value(minUserType) + " kann diesen Befehl verwenden.\n", RespondType::PrivateNotice); +} + +UserType CRSM::clientUserType(const ClientInfo &client) +{ + switch(client.interface) + { + case Clonk: + if(lists["Moderators"].contains(QString::number(client.CUID))) + return Moderator; + if(sessionAdmin == client) + return Admin; + break; + case IRC: + if(ircMods.contains(client.nick)) + return Moderator; + else if(ircAdmin == client) + return Admin; + break; + case Management: + return Max; + case Auto: //just to avoid the compiler warning + break; + } + + return User; +} + +void CRSM::setupCmds() +{ + addCommand("admin", &CRSM::admin, Clonk | IRC, User, "Ohne Name trägt es den Autor der Nachricht als Rundenadmin ein, bzw. mit Name den Spieler mit entsprechendem Namen, insofern nicht bereits ein Rundenadmin feststeht.", "[Chatnick¦PC-Name]"); + addCommand("ingameadmin", &CRSM::ingameadmin, IRC | Management, Admin, "Legt den Ingame-Rundenadmin fest.", "<Ingame-Nick¦PC-Name>"); + if(settings["UseIrc"] == "true") + { + addCommand("ircadmin", &CRSM::ircadmin, Clonk | Management, Admin, "Legt den IRC-Rundenadmin fest.", "<IRC-Nick>", "Legt den IRC-Rundenadmin fest. Der IRC-Admin kann über den IRC dieselben Aktionen durchführen wie der Ingame-Rundenadmin."); + } + addCommand("noadmin", &CRSM::noadmin, Clonk | IRC | Management, Admin, "Entzieht dem (IRC-)Rundenadmin seine Rechte, damit jemand anders Rundenadmin sein kann."); + addCommand("aliaswish", &CRSM::aliaswish, Clonk | IRC, User, "Deponiert den Wunsch, <Alias> als Alias für <Szenario> einzutragen. Ein Moderator entscheidet darüber.", "<Alias> = <Szenario>"); + if(settings["IrcUseIngameChat"] == "true") + { + addCommand("ircchat", &CRSM::ircchat, Clonk | Management, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", "<on¦off>"); + addCommand("ingamechat", &CRSM::ircchat, IRC | Management, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", "<on¦off>"); + } + addCommand("queue", &CRSM::queue, Clonk | IRC | Management, User, "Zeigt die nächsten " + settings["UserListLength"] + " Szenarien auf der Warteliste."); + addCommand("host", &CRSM::host, Clonk | IRC | Management, User, "Nimmt das angegebene Szenario in die Warteschlange auf. Optional in der Liga, wenn \"--league\" angegeben wird.", "[--league] <Szenarioname¦Alias>"); + addCommand("list", &CRSM::list, Clonk | IRC | Management, User, "Listet alle definierten Aliase oder alle möglichen Szenarien und Ordner auf, bzw. alle Szenarien im Ordner oder Rundenordner.", "[Aliase¦Rundenordner[.c4f]]"); + addCommand("clientlist", &CRSM::clientlist, IRC | Management, User, "Listet alle verbundenen Clients mit PC-Name und Chatnick auf."); + addCommand("help", &CRSM::help, Clonk | IRC | Management, User, "Zeigt die Hilfe an.", "[long¦Befehlsname]", "Listet alle verfügbaren Befehle auf. Mit long werden alle verfügbaren Befehle mit Kurzbeschreibung aufgelistet."); + addCommand("stop", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Stoppt einen laufenden Countdown."); + addCommand("start", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Startet den Countdown.", "[Countdownzeit in s]"); + addCommand("teamdist", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Ändert die Teamverteilung.", "<none¦free¦host¦random¦randominv>"); + addCommand("plrteam", &CRSM::passToClonk, Clonk | IRC, Admin, "Ändert das Team eines Spielers.", "<Spieler> <neues Team>", "Verschiebt <Spieler> in das <neue Team>."); + addCommand("pause", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Pausiert das Spiel."); + addCommand("unpause", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Setzt das pausierte Spiel fort."); + addCommand("observer", &CRSM::pcNamePassToClonk, Clonk | IRC | Management, Admin, "Der angegebene Host muss zuschauen.", "<PC-Name¦Chatnick>"); + addCommand("deactivate", &CRSM::pcNamePassToClonk, Clonk | IRC | Management, Admin, "Deaktiviert den angegebenen Host.", "<PC-Name¦Chatnick>"); + addCommand("activate", &CRSM::pcNamePassToClonk, Clonk | IRC | Management, Admin, "Aktiviert den angegebenen Host.", "<PC-Name¦Chatnick>"); + addCommand("kick", &CRSM::pcNamePassToClonk, Clonk | IRC | Management, Admin, "Kickt den angegebenen Host.", "<PC-Name¦Chatnick>"); + addCommand("script", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Führt das angegebene Script aus.", "<Script>"); + addCommand("asyncctrl", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Aktiviert den asynchronen Netzwerkmodus."); + addCommand("centralctrl", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Aktiviert den zentralen Netzwerkmodus."); + addCommand("decentralctrl", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Aktiviert den dezentralen Netzwerkmodus."); + addCommand("newscriptplayer", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Fügt in der Lobby einen Script spieler hinzu.", "[Spielername]"); + addCommand("removeplr", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Entfernt einen Spieler in der Lobby.", "[Spielername]"); + addCommand("nodebug", &CRSM::passToClonk, Clonk | IRC | Management, Admin, "Deaktiviert den Debug-Modus zwingend für alle."); + addCommand("autohost", &CRSM::autohost, Clonk | IRC | Management, Moderator, "Aktiviert Auto-Hosting."); + addCommand("noautohost", &CRSM::autohost, Clonk | IRC | Management, Moderator, "Deaktiviert Auto-Hosting."); + addCommand("set", &CRSM::set, Clonk | IRC | Management, Admin, "Setzt diverse Einstellungen entsprechend dem /set Befehl von Clonk.", "<maxplayer¦password¦faircrew¦teamcolors¦runtimejoin> [on¦off¦<Passwort>¦<Spieleranzahl>]"); + + addCommand("skip", &CRSM::skip, Clonk | IRC | Management, Moderator, "Entfernt das nächste Szenario aus der Wunschliste."); + addCommand("next", &CRSM::next, Clonk | IRC | Management, Moderator, "Versucht das aktuelle Szenario zu überspringen."); + addCommand("kill", &CRSM::kill, IRC | Management, Moderator, "Tötet den Clonk-Server, vorausgesetzt es läuft einer.", "", "Tötet den Clonk-Server, vorausgesetzt es läuft einer. Bitte nur verwenden wenn " CMD_SIGN "next nicht funktioniert."); + addCommand("clear", &CRSM::clear, Clonk | IRC | Management, Moderator, "Löscht die Wunschliste."); + addCommand("aliaswishes", &CRSM::aliaswishes, IRC | Management, Moderator, "Zum geführten Bearbeiten der Aliaswünsche."); + addCommand("newalias", &CRSM::newalias, IRC | Management, Moderator, "Trägt <Alias> als Alias für <Szenario> ein.", "<Alias> = <Szenario>"); + addCommand("modinfo", &CRSM::modinfo, IRC | Management, Moderator, "Listet alle Moderatoren auf."); + addCommand("io", &CRSM::io, IRC, Moderator, "Schaltet den IO-Modus ein bzw. aus.", "", "Schaltet den IO-Modus ein bzw. aus. Im IO-Modus wird die Ausgabe vom Clonk-Server an den Privat-Chat weitergeleitet und der Privat-Chat wird an den Clonk-Server weitergeleitet. Clonk-Befehle mit \\ statt /."); + + + if(settings["UseIrc"] == "true") + { + addCommand("join", &CRSM::join, IRC | Management, Moderator, "Betritt den angegebenen IRC-Channel, optional mit angegebenem Schlüssel.", "<channel> [channel-key]"); + addCommand("leave", &CRSM::leave, IRC | Management, Moderator, "Verlässt den angegebenen oder aktuellen IRC-Channel, optional mit angegebenem Grund.", "[channel] [reason]"); + } + + addCommand("exit", &CRSM::exit, Management, UserType::Max, "Beendet den Server Mananger.", "", "Beendet den Server Mananger und falls ein Clonk-Server läuft auch diesen."); + addCommand("exitdetach", &CRSM::exitDetach, Management, UserType::Max, "Beendet den Server Mananger, lässt den Clonk-Server weiterlaufen.", "", "Beendet den Server Mananger und falls ein Clonk-Server läuft, läuft dieser weiter."); + addCommand("exitupdate", &CRSM::exitUpdate, Management, UserType::Max, "Zusätzlich zu exitdetach wird im laufenden Spiel angekündigt, dass der Server-Manager wegen eines Updates beendet wird."); + addCommand("exitafter", &CRSM::exitAfter, Management, UserType::Max, "Beendet den Server-Manager, sobald das laufende Spiel vorbei ist."); + addCommand("reload", &CRSM::reload, Management, UserType::Max, "Liest die Konfigurationsdatei neu ein."); + addCommand("saveconfig", &CRSM::saveConfig, Management, UserType::Max, "Speichert die aktuelle Konfiguration in die Konfigurationsdatei."); + addCommand("reconnectirc", &CRSM::reconnectIrc, Management, UserType::Max, "Stellt die IRC-Verbindung wieder her."); +} + +void CRSM::respond(const ClientInfo &client, const QString &message, const RespondType type) +{ + switch(client.interface) + { + case Clonk: + writeToServer(message); + break; + case IRC: + foreach(const QString line, message.split('\n', QString::SkipEmptyParts)) + { + switch(type) + { + case RespondType::Normal: + case RespondType::Private: + connection->sendCommand(IrcCommand::createMessage(type == Normal ? client.target : client.nick, line)); + break; + case RespondType::Notice: + case RespondType::PrivateNotice: + connection->sendCommand(IrcCommand::createNotice(type == Notice ? client.target : client.nick, line)); + break; + } + } + break; + case Management: + client.management.socket->write(message.toUtf8()); + break; + case Auto: //just to avoid the compiler warning + break; + } +} + +void CRSM::ircCheckUserStatus(const ClientInfo &requester, const ClientInfo &subject, IrcCheckCallback callback) +{ + ircStatusFifos[subject.nick].append(qMakePair<ClientInfo, IrcCheckCallback>(requester, callback)); + connection->sendCommand(IrcCommand::createMessage("NickServ", "STATUS " + subject.nick)); +} + +void CRSM::setIngameAdmin(const ClientInfo &client, const QString& newAdmin) +{ + if(clients.contains(newAdmin)) + { + const ClientInfo& info = clients.value(newAdmin); + if(sessionAdmin == info) + { + respond(client, info.toString() + " ist bereits Rundenadmin.\n"); + } + sessionAdmin = info; + } + else + { + bool notFound = true, ambigous = false; + ClientInfo foundClient; + foreach(const ClientInfo& client, clients) + { + if(client.nick == newAdmin) + { + if(!notFound) + { + ambigous = true; + break; + } + foundClient = client; + notFound = false; + } + } + if(ambigous) + { + respond(client, "Chat-Nickname " + newAdmin + " ist nicht eindeutig. Bitte PC-Namen verwenden.\n"); + return; + } + else if(notFound) + { + respond(client, newAdmin + " wurde nicht gefunden!\n"); + return; + } + else if(sessionAdmin == foundClient) + { + respond(client, sessionAdmin.toString() + " ist bereits Rundenadmin!\n"); + return; + } + else + { + sessionAdmin = foundClient; + } + respond(client, sessionAdmin.toString() + " wurde als Rundenadmin eingetragen.\n"); + } +} + +void CRSM::out(const QString &text) +{ + *qout << text; + qout->flush(); + if(managementConnections.size() == 0) + { + outputBuffer.write(text.toUtf8()); + } + else + { + foreach(const ManagementConnection& conn, managementConnections) + { + conn.socket->write(text.toUtf8()); + } + } +} + +void CRSM::replayOutputBuffer(QTcpSocket *socket, bool clear) +{ + QFile outputBufferRead(MGMT_BUFFER_FILENAME); + outputBufferRead.open(QFile::ReadOnly); + const QByteArray& data = outputBufferRead.readAll(); + outputBufferRead.close(); + socket->write(data); + if(clear) + { + outputBuffer.close(); + outputBuffer.open(QFile::WriteOnly | QFile::Unbuffered); + } +} + +bool CRSM::scenAllowed(const ScenarioSettings &scen, const ClientInfo &client, UserType userType) +{ + if(userType >= UserType::Moderator) + { + return true; + } + QString scenName = scen.name; + while(maps["Alias"].contains(scenName)) + scenName = maps["Alias"].value(scenName); + + int scenCount = 0; + int userCount = 0; + foreach(const ScenarioSettings& setting, userlist) + { + userCount += (setting.wishClient == client); + + QString otherScenName = setting.name; + while(maps["Alias"].contains(otherScenName)) + otherScenName = maps["Alias"].value(otherScenName); + + scenCount += (otherScenName == scenName); + } + if(scenCount >= settings["MaxScenWishes"].toInt()) + { + respond(client, "Dieses Szenario ist jetzt oft genug in der Wunschliste!\n"); + return false; + } + if(userCount >= settings["MaxUserWishes"].toInt()) + { + respond(client, "Von dir sind jetzt genug Wünsche in der Liste!\n"); + return false; + } + return true; +} + +void CRSM::kick(const QString& pcName, const QString& reason) +{ + writeToServer("/kick " + pcName + (reason.isEmpty() ? QString("") : QString(" ") + reason) + "\n"); +} + +void CRSM::prepareAndConnectIrc() +{ + connection = new IrcConnection(settings["IrcServer"]); + connection->setUserName(settings["IrcNick"]); + connection->setNickName(settings["IrcNick"]); + connection->setRealName(settings["IrcRealName"]); + connection->setReconnectDelay(settings["IrcReconnectDelay"].toInt()); + connection->sendCommand(IrcCommand::createMode(settings["IrcNick"], "+B")); + connection->sendCommand(IrcCommand::createJoin(settings["IrcChannel"])); + if(settings["IrcUseIngameChat"] == "true" && !settings["IrcIngameChannel"].isEmpty()) + { + connection->sendCommand(IrcCommand::createJoin(settings["IrcIngameChannel"])); + connection->sendCommand(IrcCommand::createTopic(settings["IrcIngameChannel"], "Kein laufendes Spiel.")); + } + else + { + settings["IrcUseIngameChat"] = "false"; + } + + connection->setPassword(settings["IrcPassword"]); + connect(connection, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(ircMessageReceived(IrcMessage*))); + connection->open(); +} + +ClientInfo &CRSM::getClientInfo(const QString &pcName, int cuid, const QString &user) +{ + if(!clients.contains(pcName)) + { + clients.insert(pcName, ClientInfo::clonkClient(user, pcName, cuid)); + } + return clients[pcName]; +} + +CMD_FUNCTION_IMPL(help) + bool longHelp = (args == "long"); + if(args.isEmpty() || longHelp) + { + QString response = "Verfügbare Befehle: "; + foreach(const CmdFunctionRef& cmd, cmds) + { + if((cmd.interfaces & client.interface) == client.interface && userType >= cmd.userType) + { + if(longHelp) + { + response += "\n" CMD_SIGN + cmd.name + QString(" ") + (!cmd.argList.isEmpty() ? cmd.argList + QString(" ") : QString("")) + (!cmd.shortDescription.isEmpty() ? QString("- ") + cmd.shortDescription : QString("")); + } + else + response += cmd.name + ", "; + } + } + if(!longHelp) + response = response.left(response.length() - 2); + respond(client, response + "\n"); + respond(client, "Genauere Informationen zu einem Befehl mit " CMD_SIGN "help <Befehlsname>\n" + "[Sachen in eckigen Klammern sind optional]\n" + "<Sachen in spitzen Klammern sind benoetigte Argumente>\n"); + } + else + { + if(cmdExists(args, client.interface)) + { + const CmdFunctionRef& cmd = cmds.value(args); + respond(client, CMD_SIGN + cmd.name + " " + (!cmd.argList.isEmpty() ? cmd.argList + " " : "") + (!cmd.longDescription.isEmpty() ? "- " + cmd.longDescription : !cmd.shortDescription.isEmpty() ? "- " + cmd.shortDescription : "") + "\n"); + } + else + { + respond(client, "Befehl " + args + " existiert nicht.\n"); + } + } +} + +CMD_FUNCTION_IMPL(passToClonk) + writeToServer('/' + cmd + ' ' + args + '\n'); +} + +CMD_FUNCTION_IMPL(admin) + ClientInfo *interfaceAdminPtr; + switch(client.interface) + { + case Clonk: + interfaceAdminPtr = &sessionAdmin; + if(args == settings["ServerNick"] || args == settings["ServerPCName"]) + { + respond(client, "Der Server kann nicht als Rundenadmin eingetragen werden!\n"); + return; + } + break; + case IRC: + interfaceAdminPtr = &ircAdmin; + break; + default: + return; + } + ClientInfo& interfaceAdmin = *interfaceAdminPtr; + if(args.isEmpty() && interfaceAdmin == client) + { + respond(client, "Du ist bereits Rundenadmin!\n"); + return; + } + else if((ircAdmin != ClientInfo() || sessionAdmin != ClientInfo()) && userType < Admin && !(leaveAdmins.contains(client) && leaveAdmins.value(client).secsTo(QDateTime::currentDateTime()) < settings["RegainAdminTime"].toInt())) + { + respond(client, ircAdmin.toString() + (!ircAdmin.nick.isEmpty() && !sessionAdmin.nick.isEmpty() ? " und " : "") + sessionAdmin.toString() + " ist bereits Rundenadmin!\n"); + return; + } + else if(args.isEmpty()) + { + interfaceAdmin = client; + respond(client, client.toString() + " wurde als Rundenadmin eingetragen!\n"); + return; + } + else if(client.interface == Clonk) + { + setIngameAdmin(client, args); + } + else if(client.interface == IRC) + { + ircCheckUserStatus(client, ClientInfo::ircClient(args), &CRSM::ircSetAdmin); + } +} + +CMD_FUNCTION_IMPL(host) + if(userlist.length() >= settings["UserListLength"].toInt()) + respond(client, "Maximale Warteschlangenlänge von "+settings["UserListLength"]+" erreicht!\n"); + else + { + if(args.trimmed().isEmpty()) + respond(client, "Bitte gib einen Szenarionamen an!\n"); + else + { + ScenarioSettings scen(args, client); + QStringList argList = args.split(' ', QString::KeepEmptyParts); + if(argList.first().trimmed() == "--league") + { + argList.removeFirst(); + scen.league = true; + scen.name = argList.join(' '); + } + if(!(scen.name = scenPath(scen.name)).isEmpty()) + { + if(scenAllowed(scen, client, userType)) + { + userlist << scen; + respond(client, "Szenario " + scen.name + " wurde der Warteschlange" + (scen.league ? " mit Liga" : "") + " hinzugefügt.\n"); + if(userlist.length() == 1 && session["userwish"] != "true" && session["running"] != "true" && session["hosting"] == "true" && clients.size() > 0) + respond(client, "Überrede alle Spieler zu leaven und dein Wunsch wird sofort gehostet ;-)\n"); + else if(session["hosting"] != "true") + { + nextScen(); + } + else if(clients.size() == 0 && session["userwish"] != "true") + { + processManager->closeProgFifos(); + } + } + } + else + { + respond(client, "Szenario \"" + args + "\" wurde nicht gefunden!\n"); + } + } + } +} + +CMD_FUNCTION_IMPL(list) + if(client.interface == Clonk || client.interface == Management) + { + respond(client, listScenarios(args)); + } + else if(client.interface == IRC) + { + respond(client, settings["IrcScenListMessage"]); + } +} + +CMD_FUNCTION_IMPL(queue) + respond(client, printQueue(), RespondType::PrivateNotice); +} + +CMD_FUNCTION_IMPL(aliaswish) + respond(client, addAliasWish(args) + "\n"); +} + +CMD_FUNCTION_IMPL(ircchat) + respond(client, ircActivateIngameChat(args.toLower() == "on") + "\n"); +} + +CMD_FUNCTION_IMPL(autohost) + if(cmd == "autohost") + { + autoHost = true; + respond(client, "Automatisches Hosting aktiviert.\n"); + if(session["hosting"] != "true") + { + nextScen(); + } + } + else if(cmd == "noautohost") + { + autoHost = false; + respond(client, "Automatisches Hosting deaktiviert.\n"); + } + +} + +CMD_FUNCTION_IMPL(skip) + QString skipped; + if((skipped = skipScen()) != "") + { + respond(client, "\"" + skipped + "\" übersprungen.\n"); + } + else + { + respond(client, "Userliste ist leer!\n"); + } +} + +CMD_FUNCTION_IMPL(next) + if(skipCurrent()) + { + respond(client, "Versuche zu überspringen...\n"); + } +} + +CMD_FUNCTION_IMPL(kill) + processManager->kill(); + respond(client, "Clonk-Server wurde gekillt.\n"); +} + +CMD_FUNCTION_IMPL(clear) + if(userlist.length() > 0) + { + userlist.clear(); + respond(client, "Userliste geleert.\n"); + } + else + { + respond(client, "Userliste ist leer!\n"); + } +} + +CMD_FUNCTION_IMPL(aliaswishes) + if(aliasWishEditor == "") + { + aliasWishEditor = client.nick; + if(ircModIOList.contains(client.nick)) + respond(client, "IO-Eingabe ist während dem Aliaswünsche-Bearbeiten deaktiviert.\n", RespondType::Private); + editAliasWishes(); + } + else + respond(client, aliasWishEditor + " bearbeitet bereits die Aliase. Danke für die Bemühung.\n", RespondType::Private); +} + +CMD_FUNCTION_IMPL(newalias) + QRegExp aliasExp("^([^=]+)=(.*)$"); + if(aliasExp.exactMatch(args)) + { + const QString &scen = aliasExp.cap(2).trimmed(); + const QString &alias = aliasExp.cap(1).trimmed(); + QString scenP = scen; + if(!(scenP = scenPath(scen)).isEmpty()) + { + if(maps["AliasWishes"].contains(alias)) + { + respond(client, "Aliaswunsch für \"" + maps["AliasWishes"].value(alias) + "\" entfernt und Alias hinzugefügt!\n"); + maps["AliasWishes"].remove(alias); + } + else if(maps["Alias"].contains(alias)) + { + respond(client, "Alias für \"" + maps["Alias"][alias] + "\" überschrieben!\n"); + } + else + { + respond(client, "Alias hinzugefügt!\n"); + } + maps["Alias"][alias] = scenP; + } + else + { + respond(client, "Szenario \"" + scen + "\" wurde nicht gefunden!\n"); + } + } + else + { + respond(client, "Eingabefehler! Siehe !help newalias für mehr Informationen.\n"); + } +} + +CMD_FUNCTION_IMPL(modinfo) + respond(client, "Moderatoren sind (* ist aktiv, + verwendet IO):\n", RespondType::Private); + foreach(const QString &mod, lists["IrcModerators"]) + { + respond(client, (ircMods.contains(mod) ? QString("*") : QString(" ")) + (ircModIOList.contains(mod) ? QString("+") : QString(" ")) + " " + mod + "\n", RespondType::Private); + } +} + +CMD_FUNCTION_IMPL(io) + if(ircModIOList.contains(client.nick)) + ircModIOList.removeAll(client.nick); + else + ircModIOList.append(client.nick); +} + +CMD_FUNCTION_IMPL(pcNamePassToClonk) + ClientInfo info; + if(clients.contains(args)) + { + info = clients.value(args); + } + else + { + foreach(const QString& pcName, clients.keys()) + { + if(pcName.compare(args, Qt::CaseInsensitive) == 0) + { + info = clients.value(pcName); + break; + } + } + } + if(info == ClientInfo()) + { + bool notFound = true, ambigous = false; + ClientInfo foundClient; + foreach(const ClientInfo& client, clients) + { + if(client.nick.compare(args, Qt::CaseInsensitive) == 0) + { + if(!notFound) + { + ambigous = true; + break; + } + foundClient = client; + notFound = false; + } + } + if(ambigous) + { + respond(client, "Chat-Nickname " + args + " ist nicht eindeutig. Bitte PC-Namen verwenden.\n"); + return; + } + else if(notFound) + { + respond(client, args + " wurde nicht gefunden!\n"); + return; + } + else + { + info = foundClient; + } + } + if(cmd == "kick" && clientUserType(info) > userType) + { + respond(client, "Moderatoren können nur von anderen Moderatoren gekickt werden.\n"); + return; + } + writeToServer("/" + cmd + " " + info.pcName + "\n"); +} + +CMD_FUNCTION_IMPL(ircadmin) + ircCheckUserStatus(client, ClientInfo::ircClient(args), &CRSM::ircSetAdmin); +} + +CMD_FUNCTION_IMPL(ingameadmin) + setIngameAdmin(client, args); +} + +CMD_FUNCTION_IMPL(noadmin) + ircAdmin = ClientInfo(); + sessionAdmin = ClientInfo(); + respond(client, "Rundenadmin wurde freigegeben.\n"); +} + +CMD_FUNCTION_IMPL(clientlist) + QString response = "Verbundene Clients (Chatnick (PC-Name)): "; + foreach(const ClientInfo& connectedClient, clients) + { + if(connectedClient.nick.isEmpty()) + continue; + response += connectedClient.toString() + ", "; + } + response = response.left(response.length() - 2); + respond(client, response + "\n"); +} + +CMD_FUNCTION_IMPL(set) + QString what = args.left(args.indexOf(QRegExp("\\s"))).trimmed(); + QString newArgs = args.right(args.length() - args.indexOf(QRegExp("\\s"))).trimmed(); + + if(what == "teamcolors" || what == "runtimejoin" || what == "faircrew") + { + if(newArgs != "on" && newArgs != "off") + { + respond(client, what + " kann nur auf on oder off gesetzt werden.\n"); + } + else + { + writeToServer("/set " + what + " " + newArgs + "\n"); + } + } + else if(what == "maxplayer") + { + bool ok; + int max = newArgs.toInt(&ok); + if(!ok) + { + respond(client, "maxplayer muss eine Zahl sein!\n"); + } + else + { + writeToServer("/set maxplayer " + QString::number(max) + "\n"); + } + } + else if(what == "password") + { + writeToServer("/set password " + newArgs + "\n"); + } + else if(what == "comment") + { + if(userType < Moderator) + { + rightsFailMessage(client, Moderator); + } + else + { + writeToServer("/set comment " + newArgs + "\n"); + } + } + else + { + respond(client, "Unbekannte Option: \"" + what + "\"\n"); + } +} + +CMD_FUNCTION_IMPL(join) + QStringList argList = args.split(' '); + static QRegExp chanExp("^#[^\\s]+$"); + if(argList.size() < 1 || argList.first().isEmpty()) + { + respond(client, "Kein Channel angegeben!\n"); + } + else if(chanExp.exactMatch(argList.first())) + { + QString chan = argList.first(); + argList.removeFirst(); + connection->sendCommand(IrcCommand::createJoin(chan, argList.join(' '))); + } + else + { + respond(client, "\"" + argList.first() + "\" ist kein gültiger Channel-Name.\n"); + } +} + +CMD_FUNCTION_IMPL(leave) + QStringList argList = args.split(' '); + QString chan, reason; + if(!argList.isEmpty()) + { + chan = argList.first(); + argList.removeFirst(); + } + if(chan.isEmpty()) + { + chan = client.target; + } + reason = argList.join(' '); + if(reason.isEmpty()) + { + reason = client.toString(); + } + static QRegExp chanExp("^#[^\\s]+$"); + if(chanExp.exactMatch(chan)) + { + connection->sendCommand(IrcCommand::createPart(chan, reason)); + } + else + { + respond(client, "\"" + chan + "\" ist kein gültiger Channel-Name.\n"); + } +} + +CMD_FUNCTION_IMPL(exit) + processManager->closeProgFifos(); + settings.remove("ReattachId"); + finish = true; + if(session["hosting"] != "true") + scenarioFinished(); +} + +CMD_FUNCTION_IMPL(exitDetach) + settings["ReattachId"] = processManager->ID(); + writeConfig(); + QCoreApplication::quit(); +} + +CMD_FUNCTION_IMPL(exitUpdate) + writeToServer("Der Server Manager wird upgedatet. Befehle funktionieren temporär nicht.\n"); + exitDetach(cmd, args, client, userType); +} + +CMD_FUNCTION_IMPL(exitAfter) + if(processManager->isRunning()) + { + finish = true; + respond(client, "Beende nach dieser runde.\n"); + } + else + { + respond(client, "Es läuft gerade kein Spiel, beende sofort.\n"); + exit(cmd, args, client, userType); + } +} + +CMD_FUNCTION_IMPL(reload) + out("Reloading configuration..."); + ircModChecks.clear(); + ircMods.clear(); + ircModFifosOld.clear(); + ircModIOList.clear(); + ircStatusFifos.clear(); + readConfig(); + readScenarios(); + out("Configuration reloaded."); +} + +CMD_FUNCTION_IMPL(saveConfig) + out("Saving Configuration..."); + writeConfig(); + out("Configuration saved."); +} + +CMD_FUNCTION_IMPL(reconnectIrc) + respond(client, "Reconnecting...\n"); + connection->close(); + delete connection; + prepareAndConnectIrc(); +} + +IRC_CHECK_CALLBACK_IMPL(ircSetAdmin) + if(status <= 0) + { + respond(requester, "IRC-Nutzer " + subject.toString() + " wurde nicht gefunden!\n"); + } + else + { + ircAdmin = subject; + respond(requester, subject.toString() + " wurde als Rundenadmin eingetragen.\n"); + } +} + +IRC_CHECK_CALLBACK_IMPL(ircModCmd) + if(status == 3 && lists["IrcModerators"].contains(subject.nick)) + { + ircMods.append(subject.nick); + QList<QPair<CmdFunctionRef, QString>> &fifo = ircModFifos[subject.nick]; + while(fifo.size() > 0) + { + (this->*fifo.first().first.func)(fifo.first().first.name, fifo.first().second, subject, clientUserType(subject)); + fifo.removeFirst(); + } + if(!maps["AliasWishes"].isEmpty()) + { + respond(subject, QString::number(maps["AliasWishes"].size()) + " neue" + (maps["AliasWishes"].size() > 1 ? QString("") : QString("r")) + " Aliasw" + (maps["AliasWishes"].size() > 1 ? QString("ü") : QString("u")) + "nsch" + (maps["AliasWishes"].size() > 1 ? QString("e") : QString("")) + " " + (maps["AliasWishes"].size() > 1 ? QString("sind") : QString("ist")) + " verfügbar.", RespondType::PrivateNotice); + respond(subject, "Bitte bei nächster Gelegenheit mit " CMD_SIGN "aliaswishes bearbeiten.", RespondType::PrivateNotice); + } + } + else + { + if(ircModFifos.contains(subject.nick)) + { + rightsFailMessage(subject, ircModFifos[subject.nick].first().first.userType); + ircModFifos.remove(subject.nick); + } + } +} |
