diff options
| -rw-r--r-- | ClientInfo.hpp | 54 | ||||
| -rw-r--r-- | CmdFunctionRef.hpp | 45 | ||||
| -rw-r--r-- | CrServerManager.pro | 5 | ||||
| -rw-r--r-- | crsm.cpp | 1218 | ||||
| -rw-r--r-- | crsm.hpp (renamed from crsm.h) | 65 | ||||
| -rw-r--r-- | main.cpp | 2 |
6 files changed, 831 insertions, 558 deletions
diff --git a/ClientInfo.hpp b/ClientInfo.hpp index 61e1b34..f3def0e 100644 --- a/ClientInfo.hpp +++ b/ClientInfo.hpp @@ -2,14 +2,60 @@ #include <QString> -struct ClientInfo +enum ClientInterface { + Clonk = 1, + IRC = 1 << 1 +}; + +class ClientInfo { - int CUID = 0; + +public: + ClientInterface interface = Clonk; QString nick = ""; + + int CUID = 0; QString pcName = ""; bool activated = false; - bool operator==(const ClientInfo& other) + + QString target = ""; + + static inline ClientInfo ircClient(QString nick, QString target = "") + { + ClientInfo ret; + ret.interface = IRC; + ret.nick = nick; + if(target.isEmpty()) + { + target = nick; + } + ret.target = target; + return ret; + } + + static inline ClientInfo clonkClient(QString nick, QString pcName, int CUID, bool activated = false) + { + ClientInfo ret; + ret.interface = Clonk; + ret.nick = nick; + ret.nick = pcName; + ret.CUID = CUID; + ret.activated = activated; + return ret; + } + + inline bool operator==(const ClientInfo& other) + { + return other.interface == interface && other.nick == nick && (interface == Clonk ? other.pcName == pcName && other.CUID == CUID : true); + } + + inline bool operator!=(const ClientInfo& other) + { + return !operator==(other); + } + + inline QString toString() const { - return other.nick == nick && other.pcName == pcName && other.CUID == CUID; + return (!nick.isEmpty() ? nick + (interface == Clonk ? " (" + pcName + ")" : " (IRC)") : ""); } }; diff --git a/CmdFunctionRef.hpp b/CmdFunctionRef.hpp new file mode 100644 index 0000000..586090a --- /dev/null +++ b/CmdFunctionRef.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <QString> +#include <QMap> +#include <ClientInfo.hpp> + +class CRSM; + +#define CMD_FUNCTION(name) void name(const QString& cmd, const QString& args, const ClientInfo& client, UserType userTypeModerator) +#define CMD_FUNCTION_IMPL(name) void CRSM::name(const QString& cmd, const QString& args, const ClientInfo& client, UserType userType) + +enum UserType { + User = 0, + Admin = 1, + Moderator = 2 +}; + +typedef void (CRSM::*CmdFunction)(const QString&, const QString&, const ClientInfo&, UserType); + +const QMap<UserType, QString> userTypeStrings { + {User, "Benutzer"}, + {Admin, "Rundenadmin oder Moderator"}, + {Moderator, "Moderator"} +}; + +struct CmdFunctionRef +{ + CmdFunction func = nullptr; + int interfaces = Clonk | IRC; + UserType userType = User; + QString shortDescription = ""; + QString longDescription = ""; + QString name = ""; + QString argList = ""; + + CmdFunctionRef(QString name, CmdFunction func, int interfaces, UserType userType, const QString& shortDescription = "", QString argList = "", const QString &longDescription = "") : func(func), interfaces(interfaces), userType(userType), shortDescription(shortDescription), longDescription(longDescription), name(name), argList(argList) + { + + } + + CmdFunctionRef() + { + + } +}; diff --git a/CrServerManager.pro b/CrServerManager.pro index da55d93..40f76a6 100644 --- a/CrServerManager.pro +++ b/CrServerManager.pro @@ -21,8 +21,9 @@ SOURCES += main.cpp \ crsm.cpp HEADERS += \ - crsm.h \ - ClientInfo.hpp + CmdFunctionRef.hpp \ + ClientInfo.hpp \ + crsm.hpp equals(QT_ARCH, "x86_64"):linux-*: DEFINES += Q_OS_LINUX64 QMAKE_CXXFLAGS *= -std=c++11 @@ -1,4 +1,4 @@ -#include "crsm.h" +#include "crsm.hpp" #include <iostream> #include <unistd.h> #include <sys/types.h> @@ -17,7 +17,7 @@ CRSM::CRSM(QObject *parent) : qin = new QTextStream(stdin, QIODevice::ReadOnly); qout->setCodec(QTextCodec::codecForName("UTF-8")); - serverprocess=new QProcess; + serverprocess = new QProcess; args << "/fullscreen" << "/config:config" << "/lobby:60" << "/nosignup"; current = 0; finish = false; @@ -27,26 +27,20 @@ CRSM::CRSM(QObject *parent) : listC4Folders(); readScenarios(); + setupCmds(); + connect(&greetMapper, SIGNAL(mapped(QString)), this, SLOT(greet(QString))); autoHost = settings["AutoHost"] == "true"; - //QString fifopath(settings["ClonkDirectory"]+"Clonk.log"); - //mkfifo(fifopath.toUtf8(),0666); - - connect(serverprocess, SIGNAL(readyReadStandardOutput()), this, SLOT(readServerOutput())); + connect(serverprocess, SIGNAL(readyRead()), this, SLOT(readServerOutput())); connect(serverprocess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError())); connect(serverprocess, SIGNAL(finished(int)), this, SLOT(scenarioFinished())); - //int fp=open(fifopath.toUtf8(),O_RDONLY|O_NONBLOCK); - //logfile=new QFile(); - //logfile->open(fp,QIODevice::ReadOnly); - //QSocketNotifier *logNotifier = new QSocketNotifier(logfile->handle(),QSocketNotifier::Read,this); QSocketNotifier *inNotifier = new QSocketNotifier(STDIN_FILENO,QSocketNotifier::Read,this); connect(inNotifier, SIGNAL(activated(int)), this, SLOT(readInput())); - //connect(logNotifier,SIGNAL(activated(int)),this, SLOT(readLog())); QFile *reallog = new QFile(settings["ClonkDirectory"]+"CRSM.log"); - reallog->open(QIODevice::Append|QIODevice::Text); + reallog->open(QIODevice::Append | QIODevice::Text); logstream.setDevice(reallog); if(settings["UseIrc"] == "true") @@ -90,9 +84,6 @@ void CRSM::start() void CRSM::readServerOutput() { QRegExp timeRemover("^>?\\s*\\[\\d\\d:\\d\\d:\\d\\d\\]\\s+(.*)$"); - //QString what(codec->toUnicode(serverprocess->readAll())); - //QString what(QTextCodec::codecForName("Windows-1250")->toUnicode(serverprocess->readAll())); - //QString what = QString::fromLatin1(serverprocess->readAll()); QString what(serverprocess->readAll()); if(settings["ServerUsesReadline"] == "true") @@ -123,10 +114,8 @@ void CRSM::readServerOutput() *qout << what.trimmed() << endl; if(!timeRemover.exactMatch(what)) return; - what=timeRemover.cap(1).trimmed(); + what = timeRemover.cap(1).trimmed(); - //*qout << what << endl; - //QRegExp userexp("^<([^>]*)>\\s+(.*)"); QRegExp userexp("^>?\\s*<(.*)\\|(\\d+)\\|([^>]*)>\\s+(.*)$"); if(userexp.exactMatch(what)) { @@ -134,197 +123,19 @@ void CRSM::readServerOutput() QString pcName = userexp.cap(1); QString cuid = userexp.cap(2); ClientInfo& info = clients[pcName]; - if(user!=settings["ServerNick"]) + if(user != settings["ServerNick"]) { QString msg = userexp.cap(4).trimmed(); QRegExp commandExp("^\\" CMD_SIGN "([^ ]+)(\\s+(.*)\\s*)?$"); if(commandExp.exactMatch(msg)) { - QString command=commandExp.cap(1).trimmed(); - QString commandArgs=commandExp.cap(2).trimmed(); - if(command=="help") - writeToServer(QString(CMD_SIGN "host <[Rundenordner.c4f/]Szenarioname.c4s> - Startet das gewählte Szenario, solange die Warteliste nicht zu groß ist.\n" - CMD_SIGN "queue - Zeigt die nächsten %1 Szenarien auf der Warteliste.\n" - CMD_SIGN "list [Aliase¦Rundenordner[.c4f]] - Listet alle definierten Aliase oder alle möglichen Szenarien und Ordner auf, bzw. alle Szenarien im Ordner oder Rundenordner\n" - CMD_SIGN "admin [Chatnick oder PC-Name] - 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.\n" - CMD_SIGN "teamdist <none¦free¦host¦random¦randominv> - Ändert die Teamverteilung.\n" - CMD_SIGN "plrteam <Spielername> <Teamname> - Verschiebt den angegebenen Spieler ins angegebene Team.\n" - CMD_SIGN "aliaswish <Alias> = <Szenario> - Deponiert den Wunsch, <Alias> als Alias für <Szenario> einzutragen. Ein Moderator entscheidet darüber.\n" - CMD_SIGN "help - Zeigt diese Hilfe an.\n" - + (settings["IrcUseIngameChat"] == "true" ? QString(CMD_SIGN "ircchat <on¦off> - Schaltet den Irc-Ingame-Chat ein bzw. aus.\n") : QString("")) + - "Szenarionamen sind Case-Sensitive (Groß- Kleinschreibung beachten)!\n" - "Der Rundenadmin kann alle Clonk-Befehle mit " CMD_SIGN " statt / ausführen. Bspw.: " CMD_SIGN "kick Client-123\n" - "Zusätzlich zu den bekannten Clonk-Befehlen gibt es noch: stop, pause, unpause, teamcolorson, teamcolorsoff.\n" - "Ein Rundenadmin kann gegebenfalls einen anderen Spieler als Rundenadmin eintragen.\n" - "[Sachen in eckigen Klammern sind optional]\n" - "<Sachen in spitzen Klammern sind benoetigte Argumente>\n" - "Von ¦ getrennten Werten ist einer auszuwählen.\n" - "Weitere Befehle folgen.\n").arg(settings["UserListLength"])); - if(command=="host") + QString command = commandExp.cap(1).trimmed(); + QString commandArgs = commandExp.cap(2).trimmed(); + if(!cmd(command, commandArgs, info)) { - if(userlist.length()>=settings["UserListLength"].toInt()) - writeToServer(QString("Maximale Warteschlangenlänge von "+settings["UserListLength"]+" erreicht!\n")); - else - { - if(commandArgs=="") - writeToServer("Bitte gib einen Szenarionamen an!\n"); - else - { - //extractC4Folders(); - if(scenExists(commandArgs)) - { - userlist << commandArgs; - writeToServer("Szenario "+commandArgs+" wurde der Warteschlange hinzugefügt.\n"); - if(userlist.length() == 1 && session["userwish"] != "true" && session["running"] != "true") - writeToServer("Überrede alle Spieler zu leaven und dein Wunsch wird sofort gehostet ;-)\n"); - } - else - { - writeToServer(QString("Szenario \"" + commandArgs + "\" wurde nicht gefunden!\n")); - } - } - } - } - - if(command=="admin") - { - if(commandArgs!="") - { - if(commandArgs == settings["ServerNick"] || commandArgs == settings["ServerPCName"]) - { - writeToServer("Der Server kann nicht als Rundenadmin eingetragen werden!\n"); - return; - } - if(commandArgs == sessionAdmin.pcName) - { - writeToServer(QString(sessionAdmin.nick + " (" + sessionAdmin.pcName + ") ist bereits Rundenadmin!\n")); - return; - } - if(sessionAdmin == ClientInfo() || info == sessionAdmin || lists["Moderators"].contains(cuid)) - { - if(clients.contains(commandArgs)) - { - sessionAdmin = clients.value(commandArgs); - } - else - { - bool notFound = true, ambigous = false; - ClientInfo foundClient; - foreach(const ClientInfo& client, clients) - { - if(client.nick == commandArgs) - { - if(!notFound) - { - ambigous = true; - break; - } - foundClient = client; - notFound = false; - } - } - if(ambigous) - { - writeToServer(QString("Chat-Nickname " + commandArgs + " ist nicht eindeutig. Bitte PC-Namen verwenden.\n")); - return; - } - else if(notFound) - { - writeToServer(QString("Spieler " + commandArgs + " wurde nicht gefunden!\n")); - return; - } - else if(sessionAdmin == foundClient) - { - writeToServer(QString(sessionAdmin.nick + " (" + sessionAdmin.pcName + ") ist bereits Rundenadmin!\n")); - return; - } - else - { - sessionAdmin = foundClient; - } - } - writeToServer(QString("Spieler " + sessionAdmin.nick + " (" + sessionAdmin.pcName + ") wurde als Rundenadmin eingetragen.\n")); - } - else - { - writeToServer(QString(sessionAdmin.nick + " (" + sessionAdmin.pcName + ") ist bereits Rundenadmin!\n")); - } - } - else - { - if(sessionAdmin == info) - { - writeToServer(QString("Du bist bereits Rundenadmin!\n")); - } - else if(sessionAdmin == ClientInfo() || lists["Moderators"].contains(cuid)) - { - sessionAdmin = info; - writeToServer(QString("Spieler " + info.nick + " (" + info.pcName + ") wurde als Rundenadmin eingetragen!\n")); - } - else - writeToServer(QString(sessionAdmin.nick + " (" + sessionAdmin.pcName + ") ist bereits Rundenadmin!\n")); - } + respond(info, "Unbekannter Befehl: \"" + command + "\"!\n"); } - /*if(command=="chatadmin") - { - if(commandArgs!="") - { - if(session["chatadmin"]==""||user==session["chatadmin"] || lists["Moderators"].contains(user)) - { - session["chatadmin"]=commandArgs.trimmed(); - writeToServer(QString("Spieler "+commandArgs+" wurde als Chat-Rundenadmin eingetragen.\n")); - } - else - { - writeToServer(QString(session["chatadmin"]+" ist bereits Chat-Rundenadmin!\n")); - } - } - else - { - if(session["chatadmin"]=="") - { - writeToServer(QString(user + " wurde als Chat-Rundenadmin eingetragen.\n")); - } - else - writeToServer(QString(session["admin"]+" ist Chat-Rundenadmin!\n")); - } - }*/ - - else if((command=="observer"||command=="deactivate"||command=="activate"||command=="kick"||command=="set"||command=="script"||command=="asyncctrl"||command=="centralctrl"||command=="decentralctrl"||command=="start"||command=="nodebug"||command=="stop"||command=="pause"||command=="unpause"||command=="teamcolorsoff"||command=="teamcolorson"||command=="teamdist"||command=="plrteam")&&(sessionAdmin == info || lists["Moderators"].contains(cuid))) - { - if(command=="kick") - { - if(clients.contains(commandArgs)) - { - if(lists["Moderators"].contains(QString::number(clients.value(commandArgs).CUID)) && !lists["Moderators"].contains(cuid)) - { - writeToServer(QString("Moderatoren können nicht gekickt werden!\n")); - return; - } - } - } - if(!(command=="set"&&(commandArgs.simplified()=="faircrew on"||commandArgs.simplified()=="faircrew off")&&session["running"]!="true")) - writeToServer(QString("/"+command+" "+commandArgs+"\n")); - } - - else if(command=="queue") - { - writeToServer(printQueue()); - } - - else if(command=="list") - { - writeToServer(listScenarios(commandArgs)); - } - else if(command=="aliaswish") - { - writeToServer(addAliasWish(commandArgs) + "\n"); - } - else if(command=="ircchat"&&(sessionAdmin == info || lists["Moderators"].contains(cuid))) - { - writeToServer(ircActivateIngameChat(commandArgs.toLower() == "on") + "\n"); - } } else if(session["IrcUseIngameChat"] == "true") { @@ -332,12 +143,9 @@ void CRSM::readServerOutput() } } } - QRegExp joinExp("^Client (.+) (?:verbunden|connected)\\.\\s*$"); - //joinExp.setMinimal(true); if(joinExp.exactMatch(what)) { - //waitinggreets << joinExp.cap(1); QRegExp infoMatch("^<(.*)\\|(\\d+)\\|(.*)>$"); if(infoMatch.exactMatch(joinExp.cap(1))) { @@ -345,6 +153,7 @@ void CRSM::readServerOutput() info.CUID = infoMatch.cap(2).toInt(); info.nick = infoMatch.cap(3); info.pcName = infoMatch.cap(1); + info.interface = Clonk; clients.insert(info.pcName, info); QTimer *timer = new QTimer; connect(timer, SIGNAL(timeout()), &greetMapper, SLOT(map())); @@ -379,12 +188,11 @@ void CRSM::readServerOutput() if(startExp.exactMatch(what)) { writeToServer(QString("/set maxplayer 0\n")); - session["running"]="true"; + session["running"] = "true"; } QRegExp leaveExp("^Client (.+) (?:entfernt|removed)(.*)"); - //leaveExp.setMinimal(true); if(leaveExp.exactMatch(what)) { QRegExp infoMatch("^<(.*)\\|(\\d+)\\|(.*)>$"); @@ -409,27 +217,27 @@ void CRSM::processError() void CRSM::readInput() { QString what(qin->readLine()); - if(what=="/exit") + if(what == "/exit") { serverprocess->closeWriteChannel(); - finish=true; + finish = true; cleanUp(); if(session["hosting"] != "true") scenarioFinished(); return; } - if(what=="/exitafter") + if(what == "/exitafter") { - finish=true; + finish = true; *qout << "Will exit after this round." << endl; return; } - if(what=="/next") + if(what == "/next") { skipCurrent(); return; } - if(what=="/skip") + if(what == "/skip") { QString skipped; if((skipped = skipScen()) != "") @@ -441,19 +249,21 @@ void CRSM::readInput() return; } - if(what=="/reload") + if(what == "/reload") { *qout << "Reloading config..." << endl; lists.clear(); settings.clear(); ircModChecks.clear(); ircMods.clear(); + ircModFifosOld.clear(); ircModIOList.clear(); + ircStatusFifos.clear(); readConfig(); return; } - if(what=="/help") printAdditionalHelp(); + if(what == "/help") printAdditionalHelp(); writeToServer(what+"\n"); } @@ -469,7 +279,7 @@ void CRSM::nextScen() else { startScen(scenlist.at(current), args); - if(++current>=scenlist.length()) current = 0; + if(++current >= scenlist.length()) current = 0; } } @@ -486,7 +296,7 @@ void CRSM::printAdditionalHelp() void CRSM::readLog() { QString temp(logfile->readLine().trimmed()); - if(temp!="") logstream << temp << endl; + if(temp != "") logstream << temp << endl; } void CRSM::scenarioFinished() @@ -506,6 +316,7 @@ void CRSM::scenarioFinished() } session.clear(); sessionAdmin = ClientInfo(); + ircAdmin = ClientInfo(); clients.clear(); greeted.clear(); clientcount = 0; @@ -539,17 +350,13 @@ void CRSM::ircMessageReceived(IrcMessage *message) { QString statusNick = split.at(1); int status = split.at(2).toInt(); - if(ircModChecks.contains(statusNick)) + if(ircStatusFifos.contains(statusNick)) { - ircModChecks.removeAll(statusNick); - if(status == 3 && lists["IrcModerators"].contains(statusNick)) - { - ircMods.append(statusNick); - ircModJoined(statusNick); - } - else + QList<QPair<ClientInfo, IrcCheckCallback>> &fifo = ircStatusFifos[statusNick]; + while(fifo.size() > 0) { - ircModFifos.remove(statusNick); + (this->*fifo.first().second)(fifo.first().first, status, ClientInfo::ircClient(statusNick)); + fifo.removeFirst(); } } } @@ -568,111 +375,13 @@ void CRSM::ircMessageReceived(IrcMessage *message) 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 cmd = args.first().trimmed(); + QString command = args.first().trimmed(); args.removeFirst(); QString arg = args.join(" ").trimmed(); - if(cmd == "list") - { - /*foreach(const QString &line, listScenarios(arg).split('\n')) - connection->sendCommand(IrcCommand::createMessage(message->nick(), line));*/ - connection->sendCommand(IrcCommand::createMessage(target, settings["IrcScenListMessage"]/*"Szenarioliste ist zurzeit nur in der Lobby und im laufenden Spiel verfügbar."*/)); - } - else if(cmd == "autohost") - { - ircCheckModCmd(message->nick(), IrcModOperations::Autohost); - } - else if(cmd == "noautohost") - { - ircCheckModCmd(message->nick(), IrcModOperations::NoAutohost); - } - else if(cmd=="host") - { - if(userlist.length()>=settings["UserListLength"].toInt()) - connection->sendCommand(IrcCommand::createMessage(target, "Maximale Warteschlangenlänge von "+settings["UserListLength"]+" erreicht!")); - else - { - if(arg=="") - connection->sendCommand(IrcCommand::createMessage(target, "Bitte gib einen Szenarionamen an!")); - else - { - if(scenExists(arg)) - { - bool skipCurrent = false; - if(userlist.isEmpty() && session["userwish"] != "true") - skipCurrent = true; - userlist << arg; - connection->sendCommand(IrcCommand::createMessage(target, "Szenario " + arg + " wurde der Warteschlange hinzugefügt.")); - if(session["hosting"] != "true") - nextScen(); - else if(clientcount == 0 && skipCurrent) - { - serverprocess->closeWriteChannel(); - } - } - else - { - connection->sendCommand(IrcCommand::createMessage(target, "Szenario \"" + arg + "\" wurde nicht gefunden!")); - } - } - } - } - else if(cmd=="queue") - { - foreach(const QString &line, printQueue().split('\n')) - connection->sendCommand(IrcCommand::createNotice(target, line)); - } - else if(cmd=="help") - { - connection->sendCommand(IrcCommand::createNotice(target, CMD_SIGN "list - Zeigt Möglichkeiten zur Betrachtung einer Szenarienliste an.")); - connection->sendCommand(IrcCommand::createNotice(target, CMD_SIGN "host <[Rundenordner.c4f/]Szenarioname.c4s> - Startet das gewählte Szenario, solange die Warteliste nicht zu groß ist.")); - connection->sendCommand(IrcCommand::createNotice(target, QString(CMD_SIGN "queue - Zeigt die nächsten %1 Szenarien auf der Warteliste.").arg(settings["UserListLength"]))); - connection->sendCommand(IrcCommand::createNotice(target, CMD_SIGN "aliaswish <Alias> = <Szenario> - Deponiert den Wunsch, <Alias> als Alias für <Szenario> einzutragen. Ein Moderator entscheidet darüber.")); - connection->sendCommand(IrcCommand::createNotice(target, CMD_SIGN "help - Zeigt diese Hilfe an.")); - ircCheckModCmd(message->nick(), ModHelp); - } - else if(cmd=="moderatorcheck") - { - ircCheckModCmd(message->nick(), IrcModOperations::ModCheck); - } - else if(cmd=="modinfo") - { - ircCheckModCmd(message->nick(), IrcModOperations::ModInfo); - } - else if(cmd=="skip") - { - ircCheckModCmd(message->nick(), IrcModOperations::SkipScen); - } - else if(cmd=="clear") - { - ircCheckModCmd(message->nick(), IrcModOperations::ClearUserList); - } - else if(cmd=="next") - { - ircCheckModCmd(message->nick(), IrcModOperations::SkipCurrentScen); - } - else if(cmd=="kill") - { - ircCheckModCmd(message->nick(), IrcModOperations::Kill); - } - else if(cmd=="io") + const ClientInfo& client = ClientInfo::ircClient(message->nick(), target); + if(!cmd(command, arg, client)) { - ircCheckModCmd(message->nick(), IrcModOperations::IO); - } - else if(cmd=="newalias") - { - ircCheckModCmd(message->nick(), IrcModOperations::NewAlias, arg); - } - else if(cmd=="aliaswish") - { - connection->sendCommand(IrcCommand::createMessage(target, addAliasWish(arg))); - } - else if(cmd=="aliaswishes") - { - ircCheckModCmd(message->nick(), IrcModOperations::AliasWishes); - } - else if(cmd=="ingamechat") - { - ircCheckModCmd(message->nick(), IrcModOperations::IngameChat, arg); + respond(client, "Unbekannter Befehl: \"" + command + "\"!"); } } else if(message->nick() == target && ircMods.contains(target)) @@ -706,7 +415,7 @@ void CRSM::ircMessageReceived(IrcMessage *message) writeToServer("[IRC] " + message->nick() + " hat den Channel betreten." + "\n"); } else - ircCheckModCmd(message->nick(), IrcModOperations::CheckOnly); + ircCheckUserStatus(ClientInfo::ircClient(message->nick()), ClientInfo::ircClient(message->nick()), &CRSM::ircModCmd); } else if(message->type() == IrcMessage::Quit) { @@ -785,7 +494,7 @@ void CRSM::startScen(QString scen, QStringList argList) void CRSM::readConfig() { QFile config(CONFIG_FILE_NAME); - if(!config.exists()||!config.open(QIODevice::ReadOnly | QIODevice::Text)) + if(!config.exists() || !config.open(QIODevice::ReadOnly | QIODevice::Text)) { config.open(QIODevice::WriteOnly | QIODevice::Text); settings = defaultSettings(); @@ -802,7 +511,7 @@ void CRSM::readConfig() QString line; for(;;) { - line=config.readLine().trimmed(); + line = config.readLine().trimmed(); if(confPlusExp.exactMatch(line)) { lists[confPlusExp.cap(1).trimmed()].push_back(confPlusExp.cap(2).trimmed()); @@ -843,10 +552,9 @@ void CRSM::readConfig() } } *qout << endl; - args=settings["Arguments"].split(" "); + args = settings["Arguments"].split(" "); args << "/config:"+settings["ClonkConfig"]; } - //settings["ClonkDirectory"]= QDir().absoluteFilePath(settings["ServerExecutable"]).replace(QRegExp("^(.*)"+QFileInfo(settings["ServerExecutable"]).fileName()+"$"),"\\1"); settings["ClonkDirectory"] = QFileInfo(settings["ServerExecutable"]).absoluteDir().absolutePath()+QDir::separator(); QFile clonkconfig(settings["ClonkConfig"]); @@ -859,15 +567,14 @@ void CRSM::readConfig() QRegExp pcNameExp("^\\s*LocalName=\"(.*)\"\\s*$"); foreach(const QString &line, QString(clonkconfig.readAll().trimmed()).split("\n")) { - //*qout << line << endl; if(nickExp.exactMatch(line)) { - settings["ServerNick"]=nickExp.cap(1); + settings["ServerNick"] = nickExp.cap(1); break; } else if(pcNameExp.exactMatch(line)) { - settings["ServerPCName"]=pcNameExp.cap(1); + settings["ServerPCName"] = pcNameExp.cap(1); break; } } @@ -916,31 +623,9 @@ QMap<QString, QString> CRSM::defaultSettings() void CRSM::listC4Folders() { - /*// *qout << "Executable Setting: " << settings["ServerExecutable"] << endl; - // *qout << "Path: " << clonkdir << endl; - QDirIterator it(settings["ClonkDirectory"], QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); - bool extracting=false; - for(;it.hasNext();it.next()) - { - if(it.fileInfo().suffix()=="c4f"&&!it.fileInfo().isDir()&&!it.fileInfo().absoluteFilePath().contains(settings["ClonkDirectory"]+"Network/")) - { - if(!extracting) - { - extracting=true; - *qout << endl << "Extracting scenario folders for hosting through Lobby..." << endl; - } - *qout << it.fileInfo().absoluteFilePath().replace(settings["ClonkDirectory"],"") << "..." << endl; - QProcess c4group(this); - QStringList c4gargs; - c4gargs << it.fileInfo().absoluteFilePath() << "-u"; - c4group.start(settings["ClonkDirectory"]+"c4group",c4gargs); - c4group.waitForFinished(); - } - } - if(extracting) *qout << endl;*/ //Old extracting is now replaced by listing the scenarios with c4group *qout << "Listing Contents of C4Folders..."; QDirIterator it(settings["ClonkDirectory"], QDirIterator::FollowSymlinks); - for(;it.hasNext();it.next()) + for(; it.hasNext(); it.next()) { if(it.fileName() == ".." || it.fileName() == ".") continue; @@ -966,13 +651,8 @@ void CRSM::cleanUp() { *qout << endl << "Cleaning up Clonk Folder..." << endl; QDirIterator it(settings["ClonkDirectory"]+"Network/", QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); - for(;it.hasNext();it.next()) + for(; it.hasNext(); it.next()) if(it.fileInfo().exists()) QFile(it.fileInfo().absoluteFilePath()).remove(); - /*QDirIterator lstIt(settings["ClonkDirectory"], QStringList() << "*.lst", QDir::NoFilter, QDirIterator::FollowSymlinks); - for(;lstIt.hasNext();lstIt.next()) - if(!lists["IgnoreFolders"].contains(lstIt.fileInfo().baseName())) - QFile(lstIt.fileInfo().absoluteFilePath()).remove();*/ - *qout << endl; } @@ -1019,16 +699,16 @@ QString CRSM::listScenarios(QString commandArgs) { ret += "Folgende Szenarien stehen zur Auswahl:\n"; QDirIterator it(settings["ClonkDirectory"], QDirIterator::FollowSymlinks); - for(;it.hasNext();it.next()) + for(; it.hasNext(); it.next()) { - if(it.fileInfo().suffix()=="c4s"&&!it.fileInfo().absoluteFilePath().contains(settings["ClonkDirectory"]+"Network/")) + 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()) + for(; folderIt.hasNext(); folderIt.next()) { - if(folderIt.fileInfo().suffix()=="lst"&&!folderIt.fileInfo().absoluteFilePath().contains(settings["ClonkDirectory"]+"Network/") && !lists["IgnoreFolders"].contains(folderIt.fileInfo().baseName())) + 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"); } } @@ -1058,173 +738,33 @@ QString CRSM::listScenarios(QString commandArgs) QString CRSM::printQueue() { - QString ret("Folgende Szenarien befinden sich in der Warteschlange:\n"); - for(int i=0;i<settings["UserListLength"].toInt();i++) - { - ret += "\t"+QString::number(i+1)+". "+(userlist.length()>i?userlist.at(i):scenlist.at((i-userlist.length() + current)%scenlist.length()) + " (auto)")+"\n"; - } - return ret; -} - -/*QByteArray CRSM::toClonkFormat(const QString &str) -{ - QByteArray ret; - foreach(const QChar& c, str) + QString ret; + if(userlist.length() == 0 && !autoHost) + return "Die Warteschlange ist leer.\n"; + if(autoHost) { - if(c == QString("™")) - ret.append(c); - else - ret.append(c.toLatin1()); + ret = "Folgende Szenarien befinden sich in der Warteschlange:\n"; + for(int i = 0; i<settings["UserListLength"].toInt(); i++) + { + ret += "\t"+QString::number(i+1)+". "+(userlist.length()>i?userlist.at(i):scenlist.at((i-userlist.length() + current)%scenlist.length()) + " (auto)")+"\n"; + } } - - return ret; -}*/ - -void CRSM::ircCheckModCmd(const QString &nick, CRSM::IrcModOperations operation, QString arg) -{ - if(!lists["IrcModerators"].contains(nick)) - return; - if(ircMods.contains(nick)) - ircModOperation(nick, operation, arg); else { - if(!ircModChecks.contains(nick)) + int i = 1; + foreach(const QString& scen, userlist) { - ircModChecks.append(nick); - connection->sendCommand(IrcCommand::createMessage("NickServ", "STATUS " + nick)); + ret += "\t" + QString::number(i) + ". " + scen + "\n"; + ++i; } - ircModFifos[nick].append(qMakePair<IrcModOperations, QString>(operation, arg)); - } -} - -void CRSM::ircModOperation(const QString &nick, CRSM::IrcModOperations operation, QString arg) -{ - QString answer, skipped; - QRegExp aliasExp("^([^=]+)=(.*)$"); - switch(operation) - { - case IrcModOperations::ModCheck: - connection->sendCommand(IrcCommand::createMessage(nick, "Du bist Moderator.")); - break; - case IrcModOperations::ModInfo: - connection->sendCommand(IrcCommand::createMessage(nick, "Moderatoren sind (* ist aktiv, + verwendet IO):")); - foreach(const QString &mod, lists["IrcModerators"]) - { - connection->sendCommand(IrcCommand::createMessage(nick, (ircMods.contains(mod) ? QString("*") : QString(" ")) + (ircModIOList.contains(mod) ? QString("+") : QString(" ")) + " " + mod)); - } - break; - case IrcModOperations::SkipScen: - if((skipped = skipScen()) != "") - { - answer = "\"" + skipped + "\" übersprungen."; - } - else - answer = "Userliste ist leer!"; - connection->sendCommand(IrcCommand::createMessage(nick, answer)); - break; - case IrcModOperations::ClearUserList: - if(userlist.length() > 0) - { - userlist.clear(); - answer = "Userliste geleert."; - } - else - { - answer = "Userliste ist leer!"; - } - connection->sendCommand(IrcCommand::createMessage(nick, answer)); - break; - case IrcModOperations::SkipCurrentScen: - skipCurrent(); - connection->sendCommand(IrcCommand::createMessage(nick, "Versuche zu überspringen...")); - break; - case IrcModOperations::Autohost: - autoHost = true; - connection->sendCommand(IrcCommand::createMessage(nick, "Automatisches Hosting aktiviert.")); - if(session["hosting"] != "true") - { - nextScen(); - } - break; - case IrcModOperations::NoAutohost: - autoHost = false; - connection->sendCommand(IrcCommand::createMessage(nick, "Automatisches Hosting deaktiviert.")); - break; - case IrcModOperations::ModHelp: - connection->sendCommand(IrcCommand::createMessage(nick, "Moderatorbefehle:")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "next - Schließt stdin vom Clonk-Server, vorausgesetzt es läuft einer. Wenn dieser ordnungsgemäß läuft beendet er sich.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "kill - Tötet den Clonk-Server, vorausgesetzt es läuft einer. Bitte nur verwenden wenn " CMD_SIGN "next nicht funktioniert.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "skip - Entfernt das nächste Szenario aus der Wunschliste.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "clear - Löscht die Wunschliste.")); - if(settings["IrcUseIngameChat"] == "true") - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "ingamechat <on¦off> - Schaltet den Irc-Ingame-Chat ein bzw. aus.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "modinfo - Listet alle Moderatoren auf.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "io - 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 /.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "newalias <Alias> = <Szenario> - Trägt <Alias> als Alias für <Szenario> ein.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "aliaswishes - Zum geführten Bearbeiten der Aliaswünsche.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "autohost - Schaltet Autohosting ein.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "noautohost - Schaltet Autohosting aus.")); - connection->sendCommand(IrcCommand::createMessage(nick, CMD_SIGN "aliaswishes - Zum geführten Bearbeiten der Aliaswünsche.")); - break; - case IrcModOperations::Kill: - serverprocess->kill(); - connection->sendCommand(IrcCommand::createMessage(nick, "Clonk-Server wurde gekillt.")); - break; - case IrcModOperations::IO: - if(ircModIOList.contains(nick)) - ircModIOList.removeAll(nick); - else - ircModIOList.append(nick); - case IrcModOperations::CheckOnly: - break; - case IrcModOperations::NewAlias: - if(aliasExp.exactMatch(arg)) - { - const QString &scen = aliasExp.cap(2).trimmed(); - const QString &alias = aliasExp.cap(1).trimmed(); - if(scenExists(scen)) - { - if(maps["AliasWishes"].contains(alias)) - { - connection->sendCommand(IrcCommand::createMessage(nick, "Aliaswunsch für \"" + maps["AliasWishes"].value(alias) + "\" entfernt und Alias hinzugefügt!")); - maps["AliasWishes"].remove(alias); - } - else if(maps["Alias"].contains(alias)) - { - connection->sendCommand(IrcCommand::createMessage(nick, "Alias für \"" + maps["Alias"][alias] + "\" überschrieben!")); - } - else - { - connection->sendCommand(IrcCommand::createMessage(nick, "Alias hinzugefügt!")); - } - maps["Alias"][alias] = scen; - } - else - connection->sendCommand(IrcCommand::createMessage(nick, "Szenario \"" + scen + "\" wurde nicht gefunden!")); - } - else - connection->sendCommand(IrcCommand::createMessage(nick, "Eingabefehler! Siehe !help für mehr Informationen.")); - break; - case IrcModOperations::AliasWishes: - if(aliasWishEditor == "") - { - aliasWishEditor = nick; - if(ircModIOList.contains(nick)) - connection->sendCommand(IrcCommand::createMessage(nick, "IO-Eingabe ist während dem Aliaswünsche-Bearbeiten deaktiviert.")); - editAliasWishes(); - } - else - connection->sendCommand(IrcCommand::createMessage(nick, aliasWishEditor + " bearbeitet bereits die Aliase. Danke für die Bemühung.")); - break; - case IrcModOperations::IngameChat: - connection->sendCommand(IrcCommand::createMessage(nick, ircActivateIngameChat(arg.toLower() == "on"))); - break; } + return ret; } -void CRSM::ircModOperation(const QString &nick, QPair<CRSM::IrcModOperations, QString> operationArg) +void CRSM::ircCheckModCmd(const QString &nick, CmdFunctionRef func, QString arg) { - ircModOperation(nick, operationArg.first, operationArg.second); + ircModFifos[nick].append(qMakePair<CmdFunctionRef, QString>(func, arg)); + ircCheckUserStatus(ClientInfo::ircClient(nick), ClientInfo::ircClient(nick), &CRSM::ircModCmd); } QString CRSM::skipScen() @@ -1248,12 +788,48 @@ void CRSM::writeToServer(const QString &message) { if(!serverprocess->isWritable()) return; - serverprocess->write(codec->fromUnicode(message)); - if(settings["ServerUsesReadline"] == "true") + int i = 0; + QStringList split = message.split('\n', QString::KeepEmptyParts); + foreach(QString line, split) { - if(writtenToServer.length() > settings["ReadlineRereadLimit"].toInt()) - writtenToServer.clear(); - writtenToServer += codec->fromUnicode(message); + 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"; + serverprocess->write(codec->fromUnicode(linePart)); + if(settings["ServerUsesReadline"] == "true") + { + if(writtenToServer.length() > settings["ReadlineRereadLimit"].toInt()) + writtenToServer.clear(); + writtenToServer += codec->fromUnicode(linePart); + } + } + serverprocess->write(codec->fromUnicode(line)); + if(settings["ServerUsesReadline"] == "true") + { + if(writtenToServer.length() > settings["ReadlineRereadLimit"].toInt()) + writtenToServer.clear(); + writtenToServer += codec->fromUnicode(line); + } + } + ++i; } } @@ -1313,20 +889,6 @@ QString CRSM::addAliasWish(const QString ¶m) return "Eingabefehler! Siehe !help für mehr Informationen."; } -void CRSM::ircModJoined(const QString &nick) -{ - while(ircModFifos[nick].length() > 0) - { - ircModOperation(nick, ircModFifos[nick].first()); - ircModFifos[nick].removeFirst(); - } - if(!maps["AliasWishes"].isEmpty()) - { - connection->sendCommand(IrcCommand::createNotice(nick, 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.")); - connection->sendCommand(IrcCommand::createNotice(nick, "Bitte bei nächster Gelegenheit mit " CMD_SIGN "aliaswishes bearbeiten.")); - } -} - void CRSM::informModsAboutAliasWish() { foreach(const QString& mod, ircMods) @@ -1440,3 +1002,571 @@ QStringList CRSM::listC4Folder(const QString &path) } 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; + } + + 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::Private); +} + +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; + case IRC: + if(ircMods.contains(client.nick)) + return Moderator; + if(ircAdmin == client) + return Admin; + } + + 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, Admin, "Legt den Ingame-Rundenadmin fest.", "<Ingame-Nick¦PC-Name>"); + if(settings["UseIrc"] == "true") + { + addCommand("ircadmin", &CRSM::ircadmin, Clonk, 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, 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, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", "<on¦off>"); + addCommand("ingamechat", &CRSM::ircchat, IRC, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", "<on¦off>"); + } + addCommand("queue", &CRSM::queue, Clonk | IRC, User, "Zeigt die nächsten " + settings["UserListLength"] + " Szenarien auf der Warteliste."); + addCommand("host", &CRSM::host, Clonk | IRC, User, "Nimmt das angegebene Szenario in die Wunschliste auf.", "<Szenarioname¦Alias>"); + addCommand("list", &CRSM::list, Clonk | IRC, 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, Clonk | IRC, User, "Listet alle verbundenen Clients mit PC-Name und Chatnick auf."); + addCommand("help", &CRSM::help, Clonk | IRC, 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, Admin, "Stoppt einen laufenden Countdown."); + addCommand("start", &CRSM::passToClonk, Clonk | IRC, Admin, "Startet den Countdown.", "[Countdownzeit in s]"); + addCommand("teamdist", &CRSM::passToClonk, Clonk | IRC, Admin, "Ändert die Teamverteilung.", "<none¦free¦host¦random¦radnominv>"); + 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, Admin, "Pausiert das Spiel."); + addCommand("unpause", &CRSM::passToClonk, Clonk | IRC, Admin, "Setzt das pausierte Spiel fort."); + addCommand("teamcolorson", &CRSM::passToClonk, Clonk | IRC, Admin, "Schaltet Teamfarben ein."); + addCommand("teamcolorsoff", &CRSM::passToClonk, Clonk | IRC, Admin, "Schaltet Teamfarben aus."); + addCommand("observer", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Der angegebene Host muss zuschauen.", "<PC-Name¦Chatnick>"); + addCommand("deactivate", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Deaktiviert den angegebenen Host.", "<PC-Name¦Chatnick>"); + addCommand("activate", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Aktiviert den angegebenen Host.", "<PC-Name¦Chatnick>"); + addCommand("kick", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Kickt den angegebenen Host.", "<PC-Name¦Chatnick>"); + addCommand("script", &CRSM::passToClonk, Clonk | IRC, Admin, "Führt das angegebene Script aus.", "<Script>"); + addCommand("asyncctrl", &CRSM::passToClonk, Clonk | IRC, Admin, "Aktiviert den asynchronen Netzwerkmodus."); + addCommand("centralctrl", &CRSM::passToClonk, Clonk | IRC, Admin, "Aktiviert den zentralen Netzwerkmodus."); + addCommand("decentralctrl", &CRSM::passToClonk, Clonk | IRC, Admin, "Aktiviert den dezentralen Netzwerkmodus."); + addCommand("nodebug", &CRSM::passToClonk, Clonk | IRC, Admin, "Deaktiviert den Debug-Modus zwingend für alle."); + addCommand("autohost", &CRSM::autohost, Clonk | IRC, Moderator, "Deaktiviert den Debug-Modus zwingend für alle."); + addCommand("noautohost", &CRSM::autohost, Clonk | IRC, Moderator, "Deaktiviert den Debug-Modus zwingend für alle."); + + addCommand("skip", &CRSM::skip, Clonk | IRC, Moderator, "Entfernt das nächste Szenario aus der Wunschliste."); + addCommand("next", &CRSM::next, Clonk | IRC, Moderator, "Versucht das aktuelle Szenario zu überspringen."); + addCommand("kill", &CRSM::kill, IRC, 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, Moderator, "Löscht die Wunschliste."); + addCommand("aliaswishes", &CRSM::aliaswishes, IRC, Moderator, "Zum geführten Bearbeiten der Aliaswünsche."); + addCommand("newalias", &CRSM::newalias, IRC, Moderator, "Trägt <Alias> als Alias für <Szenario> ein.", "<Alias> = <Szenario>"); + addCommand("modinfo", &CRSM::modinfo, IRC, 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 /."); + +} + +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; + } +} + +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)) + { + sessionAdmin = clients.value(newAdmin); + } + 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"); + } +} + +CMD_FUNCTION_IMPL(help) +{ + bool longHelp = args == "long"; + if(args.isEmpty() || longHelp) + { + QString response = "Verfügbare Befehle: "; + UserType clientType = clientUserType(client); + foreach(const CmdFunctionRef& cmd, cmds) + { + if((cmd.interfaces & client.interface) == client.interface && clientType >= 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"); + } + 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(ircAdmin != ClientInfo() && userType < Admin) + { + respond(client, ircAdmin.toString() + (!ircAdmin.nick.isEmpty() && !sessionAdmin.nick.isEmpty() ? " und " : "") + sessionAdmin.toString() + " ist bereits Rundenadmin!\n"); + return; + } + else if(args == "") + { + 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 == "") + writeToServer("Bitte gib einen Szenarionamen an!\n"); + else + { + if(scenExists(args)) + { + userlist << args; + respond(client, "Szenario " + args + " wurde der Warteschlange hinzugefügt.\n"); + if(userlist.length() == 1 && session["userwish"] != "true" && session["running"] != "true") + respond(client, "Überrede alle Spieler zu leaven und dein Wunsch wird sofort gehostet ;-)\n"); + } + else + { + respond(client, "Szenario \"" + args + "\" wurde nicht gefunden!\n"); + } + } + } +} + +CMD_FUNCTION_IMPL(list) +{ + if(client.interface == Clonk) + { + respond(client, listScenarios(args)); + } + else if(client.interface == IRC) + { + respond(client, settings["IrcScenListMessage"]); + } +} + +CMD_FUNCTION_IMPL(queue) +{ + respond(client, printQueue(), RespondType::Notice); +} + +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) +{ + skipCurrent(); + respond(client, "Versuche zu überspringen...\n"); +} + +CMD_FUNCTION_IMPL(kill) +{ + serverprocess->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(); + if(scenExists(scen)) + { + 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] = scen; + } + 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 + { + bool notFound = true, ambigous = false; + ClientInfo foundClient; + foreach(const ClientInfo& client, clients) + { + if(client.nick == args) + { + 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"); +} + +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) + { + 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 + { + rightsFailMessage(subject, ircModFifos[subject.nick].first().first.userType); + ircModFifos.remove(subject.nick); + } +} @@ -16,19 +16,25 @@ #include <IrcCommand> #include <QSignalMapper> -#include "ClientInfo.hpp" +#include "CmdFunctionRef.hpp" #define CONFIG_FILE_NAME "CrServerManager.conf" #define CUR_SCEN_FILE_NAME "curscen.txt" #define LAST_SCEN_FILE_NAME "lastscen.txt" #define SCOREBOARD_FILE_NAME settings["ClonkDirectory"] + "scoreboard.html" +#define IRC_CHECK_CALLBACK(name) void name(const ClientInfo& requester, int status, const ClientInfo& subject) +#define IRC_CHECK_CALLBACK_IMPL(name) void CRSM::name(const ClientInfo& requester, int status, const ClientInfo& subject) + #ifdef Q_OS_LINUX64 #define C4GROUP_EXECUTABLE "c4group64" #else #define C4GROUP_EXECUTABLE "c4group" #endif +class CRSM; +typedef void (CRSM::*IrcCheckCallback)(const ClientInfo&, int, const ClientInfo&); + class CRSM : public QObject { private: @@ -49,6 +55,12 @@ private: IngameChat }; + enum RespondType { + Normal, + Notice, + Private, + PrivateNotice + }; Q_OBJECT public: @@ -76,9 +88,11 @@ private: QStringList args; QStringList ircModChecks; QStringList ircMods; + QMap<QString, QList<QPair<ClientInfo, IrcCheckCallback>>> ircStatusFifos; + QMap<QString, QList<QPair<CmdFunctionRef, QString>>> ircModFifos; + QMap<QString, QList<QPair<IrcModOperations, QString>>> ircModFifosOld; QString aliasWishEditor = ""; QString currentAliasWish = ""; - QMap<QString, QList<QPair<IrcModOperations, QString>>> ircModFifos; int current; QTextStream *qout; QTextStream *qin; @@ -94,11 +108,13 @@ private: bool autoHost = true; QSignalMapper greetMapper; ClientInfo sessionAdmin; + ClientInfo ircAdmin; QFile *logfile; QTextStream logstream; QTextCodec *codec; QStringList ircModIOList; QString writtenToServer; + QMap<QString, CmdFunctionRef> cmds; void startScen(QString, QStringList); void readConfig(); @@ -109,20 +125,55 @@ private: bool scenExists(QString filePath); QString listScenarios(QString commandArgs); QString printQueue(); - //QByteArray toClonkFormat(const QString&); - void ircCheckModCmd(const QString &nick, IrcModOperations operation, QString arg = ""); - void ircModOperation(const QString &nick, IrcModOperations operation, QString arg); - void ircModOperation(const QString &nick, QPair<IrcModOperations, QString> operationArg); + void ircCheckModCmd(const QString &nick, CmdFunctionRef func, QString arg = ""); QString skipScen(); void skipCurrent(); void writeToServer(const QString& message); void writeConfig(); QString addAliasWish(const QString& param); - void ircModJoined(const QString& nick); void informModsAboutAliasWish(); void editAliasWishes(); void editAliasWishes(const QString &message); void stopAliasWishEditing(); QString ircActivateIngameChat(bool activated = true); QStringList listC4Folder(const QString &path); + + void addCommand(const QString& name, CmdFunction func, int interfaces = Clonk | IRC, UserType userType = User, const QString& shortDescription = "", const QString &argList = "", const QString &longDescription = ""); + bool cmdExists(const QString& name, ClientInterface interface); + bool cmd(const QString& name, const QString& args, const ClientInfo& client); + void rightsFailMessage(const ClientInfo& info, UserType minUserType); + UserType clientUserType(const ClientInfo& client); + + void setupCmds(); + + void respond(const ClientInfo& client, const QString& message, const RespondType type = Normal); + void ircCheckUserStatus(const ClientInfo& requester, const ClientInfo& subject, IrcCheckCallback callback); + void setIngameAdmin(const ClientInfo &client, const QString& newAdmin); + + CMD_FUNCTION(help); + CMD_FUNCTION(passToClonk); + CMD_FUNCTION(admin); + CMD_FUNCTION(host); + CMD_FUNCTION(list); + CMD_FUNCTION(queue); + CMD_FUNCTION(aliaswish); + CMD_FUNCTION(ircchat); + CMD_FUNCTION(autohost); + CMD_FUNCTION(skip); + CMD_FUNCTION(next); + CMD_FUNCTION(kill); + CMD_FUNCTION(clear); + CMD_FUNCTION(aliaswishes); + CMD_FUNCTION(newalias); + CMD_FUNCTION(modinfo); + CMD_FUNCTION(io); + CMD_FUNCTION(pcNamePassToClonk); + CMD_FUNCTION(ingameadmin); + CMD_FUNCTION(ircadmin); + CMD_FUNCTION(noadmin); + CMD_FUNCTION(clientlist); + + IRC_CHECK_CALLBACK(ircSetAdmin); + IRC_CHECK_CALLBACK(ircModCmd); }; + @@ -1,5 +1,5 @@ #include <QCoreApplication> -#include "crsm.h" +#include "crsm.hpp" int main(int argc, char *argv[]) { |
