From 4e30841039bb7c3efe5558e40cba8375baad30f7 Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Tue, 27 Oct 2015 12:42:19 +0100 Subject: Begin adding basic stats --- src/CRSMConfig.hpp | 2 + src/CRSMStats.cpp | 66 +++++++++++++- src/CRSMStats.hpp | 234 ++++++++++++++++++++++++++++++++++++++++++++++-- src/ClientInfo.hpp | 1 + src/CmdFunctionRef.hpp | 13 ++- src/ConfigBase.cpp | 11 ++- src/ConfigBase.hpp | 5 +- src/CrServerManager.pro | 6 +- src/crsm.cpp | 122 ++++++++++++++++++++----- src/crsm.hpp | 8 +- 10 files changed, 427 insertions(+), 41 deletions(-) diff --git a/src/CRSMConfig.hpp b/src/CRSMConfig.hpp index 81c7c14..0494aaf 100644 --- a/src/CRSMConfig.hpp +++ b/src/CRSMConfig.hpp @@ -8,6 +8,7 @@ public: Port ManagementPort = 9372; String ListFolder = "ScenarioLists"; String CommandSign = "!"; + String StatsFile = "CrServerManager.stats"; String SessionFile = "CrServerManager.session"; } CRSM; @@ -85,6 +86,7 @@ public: ConfigVal(CRSM.ManagementPort), ConfigVal(CRSM.ListFolder), ConfigVal(CRSM.CommandSign), + ConfigVal(CRSM.StatsFile), ConfigVal(CRSM.SessionFile), diff --git a/src/CRSMStats.cpp b/src/CRSMStats.cpp index 86c541c..7add773 100644 --- a/src/CRSMStats.cpp +++ b/src/CRSMStats.cpp @@ -1,7 +1,71 @@ #include "CRSMStats.hpp" -CRSMStats::CRSMStats() +void CRSMStats::AddScenarioWish(const ClientInfo &client, const QString &scenarioFileName) { + Scenarios[scenarioFileName].FileName = scenarioFileName; + ++Scenarios[scenarioFileName].Sum.WishCount; + ++Scenarios[scenarioFileName].Wishers[client].WishCount; + Users[client].Client = client; + ++Users[client].ScenarioWishes[scenarioFileName].WishCount; + ++Users[client].WishSum.WishCount; } +void CRSMStats::AddScenarioStart(const ClientInfo &client, const QString &scenarioFileName) +{ + Scenarios[scenarioFileName].FileName = scenarioFileName; + ++Scenarios[scenarioFileName].Sum.StartCount; + ++Scenarios[scenarioFileName].Wishers[client].StartCount; + + Users[client].Client = client; + ++Users[client].ScenarioWishes[scenarioFileName].StartCount; + ++Users[client].WishSum.StartCount; +} + +void CRSMStats::AddCommandResult(const ClientInfo &client, const QString &cmd, CmdResult result) +{ + Users[client].Client = client; + + if(result != UnknownCommand) + { + Commands[cmd].Command = cmd; + ++Commands[cmd].Sum.UseCount; + ++Commands[cmd].Users[client].UseCount; + + ++Users[client].CommandSum.UseCount; + ++Users[client].Commands[cmd].UseCount; + } + else + { + FailCommands[cmd].Command = cmd; + ++FailCommands[cmd].Sum; + ++FailCommands[cmd].Users[client]; + + ++Users[client].FailCommands[cmd]; + } + + if(result == Success) + { + ++Commands[cmd].Sum.SuccessCount; + ++Commands[cmd].Users[client].SuccessCount; + + ++Users[client].CommandSum.SuccessCount; + ++Users[client].Commands[cmd].SuccessCount; + } + else if(result == RightsFail) + { + ++Commands[cmd].Sum.RightsFailCount; + ++Commands[cmd].Users[client].RightsFailCount; + + ++Users[client].CommandSum.RightsFailCount; + ++Users[client].Commands[cmd].RightsFailCount; + } + else if(result == SyntaxFail) + { + ++Commands[cmd].Sum.SyntaxFailCount; + ++Commands[cmd].Users[client].SyntaxFailCount; + + ++Users[client].CommandSum.SyntaxFailCount; + ++Users[client].Commands[cmd].SyntaxFailCount; + } +} diff --git a/src/CRSMStats.hpp b/src/CRSMStats.hpp index 5eb7ed0..595c58b 100644 --- a/src/CRSMStats.hpp +++ b/src/CRSMStats.hpp @@ -1,15 +1,235 @@ -#ifndef CRSMSTATS_HPP -#define CRSMSTATS_HPP +#pragma once +#include "ConfigBase.hpp" +#include "ClientInfo.hpp" +#include "CmdFunctionRef.hpp" + +struct WishStartStats { // TODO: Implement stats-gathering + int WishCount = 0; + int StartCount = 0; +}; + +struct ScenarioStats { + WishStartStats Sum; + QString FileName = ""; + QMap Wishers; +}; + +struct CommandUseStats { + int UseCount = 0; + int SuccessCount = 0; + int RightsFailCount = 0; + int SyntaxFailCount = 0; +}; + +struct UserStats { + WishStartStats WishSum; + ClientInfo Client; + QMap ScenarioWishes; + CommandUseStats CommandSum; + QMap Commands; + QMap FailCommands; +}; + +struct CommandStats { + CommandUseStats Sum; + QString Command = ""; + QMap Users; +}; + +struct FailCommandStats { + int Sum = 0; + QString Command = ""; + QMap Users; +}; + +template<> +class ConfigValue : public ConfigValueBase { + WishStartStats& config; + ConfigValue wishConfig; + ConfigValue startConfig; +public: + ConfigValue(WishStartStats& config) : config(config), wishConfig(config.WishCount), startConfig(config.StartCount) {} + void setValue(const QString& value) + { + QStringList parts = value.split(',', QString::KeepEmptyParts); + if(parts.length() != 2) + { + throw ConfigException("Can not parse corrupt WishStartStats"); + } + wishConfig.setValue(parts.first()); + startConfig.setValue(parts.at(1)); + } + + QString value() + { + return wishConfig.value() + ',' + startConfig.value(); + } +}; + +template<> +class ConfigValue : public ConfigValueBase { + CommandUseStats& config; + ConfigValue useConfig; + ConfigValue successConfig; + ConfigValue rightsFailConfig; + ConfigValue syntaxFailConfig; +public: + ConfigValue(CommandUseStats& config) : config(config), useConfig(config.UseCount), successConfig(config.SuccessCount), rightsFailConfig(config.RightsFailCount), syntaxFailConfig(config.SyntaxFailCount) {} + void setValue(const QString& value) + { + QStringList parts = value.split(',', QString::KeepEmptyParts); + if(parts.length() != 4) + { + throw ConfigException("Can not parse corrupt CommandUseStats"); + } + useConfig.setValue(parts.first()); + successConfig.setValue(parts.at(1)); + rightsFailConfig.setValue(parts.at(2)); + syntaxFailConfig.setValue(parts.at(3)); + } + + QString value() + { + return useConfig.value() + ',' + successConfig.value() + ',' + rightsFailConfig.value() + ',' + syntaxFailConfig.value(); + } +}; + +template<> +class ConfigValue : public ConfigValueBase { + ScenarioStats& config; + ConfigValue sumConfig; + ConfigValue fileNameConfig; + ConfigValue> wishersConfig; +public: + ConfigValue(ScenarioStats& config) : config(config), sumConfig(config.Sum), fileNameConfig(config.FileName), wishersConfig(config.Wishers) {} + void setValue(const QString& value) + { + QStringList parts = Util::splitEscaped(value, '|'); + if(parts.length() != 3) + { + throw ConfigException("Can not parse corrupt ScenarioStats"); + } + sumConfig.setValue(parts.first()); + fileNameConfig.setValue(parts.at(1)); + wishersConfig.setValue(parts.at(2)); + } + + QString value() + { + return Util::joinEscape({sumConfig.value(), fileNameConfig.value(), wishersConfig.value()}, '|'); + } +}; + +template<> +class ConfigValue : public ConfigValueBase { + UserStats& config; + ConfigValue wishSumConfig; + ConfigValue clientConfig; + ConfigValue> scenarioWishesConfig; + ConfigValue commandSumConfig; + ConfigValue> commandsConfig; + ConfigValue> failCommandsConfig; + +public: + ConfigValue(UserStats& config) : config(config), wishSumConfig(config.WishSum), clientConfig(config.Client), scenarioWishesConfig(config.ScenarioWishes), commandSumConfig(config.CommandSum), commandsConfig(config.Commands), failCommandsConfig(config.FailCommands) {} + + void setValue(const QString& value) + { + QStringList parts = Util::splitEscaped(value, '|'); + if(parts.length() != 6) + { + throw ConfigException("Can not parse corrupt UserStats"); + } + wishSumConfig.setValue(parts.first()); + clientConfig.setValue(parts.at(1)); + scenarioWishesConfig.setValue(parts.at(2)); + commandSumConfig.setValue(parts.at(3)); + commandsConfig.setValue(parts.at(4)); + failCommandsConfig.setValue(parts.at(5)); + } + + QString value() + { + return Util::joinEscape({wishSumConfig.value(), clientConfig.value(), scenarioWishesConfig.value(), commandSumConfig.value(), commandsConfig.value(), failCommandsConfig.value()}, '|'); + } +}; + +template<> +class ConfigValue : public ConfigValueBase { + CommandStats& config; + ConfigValue sumConfig; + ConfigValue commandConfig; + ConfigValue> usersConfig; + +public: + ConfigValue(CommandStats& config) : config(config), sumConfig(config.Sum), commandConfig(config.Command), usersConfig(config.Users) {} + + void setValue(const QString &value) + { + QStringList parts = Util::splitEscaped(value, '|'); + if(parts.length() != 3) + { + throw ConfigException("Can not parse corrupt CommandStats"); + } + sumConfig.setValue(parts.first()); + commandConfig.setValue(parts.at(1)); + usersConfig.setValue(parts.at(2)); + } + + QString value() + { + return Util::joinEscape({sumConfig.value(), commandConfig.value(), usersConfig.value()}, '|'); + } + +}; + +template<> +class ConfigValue : public ConfigValueBase { + FailCommandStats& config; + ConfigValue sumConfig; + ConfigValue commandConfig; + ConfigValue> usersConfig; + +public: + ConfigValue(FailCommandStats& config) : config(config), sumConfig(config.Sum), commandConfig(config.Command), usersConfig(config.Users) {} + + void setValue(const QString &value) + { + QStringList parts = Util::splitEscaped(value, '|'); + if(parts.length() != 3) + { + throw ConfigException("Can not parse corrupt CommandStats"); + } + sumConfig.setValue(parts.first()); + commandConfig.setValue(parts.at(1)); + usersConfig.setValue(parts.at(2)); + } + + QString value() + { + return Util::joinEscape({sumConfig.value(), commandConfig.value(), usersConfig.value()}, '|'); + } + +}; class CRSMStats : public ConfigBase { + QString curFileName; public: - CRSMStats(); + Map(String, ScenarioStats) Scenarios; + Map(ClientInfo, UserStats) Users; + Map(String, CommandStats) Commands; + Map(String, FailCommandStats) FailCommands; -signals: + CRSMStats() : ConfigBase({ + ConfigVal(Scenarios), + ConfigVal(Users), + ConfigVal(Commands), + ConfigVal(FailCommands) + }) {} -public slots: + void AddScenarioWish(const ClientInfo& client, const QString& scenarioFileName); + void AddScenarioStart(const ClientInfo& client, const QString& scenarioFileName); + void AddCommandResult(const ClientInfo& client, const QString& cmd, CmdResult result); }; - -#endif // CRSMSTATS_HPP diff --git a/src/ClientInfo.hpp b/src/ClientInfo.hpp index 83005e6..eb39b82 100644 --- a/src/ClientInfo.hpp +++ b/src/ClientInfo.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include diff --git a/src/CmdFunctionRef.hpp b/src/CmdFunctionRef.hpp index b57dd8d..ff63951 100644 --- a/src/CmdFunctionRef.hpp +++ b/src/CmdFunctionRef.hpp @@ -6,8 +6,8 @@ class CRSM; -#define CMD_FUNCTION(name) void name(const QString& cmd, const QString& args, const ClientInfo& client, UserType userType) -#define CMD_FUNCTION_IMPL(name) void CRSM::name(const QString& cmd, const QString& args, const ClientInfo& client, UserType userType) { (void)cmd; (void)args; (void)client; (void)userType; +#define CMD_FUNCTION(name) CmdResult name(const QString& cmd, const QString& args, const ClientInfo& client, UserType userType) +#define CMD_FUNCTION_IMPL(name) CmdResult CRSM::name(const QString& cmd, const QString& args, const ClientInfo& client, UserType userType) { (void)cmd; (void)args; (void)client; (void)userType; enum UserType { User = 0, @@ -16,7 +16,14 @@ enum UserType { Max = Moderator }; -using CmdFunction = void (CRSM::*)(const QString&, const QString&, const ClientInfo&, UserType); +enum CmdResult { + Success = 0, + SyntaxFail = 1, + RightsFail = 2, + UnknownCommand = 3 +}; + +using CmdFunction = CmdResult (CRSM::*)(const QString&, const QString&, const ClientInfo&, UserType); const QMap userTypeStrings { {User, "Benutzer"}, diff --git a/src/ConfigBase.cpp b/src/ConfigBase.cpp index 9f62d77..317ecec 100644 --- a/src/ConfigBase.cpp +++ b/src/ConfigBase.cpp @@ -102,6 +102,7 @@ ConfigValueBase& ConfigBase::getConfigValue(const QString& name) QString ConfigBase::read(const QString &fileName, bool writeDefault) { + curFileName = fileName; QString ret = ""; QFile config(fileName); @@ -147,8 +148,16 @@ QString ConfigBase::read(const QString &fileName, bool writeDefault) return ret; } -bool ConfigBase::write(const QString &fileName) +bool ConfigBase::write(QString fileName) { + if(fileName.isEmpty()) + { + fileName = curFileName; + } + if(fileName.isEmpty()) + { + return false; + } QFile config(fileName); if(config.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { diff --git a/src/ConfigBase.hpp b/src/ConfigBase.hpp index a3a90b1..d4c5b8a 100644 --- a/src/ConfigBase.hpp +++ b/src/ConfigBase.hpp @@ -312,7 +312,7 @@ public: void setConfigMapValue(const QString& name, const QString& key, const QString& value); QString read(const QString& fileName, bool writeDefault = true); - bool write(const QString& fileName); + bool write(QString fileName = ""); void setConfigLine(const QString& line); @@ -321,6 +321,9 @@ protected: ConfigValueBase& getConfigValue(const QString &name); QMap configValues; + +private: + QString curFileName = ""; }; #endif // CONFIGBASE diff --git a/src/CrServerManager.pro b/src/CrServerManager.pro index 8ba7cd6..fa0a7a2 100644 --- a/src/CrServerManager.pro +++ b/src/CrServerManager.pro @@ -21,7 +21,8 @@ SOURCES += main.cpp \ crsm.cpp \ ProcessManager.cpp \ ConfigBase.cpp \ - Util.cpp + Util.cpp \ + CRSMStats.cpp HEADERS += \ CmdFunctionRef.hpp \ @@ -30,7 +31,8 @@ HEADERS += \ ProcessManager.hpp \ CRSMConfig.hpp \ ConfigBase.hpp \ - Util.hpp + Util.hpp \ + CRSMStats.hpp equals(QT_ARCH, "x86_64"):linux-*: DEFINES += Q_OS_LINUX64 QMAKE_CXXFLAGS *= -std=c++11 -Wall -Wextra -Werror -Wunused diff --git a/src/crsm.cpp b/src/crsm.cpp index 36058c2..6da3fb2 100644 --- a/src/crsm.cpp +++ b/src/crsm.cpp @@ -229,6 +229,7 @@ void CRSM::readServerOutput() if(startExp.exactMatch(what)) { Session.State = CRSMSession::Running; + Stats.AddScenarioStart(Session.Scenario.wishClient, scenarioFileName(Session.Scenario.name)); if(!Session.League) { writeToServer(QString("/set maxplayer 0\n")); @@ -615,13 +616,11 @@ void CRSM::startScen(const ScenarioSettings &scen, QStringList argList) scoreboardFile.open(QFile::WriteOnly); scoreboardFile.close(); - Session.ScenarioName = scen.name; + Session.Scenario= scen; Session.IRC.UseIngameChat = Config.IRC.UseIngameChat; Session.State = CRSMSession::Lobby; ircSetIngameChannelTopic(); - filename = scen.name; - while(Config.Hosting.Alias.contains(filename)) - filename = Config.Hosting.Alias.value(filename); + filename = scenarioFileName(scen.name); if(scen.league) { @@ -1136,7 +1135,7 @@ void CRSM::ircSetIngameChannelTopic() default: break; } - connection->sendCommand(IrcCommand::createTopic(Config.IRC.IngameChannel, "Aktuelles Szenario: " + Session.ScenarioName + " | " + statusText + " | Ingamechat ist " + (Session.IRC.UseIngameChat ? "" : "de") + "aktviert.")); + connection->sendCommand(IrcCommand::createTopic(Config.IRC.IngameChannel, "Aktuelles Szenario: " + Session.Scenario.name + " | " + statusText + " | Ingamechat ist " + (Session.IRC.UseIngameChat ? "" : "de") + "aktviert.")); } } } @@ -1189,24 +1188,25 @@ bool CRSM::cmd(const QString &cmd, const ClientInfo &client) CmdFunctionRef* cmdPtr; if((cmdPtr = findCommand(cmd, client.interface)) != nullptr) { - const CmdFunctionRef& cmdRef = *cmdPtr; + CmdFunctionRef cmdRef = *cmdPtr; QString args = cmd.mid(cmdRef.name.length()).trimmed(); switch(client.interface) { case Clonk: if(clientUserType(client) >= cmdRef.userType) { - (this->*cmdRef.func)(cmdRef.name, args, client, clientUserType(client)); + callCommand(cmdRef, args, client, clientUserType(client)); } else { rightsFailMessage(client, cmdRef.userType); + Stats.AddCommandResult(client, cmdRef.name, RightsFail); } break; case IRC: if(cmdRef.userType == User || ((cmdRef.userType == Admin && (clientUserType(client) >= Admin)) || (cmdRef.userType == Moderator && clientUserType(client) >= Moderator))) { - (this->*cmdRef.func)(cmdRef.name, args, client, clientUserType(client)); + callCommand(cmdRef, args, client, clientUserType(client)); } else if(cmdRef.userType >= Admin) { @@ -1214,7 +1214,7 @@ bool CRSM::cmd(const QString &cmd, const ClientInfo &client) } break; case Management: - (this->*cmdRef.func)(cmdRef.name, args, client, UserType::Max); + callCommand(cmdRef, args, client, UserType::Max); break; case Auto: //just to avoid the compiler warning, there can't be a command from this interface break; @@ -1224,6 +1224,7 @@ bool CRSM::cmd(const QString &cmd, const ClientInfo &client) } else { + Stats.AddCommandResult(client, cmd, UnknownCommand); return false; } } @@ -1548,6 +1549,7 @@ void CRSM::exit() void CRSM::applyConfig() { + Stats.write(); args.clear(); args << "/config:" + Config.Clonk.Server.Config; args << Config.Clonk.Server.Arguments.split(" "); @@ -1601,6 +1603,21 @@ void CRSM::applyConfig() exit(); } setupCmds(); + out(Stats.read(Config.CRSM.StatsFile)); +} + +QString CRSM::scenarioFileName(QString name) +{ + while(Config.Hosting.Alias.contains(name)) + name = Config.Hosting.Alias.value(name); + return name; +} + +CmdResult CRSM::callCommand(const CmdFunctionRef &func, const QString &args, const ClientInfo &client, UserType userType) +{ + CmdResult ret = (this->*func.func)(func.name, args, client, userType); + Stats.AddCommandResult(client, func.name, ret); + return ret; } CMD_FUNCTION_IMPL(help) @@ -1674,10 +1691,12 @@ CMD_FUNCTION_IMPL(help) respond(client, "Der Befehl " + args + " existiert nicht.\n"); } } + return Success; } CMD_FUNCTION_IMPL(passToClonk) writeToServer('/' + cmd + ' ' + args + '\n'); + return Success; } CMD_FUNCTION_IMPL(admin) @@ -1689,31 +1708,31 @@ CMD_FUNCTION_IMPL(admin) if(args == Config.Auto.Volatile.Clonk.ServerNick || args == Config.Auto.Volatile.Clonk.ServerPCName) { respond(client, "Der Server kann nicht als Rundenadmin eingetragen werden!\n"); - return; + return Success; } break; case IRC: interfaceAdminPtr = &Session.IRC.Admin; break; default: - return; + return Success; } ClientInfo& interfaceAdmin = *interfaceAdminPtr; if(args.isEmpty() && interfaceAdmin == client) { respond(client, "Du bist bereits Rundenadmin!\n"); - return; + return Success; } else if((!Session.IRC.Admin.empty() || !Session.Clonk.Admin.empty()) && userType < Admin && !(Session.Clonk.LeaveAdmins.contains(client) && Session.Clonk.LeaveAdmins.value(client).secsTo(QDateTime::currentDateTime()) < Config.Clonk.Chat.RegainAdminTime)) { respond(client, Session.IRC.Admin.toString() + (!Session.IRC.Admin.nick.isEmpty() && !Session.Clonk.Admin.nick.isEmpty() ? " und " : "") + Session.Clonk.Admin.toString() + " ist bereits Rundenadmin!\n"); - return; + return Success; } else if(args.isEmpty()) { interfaceAdmin = client; respond(client, client.toString() + " wurde als Rundenadmin eingetragen!\n"); - return; + return Success; } else if(client.interface == Clonk) { @@ -1723,6 +1742,7 @@ CMD_FUNCTION_IMPL(admin) { ircCheckUserStatus(client, ClientInfo::ircClient(args), &CRSM::ircSetAdmin); } + return Success; } CMD_FUNCTION_IMPL(host) @@ -1747,6 +1767,8 @@ CMD_FUNCTION_IMPL(host) if(scenAllowed(scen, client, userType)) { userlist << scen; + Stats.AddScenarioWish(client, scenarioFileName(scen.name)); + respond(client, "Szenario " + scen.name + (scen.league ? " mit Liga" : "") + " " + (Session.State == CRSMSession::None || (Session.Clonk.Clients.size() == 0 && !Session.UserWish) ? "wird jetzt gehostet" : "wurde der Warteschlange hinzugefügt") + ".\n"); if(userlist.length() == 1 && !Session.UserWish && !Session.Running && Session.State != CRSMSession::None && Session.Clonk.Clients.size() > 0) respond(client, "Überrede alle Spieler zu leaven und dein Wunsch wird sofort gehostet ;-)\n"); @@ -1766,6 +1788,7 @@ CMD_FUNCTION_IMPL(host) } } } + return Success; } CMD_FUNCTION_IMPL(list) @@ -1777,18 +1800,22 @@ CMD_FUNCTION_IMPL(list) { respond(client, Config.IRC.ScenListMessage); } + return Success; } CMD_FUNCTION_IMPL(queue) respond(client, printQueue(), RespondType::PrivateNotice); + return Success; } CMD_FUNCTION_IMPL(aliaswish) respond(client, addAliasWish(args) + "\n"); + return Success; } CMD_FUNCTION_IMPL(ircchat) respond(client, ircActivateIngameChat(args.toLower() == "on") + "\n"); + return Success; } CMD_FUNCTION_IMPL(autohost) @@ -1806,7 +1833,7 @@ CMD_FUNCTION_IMPL(autohost) autoHost = false; respond(client, "Automatisches Hosting deaktiviert.\n"); } - + return Success; } CMD_FUNCTION_IMPL(skip) @@ -1819,6 +1846,7 @@ CMD_FUNCTION_IMPL(skip) { respond(client, "Userliste ist leer!\n"); } + return Success; } CMD_FUNCTION_IMPL(next) @@ -1826,11 +1854,13 @@ CMD_FUNCTION_IMPL(next) { respond(client, "Versuche zu überspringen...\n"); } + return Success; } CMD_FUNCTION_IMPL(kill) processManager->kill(); respond(client, "Clonk-Server wurde gekillt.\n"); + return Success; } CMD_FUNCTION_IMPL(clear) @@ -1843,6 +1873,7 @@ CMD_FUNCTION_IMPL(clear) { respond(client, "Userliste ist leer!\n"); } + return Success; } CMD_FUNCTION_IMPL(aliaswishes) @@ -1855,6 +1886,7 @@ CMD_FUNCTION_IMPL(aliaswishes) } else respond(client, aliasWishEditor + " bearbeitet bereits die Aliase. Danke für die Bemühung.\n", RespondType::Private); + return Success; } CMD_FUNCTION_IMPL(newalias) @@ -1888,8 +1920,9 @@ CMD_FUNCTION_IMPL(newalias) } else { - respond(client, "Eingabefehler! Siehe !help newalias für mehr Informationen.\n"); + respond(client, "Eingabefehler! Siehe " + Config.CRSM.CommandSign + "help newalias für mehr Informationen.\n"); } + return Success; } CMD_FUNCTION_IMPL(modinfo) @@ -1898,6 +1931,7 @@ CMD_FUNCTION_IMPL(modinfo) { respond(client, (ircMods.contains(mod) ? QString("*") : QString(" ")) + (ircModIOList.contains(mod) ? QString("+") : QString(" ")) + (ircModWatchList.contains(ClientInfo::ircClient(mod)) ? QString("-") : QString(" ")) + " " + Config.IRC.Moderators.value(mod) + "\n", RespondType::Private); } + return Success; } CMD_FUNCTION_IMPL(io) @@ -1905,13 +1939,14 @@ CMD_FUNCTION_IMPL(io) ircModIOList.removeAll(client.nick); else ircModIOList.append(client.nick); + return Success; } CMD_FUNCTION_IMPL(passToClonkPcName) if(args == Config.Auto.Volatile.Clonk.ServerNick || args == Config.Auto.Volatile.Clonk.ServerPCName) { respond(client, Config.CRSM.CommandSign + cmd + " kann nicht auf den Server angewendet werden!\n"); - return; + return Success; } ClientInfo info; if(Session.Clonk.Clients.contains(args)) @@ -1949,12 +1984,12 @@ CMD_FUNCTION_IMPL(passToClonkPcName) if(ambigous) { respond(client, "Chat-Nickname " + args + " ist nicht eindeutig. Bitte PC-Namen verwenden.\n"); - return; + return Success; } else if(notFound) { respond(client, args + " wurde nicht gefunden!\n"); - return; + return Success; } else { @@ -1964,23 +1999,27 @@ CMD_FUNCTION_IMPL(passToClonkPcName) if(cmd == "kick" && clientUserType(info) > userType) { respond(client, "Moderatoren können nur von anderen Moderatoren gekickt werden.\n"); - return; + return RightsFail; } writeToServer("/" + cmd + " " + info.pcName + "\n"); + return Success; } CMD_FUNCTION_IMPL(ircadmin) ircCheckUserStatus(client, ClientInfo::ircClient(args), &CRSM::ircSetAdmin); + return Success; } CMD_FUNCTION_IMPL(ingameadmin) setIngameAdmin(client, args); + return Success; } CMD_FUNCTION_IMPL(noadmin) Session.IRC.Admin.clear(); Session.Clonk.Admin.clear(); respond(client, "Rundenadmin wurde freigegeben.\n"); + return Success; } CMD_FUNCTION_IMPL(clientlist) @@ -1993,6 +2032,7 @@ CMD_FUNCTION_IMPL(clientlist) } response = response.left(response.length() - 2); respond(client, response + "\n"); + return Success; } CMD_FUNCTION_IMPL(passToClonkNumeric) @@ -2001,10 +2041,12 @@ CMD_FUNCTION_IMPL(passToClonkNumeric) if(!ok) { respond(client, Config.CRSM.CommandSign + cmd + " kann nur mit einer Zahl verwendet werden!\n"); + return SyntaxFail; } else { writeToServer("/" + cmd + " " + QString::number(number) + "\n"); + return Success; } } @@ -2012,10 +2054,11 @@ CMD_FUNCTION_IMPL(passToClonkNumericOrEmpty) if(args.isEmpty()) { writeToServer("/" + cmd + "\n"); + return Success; } else { - passToClonkNumeric(cmd, args, client, userType); + return passToClonkNumeric(cmd, args, client, userType); } } @@ -2023,10 +2066,12 @@ CMD_FUNCTION_IMPL(passToClonkOnOff) if(args != "on" && args != "off") { respond(client, Config.CRSM.CommandSign + cmd + " kann nur mit \"on\" oder \"off\" verwendet werden.\n"); + return SyntaxFail; } else { writeToServer("/" + cmd + " " + args + "\n"); + return Success; } } @@ -2036,16 +2081,19 @@ CMD_FUNCTION_IMPL(join) if(argList.size() < 1 || argList.first().isEmpty()) { respond(client, "Kein Channel angegeben!\n"); + return SyntaxFail; } else if(chanExp.exactMatch(argList.first())) { QString chan = argList.first(); argList.removeFirst(); connection->sendCommand(IrcCommand::createJoin(chan, argList.join(' '))); + return Success; } else { respond(client, "\"" + argList.first() + "\" ist kein gültiger Channel-Name.\n"); + return SyntaxFail; } } @@ -2070,27 +2118,33 @@ CMD_FUNCTION_IMPL(leave) if(chanExp.exactMatch(chan)) { connection->sendCommand(IrcCommand::createPart(chan, reason)); + return Success; } else { respond(client, "\"" + chan + "\" ist kein gültiger Channel-Name.\n"); + return SyntaxFail; } } CMD_FUNCTION_IMPL(exit) exit(); + return Success; } CMD_FUNCTION_IMPL(exitDetach) Config.Auto.ProcessManager.ReattachId = processManager->ID(); writeConfig(); Session.write(Config.CRSM.SessionFile); + Stats.write(); + connection->quit(Config.IRC.QuitMessage); QCoreApplication::quit(); + return Success; } CMD_FUNCTION_IMPL(exitUpdate) writeToServer("Der Server Manager wird upgedatet. Befehle funktionieren temporär nicht.\n"); - exitDetach(cmd, args, client, userType); + return exitDetach(cmd, args, client, userType); } CMD_FUNCTION_IMPL(exitAfter) @@ -2098,11 +2152,12 @@ CMD_FUNCTION_IMPL(exitAfter) { finish = true; respond(client, "Beende nach dieser runde.\n"); + return Success; } else { respond(client, "Es läuft gerade kein Spiel, beende sofort.\n"); - exit(cmd, args, client, userType); + return exit(cmd, args, client, userType); } } @@ -2115,12 +2170,14 @@ CMD_FUNCTION_IMPL(reload) readConfig(); readScenarios(); out("Configuration reloaded.\n"); + return Success; } CMD_FUNCTION_IMPL(saveConfig) out("Saving Configuration...\n"); writeConfig(); out("Configuration saved.\n"); + return Success; } CMD_FUNCTION_IMPL(reconnectIrc) @@ -2128,6 +2185,7 @@ CMD_FUNCTION_IMPL(reconnectIrc) connection->close(); delete connection; prepareAndConnectIrc(); + return Success; } CMD_FUNCTION_IMPL(groupinfo) @@ -2136,6 +2194,7 @@ CMD_FUNCTION_IMPL(groupinfo) respond(client, "Unbekannter Unterbefehl: \"" + args + "\"!\n"); } help("help", cmd, client, userType); + return Success; } CMD_FUNCTION_IMPL(grouphelp) @@ -2147,15 +2206,18 @@ CMD_FUNCTION_IMPL(grouphelp) { help("help", QString(cmd).replace(cmd.indexOf("help"), 4, "") + args, client, userType); } + return Success; } CMD_FUNCTION_IMPL(setRaw) writeToServer("/set " + args + '\n'); + return Success; } CMD_FUNCTION_IMPL(relist) listC4Folders(); readScenarios(); + return Success; } CMD_FUNCTION_IMPL(getConfigValue) @@ -2167,6 +2229,7 @@ CMD_FUNCTION_IMPL(getConfigValue) { respond(client, "Es existiert kein Wert mit dem Namen \"" + args + "\"\n"); } + return Success; } CMD_FUNCTION_IMPL(setConfigValue) @@ -2180,6 +2243,7 @@ CMD_FUNCTION_IMPL(setConfigValue) { respond(client, QString("Fehler beim Setzen: ") + e.what() + "\n"); } + return Success; } CMD_FUNCTION_IMPL(ircSay) @@ -2187,6 +2251,7 @@ CMD_FUNCTION_IMPL(ircSay) if(parts.length() < 2) { respond(client, "Es wird ein Empfänger und eine Nachricht benötigt.\n"); + return SyntaxFail; } else { @@ -2203,6 +2268,7 @@ CMD_FUNCTION_IMPL(ircSay) connection->sendCommand(IrcCommand::createMessage(target, message)); } } + return Success; } CMD_FUNCTION_IMPL(ircWatch) @@ -2234,7 +2300,9 @@ CMD_FUNCTION_IMPL(ircWatch) else { respond(client, Config.CRSM.CommandSign + cmd + " kann nur mit \"on\" oder \"off\" verwendet werden.\n"); + return SyntaxFail; } + return Success; } CMD_FUNCTION_IMPL(getAdmin) @@ -2264,6 +2332,7 @@ CMD_FUNCTION_IMPL(getAdmin) response.append("Rundenadmin.\n"); respond(client, response); } + return Success; } IRC_CHECK_CALLBACK_IMPL(ircSayQuery) @@ -2300,7 +2369,7 @@ IRC_CHECK_CALLBACK_IMPL(ircModCmd) QList> &fifo = ircModFifos[subject.nick]; while(fifo.size() > 0) { - (this->*fifo.first().first.func)(fifo.first().first.name, fifo.first().second, subject, clientUserType(subject)); + callCommand(fifo.first().first, fifo.first().second, subject, clientUserType(subject)); fifo.removeFirst(); } if(!Config.Auto.Hosting.AliasWishes.isEmpty()) @@ -2314,6 +2383,11 @@ IRC_CHECK_CALLBACK_IMPL(ircModCmd) if(ircModFifos.contains(subject.nick)) { rightsFailMessage(subject, ircModFifos[subject.nick].first().first.userType); + using CmdArgType = QPair; + foreach(const CmdArgType& cmd, ircModFifos[subject.nick]) + { + Stats.AddCommandResult(subject, cmd.first.name, RightsFail); + } ircModFifos.remove(subject.nick); } } diff --git a/src/crsm.hpp b/src/crsm.hpp index a9a3143..63e6156 100644 --- a/src/crsm.hpp +++ b/src/crsm.hpp @@ -21,6 +21,7 @@ #include "CmdFunctionRef.hpp" #include "ProcessManager.hpp" #include "CRSMConfig.hpp" +#include "CRSMStats.hpp" #define CONFIG_FILE_NAME "CrServerManager.conf" #define SESSION_FILE_NAME "CrServerManager.session" @@ -142,7 +143,7 @@ private: enum {None = -1, Lobby = 0, Loading = 1, Running = 2} State = None; Boolean UserWish = false; Integer CountDown = -1; - String ScenarioName = ""; + ScenarioSettings Scenario; struct { @@ -168,7 +169,7 @@ private: ConfigVal(State), ConfigVal(UserWish), ConfigVal(CountDown), - ConfigVal(ScenarioName), + ConfigVal(Scenario), ConfigVal(Clonk.Admin), ConfigVal(Clonk.Clients), @@ -185,6 +186,7 @@ private: }; CRSMSession Session; + CRSMStats Stats; QList userlist; QList autolist; @@ -270,6 +272,8 @@ private: void exit(); void applyConfig(); + QString scenarioFileName(QString name); + CmdResult callCommand(const CmdFunctionRef& func, const QString& args, const ClientInfo& client, UserType userType); CMD_FUNCTION(help); CMD_FUNCTION(passToClonk); -- cgit v1.2.3-54-g00ecf