summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/CRSMConfig.hpp2
-rw-r--r--src/CRSMStats.cpp66
-rw-r--r--src/CRSMStats.hpp234
-rw-r--r--src/ClientInfo.hpp1
-rw-r--r--src/CmdFunctionRef.hpp13
-rw-r--r--src/ConfigBase.cpp11
-rw-r--r--src/ConfigBase.hpp5
-rw-r--r--src/CrServerManager.pro6
-rw-r--r--src/crsm.cpp122
-rw-r--r--src/crsm.hpp8
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<ClientInfo, WishStartStats> Wishers;
+};
+
+struct CommandUseStats {
+ int UseCount = 0;
+ int SuccessCount = 0;
+ int RightsFailCount = 0;
+ int SyntaxFailCount = 0;
+};
+
+struct UserStats {
+ WishStartStats WishSum;
+ ClientInfo Client;
+ QMap<QString, WishStartStats> ScenarioWishes;
+ CommandUseStats CommandSum;
+ QMap<QString, CommandUseStats> Commands;
+ QMap<QString, int> FailCommands;
+};
+
+struct CommandStats {
+ CommandUseStats Sum;
+ QString Command = "";
+ QMap<ClientInfo, CommandUseStats> Users;
+};
+
+struct FailCommandStats {
+ int Sum = 0;
+ QString Command = "";
+ QMap<ClientInfo, int> Users;
+};
+
+template<>
+class ConfigValue<WishStartStats> : public ConfigValueBase {
+ WishStartStats& config;
+ ConfigValue<int> wishConfig;
+ ConfigValue<int> 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<CommandUseStats> : public ConfigValueBase {
+ CommandUseStats& config;
+ ConfigValue<int> useConfig;
+ ConfigValue<int> successConfig;
+ ConfigValue<int> rightsFailConfig;
+ ConfigValue<int> 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<ScenarioStats> : public ConfigValueBase {
+ ScenarioStats& config;
+ ConfigValue<WishStartStats> sumConfig;
+ ConfigValue<QString> fileNameConfig;
+ ConfigValue<QMap<ClientInfo, WishStartStats>> 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<UserStats> : public ConfigValueBase {
+ UserStats& config;
+ ConfigValue<WishStartStats> wishSumConfig;
+ ConfigValue<ClientInfo> clientConfig;
+ ConfigValue<QMap<QString, WishStartStats>> scenarioWishesConfig;
+ ConfigValue<CommandUseStats> commandSumConfig;
+ ConfigValue<QMap<QString, CommandUseStats>> commandsConfig;
+ ConfigValue<QMap<QString, int>> 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<CommandStats> : public ConfigValueBase {
+ CommandStats& config;
+ ConfigValue<CommandUseStats> sumConfig;
+ ConfigValue<QString> commandConfig;
+ ConfigValue<QMap<ClientInfo, CommandUseStats>> 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<FailCommandStats> : public ConfigValueBase {
+ FailCommandStats& config;
+ ConfigValue<int> sumConfig;
+ ConfigValue<QString> commandConfig;
+ ConfigValue<QMap<ClientInfo, int>> 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 <QDateTime>
#include <QString>
#include <QStringList>
+#include <QTcpSocket>
#include <CRSMConfig.hpp>
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<UserType, QString> 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<QString, ConfigValueBase*> 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<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));
+ 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<CmdFunctionRef, QString>;
+ 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<ScenarioSettings> userlist;
QList<ScenarioSettings> 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);