From 3d01ec1643191822370ba01c11ec6cc247e06599 Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Thu, 19 Mar 2015 00:59:37 +0100 Subject: Added Managemen-Interface on TCP port 9372 listening on localhost. --- crsm.cpp | 330 +++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 258 insertions(+), 72 deletions(-) (limited to 'crsm.cpp') diff --git a/crsm.cpp b/crsm.cpp index 69af23b..44ef4c5 100644 --- a/crsm.cpp +++ b/crsm.cpp @@ -6,22 +6,32 @@ #include #include #include +#include #define CMD_SIGN "!" +#define MGMT_BUFFER_FILENAME "CRSM-MGMT-Buffer" + CRSM::CRSM(QObject *parent) : QObject(parent) { codec = QTextCodec::codecForName("Windows-1252"); - qout = new QTextStream(stdout,QIODevice::WriteOnly); + 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"; current = 0; finish = false; readConfig(); + + connect(&managementServer, SIGNAL(newConnection()), this, SLOT(newManagementConnection())); + managementServer.listen(QHostAddress::LocalHostIPv6, settings["ManagementPort"].toUInt()); + //cleanUp(); listC4Folders(); readScenarios(); @@ -38,7 +48,7 @@ CRSM::CRSM(QObject *parent) : if(!processManager->isOk()) { - *qout << "Could not start Process Manager!" << endl; + out("Could not start Process Manager!"); settings.remove("ReattachId"); writeConfig(); return; @@ -143,7 +153,7 @@ void CRSM::readServerOutput() connection->sendCommand(IrcCommand::createNotice(mod, mess)); } } - *qout << what.trimmed() << endl; + out(what.trimmed() + "\n"); if(!timeRemover.exactMatch(what)) return; what = timeRemover.cap(1).trimmed(); @@ -219,7 +229,10 @@ void CRSM::readServerOutput() QRegExp startExp("^Start!\\s*$"); if(startExp.exactMatch(what)) { - writeToServer(QString("/set maxplayer 0\n")); + if(session["league"] != "true") + { + writeToServer(QString("/set maxplayer 0\n")); + } session["running"] = "true"; } @@ -272,7 +285,7 @@ void CRSM::readInput() if(what == "/exitafter") { finish = true; - *qout << "Will exit after this round." << endl; + out("Will exit after this round."); return; } if(what == "/next") @@ -285,16 +298,16 @@ void CRSM::readInput() QString skipped; if((skipped = skipScen()) != "") { - *qout << "Skipped \"" << skipped << "\"." << endl; + out("Skipped \"" + skipped + "\".\n"); } else - *qout << "User list is empty!" << endl; + out("User list is empty!\n"); return; } if(what == "/reload") { - *qout << "Reloading config..." << endl; + out("Reloading config...\n"); lists.clear(); settings.clear(); ircModChecks.clear(); @@ -328,12 +341,12 @@ void CRSM::nextScen() void CRSM::printAdditionalHelp() { - *qout << "/exit stops the actual Clonk Server and exits the Server Manager.\n" + 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..." << endl; + "Clonk commands following...\n"); } void CRSM::readLog() @@ -511,6 +524,74 @@ void CRSM::greet(QString pcName) writeToServer(QString("Hallo " + info.nick + "!\n")); } +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, ManagementConnection(sock)); + 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(); + if(managementConnections.contains(sock)) + { + managementConnections.remove(sock); + } +} + void CRSM::startScen(const ScenarioSettings &scen, QStringList argList) { QString filename; @@ -541,6 +622,7 @@ void CRSM::startScen(const ScenarioSettings &scen, QStringList argList) if(scen.league) { argList << "/league"; + session["league"] = "true"; } else { @@ -588,30 +670,30 @@ void CRSM::readConfig() break; } - *qout << "config:" << endl; + out("config:\n"); foreach(const QString &key, settings.keys()) { - *qout << key << " = " << settings.value(key) << endl; + out(key + " = " + settings.value(key) + "\n"); } - *qout << endl; + out("\n"); foreach(const QString &key, lists.keys()) { - *qout << key << ":" << endl; + out(key + ":\n"); foreach(const QString &val, lists.value(key)) { - *qout << "\t" << val << endl; + out("\t" + val + "\n"); } } - *qout << endl; + out("\n"); foreach(const QString &key, maps.keys()) { - *qout << key << ":" << endl; + out(key + ":\n"); foreach(const QString &mapkey, maps.value(key).keys()) { - *qout << "\t[" << mapkey << "]" << " = " << maps.value(key).value(mapkey) << endl; + out("\t[" + mapkey + "]" + " = " + maps.value(key).value(mapkey) + "\n"); } } - *qout << endl; + out("\n"); args = settings["Arguments"].split(" "); args << "/config:"+settings["ClonkConfig"]; } @@ -619,7 +701,7 @@ void CRSM::readConfig() QFile clonkconfig(settings["ClonkConfig"]); if(!clonkconfig.exists()) - *qout << "WARNING: Clonk's config file is not existing!"; + out("WARNING: Clonk's config file is not existing!"); else { clonkconfig.open(QFile::ReadOnly); @@ -638,11 +720,17 @@ void CRSM::readConfig() break; } } - *qout << endl; - *qout << "ClonkDirectory" << " = " << settings.value("ClonkDirectory") << endl; - *qout << "ServerNick" << " = " << settings.value("ServerNick") << endl; + 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"; } - *qout << endl; } void CRSM::readScenarios() @@ -650,23 +738,23 @@ void CRSM::readScenarios() QFile scenfile("scenarios.lst"); if(!scenfile.exists()) { - *qout << "No scenarios.lst found!"; + out("No scenarios.lst found!"); scenfile.open(QFile::WriteOnly); scenfile.write("Worlds.c4f/Goldmine.c4s"); scenfile.close(); } scenfile.open(QFile::ReadOnly); scenlist = QString(scenfile.readAll()).trimmed().split("\n",QString::SkipEmptyParts); - *qout << "Scenarios in list:" << endl; + out("Scenarios in list:\n"); foreach(const QString &scen, scenlist) { if(scenExists(scen)) - *qout << scen << endl; + out(scen + "\n"); else - *qout << "WARNING: Scenario " << scen << " not found!" << endl; + out("WARNING: Scenario " + scen + " not found!\n"); } - *qout << endl; + out("\n"); scenfile.close(); } @@ -683,8 +771,7 @@ QMap CRSM::defaultSettings() void CRSM::listC4Folders() { - *qout << "Listing Contents of C4Folders..."; - qout->flush(); + out("Listing Contents of C4Folders..."); QDirIterator it(settings["ClonkDirectory"], QDirIterator::FollowSymlinks); for(; it.hasNext(); it.next()) { @@ -705,16 +792,16 @@ void CRSM::listC4Folders() } } } - *qout << QString(3, '\b') << ": Finished" << endl; + out(QString(3, '\b') + ": Finished\n"); } void CRSM::cleanUp() { - *qout << endl << "Cleaning up Clonk Folder..." << endl; + 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(); - *qout << endl; + out("\n"); } bool CRSM::scenExists(QString filePath) @@ -1109,6 +1196,9 @@ bool CRSM::cmd(const QString &name, const QString &args, const ClientInfo &clien ircCheckModCmd(client.nick, cmdRef, args); } break; + case Management: + (this->*cmdRef.func)(name, args, client, UserType::Max); + break; } return true; @@ -1146,51 +1236,57 @@ UserType CRSM::clientUserType(const ClientInfo &client) 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.", ""); + addCommand("ingameadmin", &CRSM::ingameadmin, IRC | Management, Admin, "Legt den Ingame-Rundenadmin fest.", ""); if(settings["UseIrc"] == "true") { - addCommand("ircadmin", &CRSM::ircadmin, Clonk, Admin, "Legt den IRC-Rundenadmin fest.", "", "Legt den IRC-Rundenadmin fest. Der IRC-Admin kann über den IRC dieselben Aktionen durchführen wie der Ingame-Rundenadmin."); + addCommand("ircadmin", &CRSM::ircadmin, Clonk | Management, Admin, "Legt den IRC-Rundenadmin fest.", "", "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("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, als Alias für einzutragen. Ein Moderator entscheidet darüber.", " = "); if(settings["IrcUseIngameChat"] == "true") { - addCommand("ircchat", &CRSM::ircchat, Clonk, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", ""); - addCommand("ingamechat", &CRSM::ircchat, IRC, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", ""); - } - 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. Optional in der Liga, wenn \"--league\" angegeben wird.", "[--league] "); - 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.", ""); + addCommand("ircchat", &CRSM::ircchat, Clonk | Management, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", ""); + addCommand("ingamechat", &CRSM::ircchat, IRC | Management, Admin, "Schaltet den Irc-Ingame-Chat ein bzw. aus.", ""); + } + 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 Wunschliste auf. Optional in der Liga, wenn \"--league\" angegeben wird.", "[--league] "); + 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.", ""); addCommand("plrteam", &CRSM::passToClonk, Clonk | IRC, Admin, "Ändert das Team eines Spielers.", " ", "Verschiebt in das ."); - addCommand("pause", &CRSM::passToClonk, Clonk | IRC, Admin, "Pausiert das Spiel."); - addCommand("unpause", &CRSM::passToClonk, Clonk | IRC, Admin, "Setzt das pausierte Spiel fort."); - addCommand("observer", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Der angegebene Host muss zuschauen.", ""); - addCommand("deactivate", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Deaktiviert den angegebenen Host.", ""); - addCommand("activate", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Aktiviert den angegebenen Host.", ""); - addCommand("kick", &CRSM::pcNamePassToClonk, Clonk | IRC, Admin, "Kickt den angegebenen Host.", ""); - addCommand("script", &CRSM::passToClonk, Clonk | IRC, Admin, "Führt das angegebene Script aus.", "