summaryrefslogtreecommitdiffstats
path: root/src/crsm.cpp
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2015-09-09 19:00:56 +0200
committerMarkus Mittendrein <git@maxmitti.tk>2015-09-09 19:02:23 +0200
commit8a6d4b06f2291c363f3dea17837ed20893852453 (patch)
treec091375499e35eaa1810586454e0834c06e6c9b2 /src/crsm.cpp
parentf554a27046f203e56a07baaf214d90834942e3f5 (diff)
downloadmanager-8a6d4b06f2291c363f3dea17837ed20893852453.tar.gz
manager-8a6d4b06f2291c363f3dea17837ed20893852453.zip
Cleanup repo with some directories
Diffstat (limited to 'src/crsm.cpp')
-rw-r--r--src/crsm.cpp2184
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 &param)
+{
+ 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);
+ }
+ }
+}