summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CRSMSession.cpp29
-rw-r--r--src/CRSMSession.hpp51
-rw-r--r--src/ClonkInterface.cpp95
-rw-r--r--src/ClonkInterface.hpp80
-rw-r--r--src/CrServerManager.pro10
-rw-r--r--src/PatchedClonkControl.cpp6
-rw-r--r--src/PatchedClonkControl.hpp11
-rw-r--r--src/PatchedClonkParser.cpp212
-rw-r--r--src/PatchedClonkParser.hpp14
-rw-r--r--src/crsm.cpp359
-rw-r--r--src/crsm.hpp89
11 files changed, 676 insertions, 280 deletions
diff --git a/src/CRSMSession.cpp b/src/CRSMSession.cpp
new file mode 100644
index 0000000..caba9e2
--- /dev/null
+++ b/src/CRSMSession.cpp
@@ -0,0 +1,29 @@
+#include "CRSMSession.hpp"
+#include "crsm.hpp"
+
+void CRSMSession::clear()
+{
+ auto configVals = configValues;
+ *this = CRSMSession();
+ configValues = configVals;
+}
+
+CRSMSession::CRSMSession() : ConfigBase::ConfigBase({
+ ConfigVal(League),
+ ConfigVal(State),
+ ConfigVal(UserWish),
+ ConfigVal(CountDown),
+ ConfigVal(Scenario),
+
+ ConfigVal(Clonk.Admin),
+ ConfigVal(Clonk.Clients),
+ ConfigVal(Clonk.Server),
+
+ ConfigVal(IRC.Admin),
+ ConfigVal(IRC.UseIngameChat),
+ }) {}
+
+CRSMSession::CRSMSession(CRSM *crsm) : CRSMSession()
+{
+ addConfigValue("Hosting.UserWishes", mkConfigValue(crsm->userlist, false));
+}
diff --git a/src/CRSMSession.hpp b/src/CRSMSession.hpp
new file mode 100644
index 0000000..5c9a72e
--- /dev/null
+++ b/src/CRSMSession.hpp
@@ -0,0 +1,51 @@
+#pragma once
+#include "qt-config/ConfigBase.hpp"
+#include "ClientInfo.hpp"
+
+class CRSM;
+
+class ScenarioSettings {
+public:
+ QString name;
+ ClientInfo wishClient;
+ bool league = false;
+ bool randomLeague = false;
+
+ ScenarioSettings(const QString& name, bool league = false) : name(name), league(league) {}
+ ScenarioSettings(const QString& name, const ClientInfo& client, bool league = false) : name(name), wishClient(client), league(league) {}
+ ScenarioSettings() {}
+ inline bool operator ==(const ScenarioSettings& other) const
+ {
+ return name == other.name && league == other.league && randomLeague == other.randomLeague && wishClient == other.wishClient;
+ }
+};
+
+struct CRSMSession : public ConfigBase {
+ enum SessionState {None = -1, Lobby = 0, Loading = 1, Running = 2};
+
+ Boolean League = false;
+ SessionState State = None;
+ Boolean UserWish = false;
+ Boolean AfkAdmin = false;
+ Integer CountDown = -1;
+ ScenarioSettings Scenario;
+
+
+ struct {
+ ClientInfo Admin;
+ Map(String, ClientInfo) Clients;
+ ClientInfo Server;
+ QMap<ClientInfo, QDateTime> LeaveAdmins;
+ } Clonk;
+
+ struct {
+ ClientInfo Admin;
+ Boolean UseIngameChat = false;
+ } IRC;
+
+ void clear();
+
+ CRSMSession();
+
+ CRSMSession(CRSM* crsm);
+};
diff --git a/src/ClonkInterface.cpp b/src/ClonkInterface.cpp
new file mode 100644
index 0000000..8d6ab98
--- /dev/null
+++ b/src/ClonkInterface.cpp
@@ -0,0 +1,95 @@
+#include "ClonkInterface.hpp"
+
+
+ClonkOutputInterface::~ClonkOutputInterface() { }
+
+void ClonkOutputInterface::raw(const QString &msg) { Q_UNUSED(msg); }
+
+void ClonkOutputInterface::rawTimed(const QString &msg, const QTime &time) { Q_UNUSED(msg); Q_UNUSED(time); }
+
+void ClonkOutputInterface::lobbyCountdown(unsigned int seconds) { Q_UNUSED(seconds); }
+
+void ClonkOutputInterface::lobbyCountdownAborted() {}
+
+void ClonkOutputInterface::playerRemoved(const QString &name) { Q_UNUSED(name); }
+
+void ClonkOutputInterface::playerJoined(const ClientInfo &client, const QString &name) { Q_UNUSED(client); Q_UNUSED(name); }
+
+void ClonkOutputInterface::watchdog(const QString &id) { Q_UNUSED(id); }
+
+void ClonkOutputInterface::clientMessage(ClientInfo &client, const QString &message, ClonkOutputInterface::MessageType type, const QTime &time) { Q_UNUSED(client); Q_UNUSED(message); Q_UNUSED(type); Q_UNUSED(time); }
+
+void ClonkOutputInterface::clientConnected(const ClientInfo &client) { Q_UNUSED(client); }
+
+void ClonkOutputInterface::clientRemoved(const ClientInfo &client, const QString& reason) { Q_UNUSED(client); Q_UNUSED(reason); }
+
+void ClonkOutputInterface::clientStateChanged(const ClientInfo &client, bool activated) { Q_UNUSED(client); Q_UNUSED(activated); }
+
+void ClonkOutputInterface::gameLoading() {}
+
+void ClonkOutputInterface::gameStarted() {}
+
+void ClonkOutputInterface::masterserverError(const QString &msg) { Q_UNUSED(msg); }
+
+ClonkParser::ClonkParser(CRSMSession &session) : Session(session)
+{
+
+}
+
+ClonkParser::~ClonkParser()
+{
+
+}
+
+void ClonkParser::addTarget(ClonkOutputInterface *target)
+{
+ if(!targets.contains(target))
+ {
+ targets.push_back(target);
+ }
+}
+
+void ClonkParser::removeTarget(ClonkOutputInterface *target)
+{
+ targets.removeAll(target);
+}
+
+#define dispatch(x) for(auto target : targets)\
+{\
+ target->x;\
+}
+
+void ClonkParser::raw(const QString &msg) { dispatch(raw(msg)) }
+
+void ClonkParser::rawTimed(const QString &msg, const QTime &time) { dispatch(rawTimed(msg, time)) }
+
+void ClonkParser::lobbyCountdown(unsigned int seconds) { dispatch(lobbyCountdown(seconds)) }
+
+void ClonkParser::lobbyCountdownAborted() { dispatch(lobbyCountdownAborted()) }
+
+void ClonkParser::playerRemoved(const QString &name) { dispatch(playerRemoved(name)) }
+
+void ClonkParser::playerJoined(const ClientInfo &client, const QString &name) { dispatch(playerJoined(client, name)) }
+
+void ClonkParser::watchdog(const QString &id) { dispatch(watchdog(id)) }
+
+void ClonkParser::clientMessage(ClientInfo &client, const QString &message, ClonkOutputInterface::MessageType type, const QTime &time) { dispatch(clientMessage(client, message, type, time)) }
+
+void ClonkParser::clientConnected(const ClientInfo &client) { dispatch(clientConnected(client)) }
+
+void ClonkParser::clientRemoved(const ClientInfo &client, const QString& reason) { dispatch(clientRemoved(client, reason)) }
+
+void ClonkParser::clientStateChanged(const ClientInfo &client, bool activated) { dispatch(clientStateChanged(client, activated)) }
+
+void ClonkParser::gameLoading() { dispatch(gameLoading()) }
+
+void ClonkParser::gameStarted() { dispatch(gameStarted()) }
+
+void ClonkParser::masterserverError(const QString &msg) { dispatch(masterserverError(msg)) }
+
+#undef dispatch
+
+ClonkControllerInterface::~ClonkControllerInterface()
+{
+
+}
diff --git a/src/ClonkInterface.hpp b/src/ClonkInterface.hpp
new file mode 100644
index 0000000..376acbe
--- /dev/null
+++ b/src/ClonkInterface.hpp
@@ -0,0 +1,80 @@
+#pragma once
+#include <QString>
+
+#include "ClientInfo.hpp"
+#include "CRSMSession.hpp"
+
+class ClonkControlInterface {
+ ClonkControlInterface() = delete;
+ virtual ~ClonkControlInterface();
+public:
+ virtual void serverMessage(const QString& msg, bool action = false) = 0;
+ virtual void setCountdown(unsigned int seconds) = 0;
+ virtual void abortCountdown() = 0;
+ virtual void setCommand(const QString& command) = 0; // split?
+ virtual void kick(const ClientInfo& client) = 0;
+ virtual void watchdog(const QString& id) = 0;
+ virtual void rawCommand(const QString& command) = 0;
+ virtual void alert() = 0; // specify client? (Clonk patch first)
+};
+
+class ClonkOutputInterface {
+public:
+ virtual ~ClonkOutputInterface();
+
+ enum MessageType { Message, Action, Sound };
+
+ virtual void raw(const QString& msg);
+ virtual void rawTimed(const QString& msg, const QTime& time);
+
+ virtual void lobbyCountdown(unsigned int seconds);
+ virtual void lobbyCountdownAborted();
+
+ virtual void playerRemoved(const QString& name);
+ virtual void playerJoined(const ClientInfo& client, const QString& name);
+
+ virtual void watchdog(const QString& id);
+
+ virtual void clientMessage(ClientInfo& client, const QString& message, MessageType type, const QTime& time);
+
+ virtual void clientConnected(const ClientInfo& client);
+ virtual void clientRemoved(const ClientInfo& client, const QString& reason);
+ virtual void clientStateChanged(const ClientInfo& client, bool activated);
+
+ virtual void gameLoading();
+ virtual void gameStarted();
+
+ virtual void masterserverError(const QString& msg);
+};
+
+class ClonkParser {
+
+protected:
+ QList<ClonkOutputInterface*> targets;
+ CRSMSession& Session;
+
+public:
+ ClonkParser(CRSMSession& session);
+ virtual ~ClonkParser();
+
+ void addTarget(ClonkOutputInterface* target);
+ void removeTarget(ClonkOutputInterface* target);
+
+ virtual void parseMessage(const QString& msg) = 0;
+
+protected:
+ void raw(const QString &msg);
+ void rawTimed(const QString &msg, const QTime &time);
+ void lobbyCountdown(unsigned int seconds);
+ void lobbyCountdownAborted();
+ void playerRemoved(const QString &name);
+ void playerJoined(const ClientInfo &client, const QString &name);
+ void watchdog(const QString &id);
+ void clientMessage(ClientInfo &client, const QString &message, ClonkOutputInterface::MessageType type, const QTime &time);
+ void clientConnected(const ClientInfo &client);
+ void clientRemoved(const ClientInfo &client, const QString &reason);
+ void clientStateChanged(const ClientInfo &client, bool activated);
+ void gameLoading();
+ void gameStarted();
+ void masterserverError(const QString &msg);
+};
diff --git a/src/CrServerManager.pro b/src/CrServerManager.pro
index 786c8a9..d197acb 100644
--- a/src/CrServerManager.pro
+++ b/src/CrServerManager.pro
@@ -24,7 +24,10 @@ SOURCES += main.cpp \
Util.cpp \
CRSMStats.cpp \
CRSMPackCompatibility.cpp \
- CRSMLogging.cpp
+ CRSMLogging.cpp \
+ ClonkInterface.cpp \
+ PatchedClonkParser.cpp \
+ CRSMSession.cpp
HEADERS += \
CmdFunctionRef.hpp \
@@ -38,7 +41,10 @@ HEADERS += \
CRSMPackCompatibility.hpp \
CRSMLogging.hpp \
GreetingSetting.hpp \
- ClientInterface.hpp
+ ClientInterface.hpp \
+ ClonkInterface.hpp \
+ PatchedClonkParser.hpp \
+ CRSMSession.hpp
equals(QT_ARCH, "x86_64"):linux-*: DEFINES += Q_OS_LINUX64
QMAKE_CXXFLAGS *= -std=c++11 -Wall -Wextra -Werror -Wunused
diff --git a/src/PatchedClonkControl.cpp b/src/PatchedClonkControl.cpp
new file mode 100644
index 0000000..47758d2
--- /dev/null
+++ b/src/PatchedClonkControl.cpp
@@ -0,0 +1,6 @@
+#include "PatchedClonkControl.hpp"
+
+PatchedClonkControl::PatchedClonkControl()
+{
+
+}
diff --git a/src/PatchedClonkControl.hpp b/src/PatchedClonkControl.hpp
new file mode 100644
index 0000000..2469178
--- /dev/null
+++ b/src/PatchedClonkControl.hpp
@@ -0,0 +1,11 @@
+#ifndef PATCHEDCLONKCONTROL_HPP
+#define PATCHEDCLONKCONTROL_HPP
+
+
+class PatchedClonkControl : public ClonkControlInterface
+{
+public:
+ PatchedClonkControl();
+};
+
+#endif // PATCHEDCLONKCONTROL_HPP \ No newline at end of file
diff --git a/src/PatchedClonkParser.cpp b/src/PatchedClonkParser.cpp
new file mode 100644
index 0000000..d8e13f4
--- /dev/null
+++ b/src/PatchedClonkParser.cpp
@@ -0,0 +1,212 @@
+#include "PatchedClonkParser.hpp"
+
+PatchedClonkParser::PatchedClonkParser(CRSMSession& session) : ClonkParser(session)
+{
+
+}
+
+namespace {
+ void parseClientInfo(QString& message, QString& pcName, int& CUID, QString& user)
+ {
+ if(message.length() > 5 && message.at(0) == '\\')
+ {
+ QString parts[3];
+ for(int i = 1, part = 0; i < message.length() && part < 3; ++i)
+ {
+ bool last = (i == message.length() -1);
+ QChar c = message.at(i);
+ QChar next = '\0';
+ if(!last)
+ {
+ next = message.at(i + 1);
+ }
+ if(c == '\\')
+ {
+ if((last || next == ' ') && part == 2)
+ {
+ message = message.mid(i + 2);
+
+ pcName = parts[0];
+ CUID = parts[1].toInt();
+ user = parts[2];
+ return;
+ }
+ else
+ {
+ if(next == '\\' || next == '"')
+ {
+ i += 2;
+ parts[part].append(next);
+ }
+ else
+ {
+ ++part;
+ }
+ }
+ }
+ else
+ {
+ parts[part].append(c);
+ }
+ }
+ }
+ }
+};
+
+void PatchedClonkParser::parseMessage(const QString &line)
+{
+ QString what = line;
+
+ raw(what);
+
+ if(what.length() < 12 || what.at(0) != '[' || what.at(9) != ']' || what.at(10) != ' ')
+ {
+ return;
+ }
+
+ const QTime& msgTime = QTime::fromString(what.mid(1, 8), "hh:mm:ss");
+ what = what.mid(11);
+ rawTimed(what, msgTime);
+
+ if(what.length() > 5 && what.midRef(3, 2) == ": ")
+ {
+ const QString& type = what.left(3);
+ what = what.mid(5);
+
+ static ClientInfo* playerInfoClient = nullptr;
+
+ if(type == "Plr" && playerInfoClient != nullptr)
+ {
+ playerInfoClient->players.append(what);
+ }
+ else
+ {
+ playerInfoClient = nullptr;
+
+ if(type == "Cnt")
+ {
+ lobbyCountdown(what.toUInt());
+ return;
+ }
+ }
+
+ if(type == "Prm")
+ {
+ if(Session.Clonk.Server.players.contains(what))
+ {
+ Session.Clonk.Server.players.removeAll(what);
+ return;
+ }
+
+ for(ClientInfo& client : Session.Clonk.Clients)
+ {
+ if(client.players.contains(what))
+ {
+ client.players.removeAll(what);
+ return;
+ }
+ }
+ playerRemoved(what);
+ return;
+ }
+
+ if(type == "Wtd")
+ {
+ watchdog(what);
+ return;
+ }
+
+ QString pcName, user;
+ int CUID = 0;
+ parseClientInfo(what, pcName, CUID, user);
+ if(pcName.isEmpty())
+ {
+ return;
+ }
+
+ ClientInfo& client = getClientInfo(pcName, CUID, user);
+
+ if(type == "Pli" || type == "Pla")
+ {
+ playerInfoClient = &client;
+
+ if(type == "Pli")
+ {
+ playerInfoClient->players.clear();
+ }
+ }
+ else if(&client == &Session.Clonk.Server)
+ {
+ return;
+ }
+
+ if(type == "Msg" || type == "Mac" || type == "Snd")
+ {
+ ClonkOutputInterface::MessageType msgType = ClonkOutputInterface::Message;
+ if(type == "Mac")
+ {
+ msgType = ClonkOutputInterface::Action;
+ }
+ else if(type == "Snd")
+ {
+ msgType = ClonkOutputInterface::Sound;
+ }
+ clientMessage(client, what, msgType, msgTime);
+ }
+ else if(type == "Con")
+ {
+ clientConnected(client);
+ }
+ else if(type == "Act" || type == "Dct")
+ {
+ clientStateChanged(client, type == "Act");
+ client.activated = type == "Act";
+ }
+ else if(type == "Rem")
+ {
+ clientRemoved(client, what);
+ }
+ return;
+ }
+
+ if((what == "Los geht's!" || what == "Action go!") && Session.State == CRSMSession::Lobby)
+ {
+ gameLoading();
+ return;
+ }
+
+ if(what == "Start!")
+ {
+ gameStarted();
+ return;
+ }
+
+ if(what == "Spielstart abgebrochen." || what == "Game start aborted.")
+ {
+ lobbyCountdownAborted();
+ return;
+ }
+
+ for(const QString& text : {"Spiel konnte nicht registriert werden: ", "Could not register game: ", "FATALER FEHLER: Liga konnte nicht initialisiert werden: ", "FATAL ERROR: Could not initialize league: "})
+ {
+ if(what.startsWith(text))
+ {
+ masterserverError(what.mid(text.length()));
+ return;
+ }
+ }
+}
+
+ClientInfo &PatchedClonkParser::getClientInfo(const QString &pcName, int CUID, const QString &user)
+{
+ if(pcName == Session.Clonk.Server.pcName)
+ {
+ return Session.Clonk.Server;
+ }
+
+ if(!Session.Clonk.Clients.contains(pcName))
+ {
+ Session.Clonk.Clients.insert(pcName, ClientInfo::clonkClient(user, pcName, CUID));
+ }
+ return Session.Clonk.Clients[pcName];
+}
diff --git a/src/PatchedClonkParser.hpp b/src/PatchedClonkParser.hpp
new file mode 100644
index 0000000..dd4a661
--- /dev/null
+++ b/src/PatchedClonkParser.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "ClonkInterface.hpp"
+
+class PatchedClonkParser : public ClonkParser
+{
+public:
+ PatchedClonkParser(CRSMSession& session);
+
+ void parseMessage(const QString& line);
+
+protected:
+ ClientInfo& getClientInfo(const QString& pcName, int CUID, const QString& user);
+};
diff --git a/src/crsm.cpp b/src/crsm.cpp
index e887836..754e73c 100644
--- a/src/crsm.cpp
+++ b/src/crsm.cpp
@@ -12,8 +12,10 @@
#define MGMT_BUFFER_FILENAME "CRSM-MGMT-Buffer"
CRSM::CRSM(QObject *parent) :
- QObject(parent), Session(this)
+ QObject(parent), Session(this), parser(Session)
{
+ parser.addTarget(this);
+
qsrand((uint)QDateTime::currentMSecsSinceEpoch());
codec = QTextCodec::codecForName("Windows-1252");
@@ -212,261 +214,200 @@ QString CRSM::findClientByName(ClientInfo &info, const QString &name)
}
}
-void CRSM::readServerOutput()
+void CRSM::lobbyCountdown(unsigned int seconds)
{
- QString what(processManager->readLine().trimmed());
+ Session.CountDown = (int)seconds;
+}
- if(Config.Readline.ServerUses)
- {
- while(writtenToServer.length() > 0 && what.length() > 0 && writtenToServer.at(0) == what.at(0))
- {
- writtenToServer.remove(0, 1);
- what.remove(0, 1);
- }
+void CRSM::lobbyCountdownAborted()
+{
+ Session.CountDown = -1;
+}
- if(what.at(0) == '>')
- {
- what.remove(0, 1);
- }
- what = what.trimmed();
+void CRSM::watchdog(const QString& id)
+{
+ if(id == watchDogString)
+ {
+ watchDogString.clear();
+ watchDogTimer.start(Config.Clonk.Server.Watchdog.Interval * 1000);
}
+}
- if(what.isEmpty())
- return;
-
- if(Config.IRC.Use)
+void CRSM::clientMessage(ClientInfo& client, const QString& message, ClonkOutputInterface::MessageType type, const QTime& time)
+{
+ bool isMeMessage = (type == Action);
+ if(type == Sound)
{
- foreach(const QString &mess, what.split("\n", QString::SkipEmptyParts))
- foreach(const QString &mod, ircModIOList)
- {
- sendIrcMessage(mess, mod, false, true, true);
- }
+ Log.clonkChatLog("Sound: " + client.toString(true, true) + " " + message);
}
- out(what + "\n");
- if(what.length() < 12 || what.at(0) != '[' || what.at(9) != ']' || what.at(10) != ' ')
+ else if(isMeMessage)
{
- return;
+ Log.clonkChatLog("* " + client.toString(true, true) + " " + message);
}
-
- const QTime& msgTime = QTime::fromString(what.mid(1, 8), "hh:mm:ss");
- what = what.mid(11);
-
- Log.clonkLog(what);
-
- if(what.length() > 5 && what.midRef(3, 2) == ": ")
+ else
{
- const QString& type = what.left(3);
- what = what.mid(5);
-
- static ClientInfo* playerInfoClient = nullptr;
-
- if(type == "Plr" && playerInfoClient != nullptr)
- {
- playerInfoClient->players.append(what);
- }
- else
- {
- playerInfoClient = nullptr;
-
- if(type == "Cnt")
- {
- Session.CountDown = what.toInt();
- return;
- }
- }
-
- if(type == "Prm")
- {
- if(Session.Clonk.Server.players.contains(what))
- {
- Session.Clonk.Server.players.removeAll(what);
- return;
- }
-
- for(ClientInfo& client : Session.Clonk.Clients)
- {
- if(client.players.contains(what))
- {
- client.players.removeAll(what);
- return;
- }
- }
- return;
- }
-
- if(type == "Wtd")
- {
- if(what == watchDogString)
- {
- watchDogString.clear();
- watchDogTimer.start(Config.Clonk.Server.Watchdog.Interval * 1000);
- }
- return;
- }
+ Log.clonkChatLog(client.toString(true, true) + " " + message);
+ }
- ClientInfo* clientPtr = parseClientInfo(what);
- if(clientPtr == nullptr)
+ if(client.pcName != Config.Auto.Volatile.Clonk.ServerPCName)
+ {
+ Log.clonkUserLog(message, client, isMeMessage);
+ if(client == Session.Clonk.Admin)
{
- return;
+ checkActivity(Session.Clonk.Admin);
}
- ClientInfo& client = *clientPtr;
-
- if(type == "Pli" || type == "Pla")
+ if(client.floodCheck(Config.Clonk.Chat.AntiFlood.Count, Config.Clonk.Chat.AntiFlood.Time, QDateTime(QDate::currentDate(), time)))
{
- playerInfoClient = clientPtr;
-
- if(type == "Pli")
- {
- playerInfoClient->players.clear();
- }
+ kick(client.pcName, "Flooding! Maximal " + QString::number(Config.Clonk.Chat.AntiFlood.Count) + " Nachrichten in " + QString::number(Config.Clonk.Chat.AntiFlood.Time) + "s");
}
- else if(clientPtr == &Session.Clonk.Server)
+ else if(type == Sound)
{
return;
}
-
- if(type == "Msg" || type == "Mac" || type == "Snd")
+ else if(!isMeMessage)
{
- bool isMeMessage = (type == "Mac");
- if(type == "Snd")
- {
- Log.clonkChatLog("Sound: " + client.toString(true, true) + " " + what);
- }
- else if(isMeMessage)
- {
- Log.clonkChatLog("* " + client.toString(true, true) + " " + what);
- }
- else
- {
- Log.clonkChatLog(client.toString(true, true) + " " + what);
- }
-
- if(client.pcName != Config.Auto.Volatile.Clonk.ServerPCName)
+ QString command = getCommand(message);
+ if(!command.isEmpty())
{
- Log.clonkUserLog(what, client, isMeMessage);
- if(client == Session.Clonk.Admin)
- {
- checkActivity(Session.Clonk.Admin);
- }
- if(client.floodCheck(Config.Clonk.Chat.AntiFlood.Count, Config.Clonk.Chat.AntiFlood.Time, QDateTime(QDate::currentDate(), msgTime)))
- {
- kick(client.pcName, "Flooding! Maximal " + QString::number(Config.Clonk.Chat.AntiFlood.Count) + " Nachrichten in " + QString::number(Config.Clonk.Chat.AntiFlood.Time) + "s");
- }
- else if(type == "Snd")
+ if(!cmd(command, client))
{
- return;
+ respond(client, "Unbekannter Befehl: \"" + command + "\"!\n");
}
- else if(!isMeMessage)
- {
- QString command = getCommand(what);
- if(!command.isEmpty())
- {
- if(!cmd(command, client))
- {
- respond(client, "Unbekannter Befehl: \"" + command + "\"!\n");
- }
- }
- else if(Session.IRC.UseIngameChat)
- {
- sendIrcMessage("[Clonk]<" + client.nick+ "> " + what, Config.IRC.IngameChannel, false, false);
- }
- }
- else if(Session.IRC.UseIngameChat)
- {
- sendIrcMessage("[Clonk] " + client.nick + " " + what, Config.IRC.IngameChannel, true, false);
- }
}
- }
- else if(type == "Con")
- {
- QTimer *timer = new QTimer;
- connect(timer, SIGNAL(timeout()), &greetMapper, SLOT(map()));
- connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
- greetMapper.setMapping(timer, client.pcName);
- timer->start(1000);
- if(Session.IRC.UseIngameChat)
+ else if(Session.IRC.UseIngameChat)
{
- sendIrcMessage("[Clonk] " + client.toString() + " verbunden.", Config.IRC.IngameChannel, false, false);
+ sendIrcMessage("[Clonk]<" + client.nick+ "> " + message, Config.IRC.IngameChannel, false, false);
}
}
- else if(type == "Act" || type == "Dct")
+ else if(Session.IRC.UseIngameChat)
{
- client.activated = type == "Act";
+ sendIrcMessage("[Clonk] " + client.nick + " " + message, Config.IRC.IngameChannel, true, false);
}
- else if(type == "Rem")
- {
- writeToServer(QString(client.nick +" ist ein L34V0R!\n"));
+ }
+}
- if(Session.IRC.UseIngameChat)
- {
- sendIrcMessage("[Clonk] " + client.toString() + " entfernt (" + what + ").", Config.IRC.IngameChannel, false, false);
- }
+void CRSM::clientConnected(const ClientInfo& client)
+{
+ QTimer *timer = new QTimer;
+ connect(timer, SIGNAL(timeout()), &greetMapper, SLOT(map()));
+ connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
+ greetMapper.setMapping(timer, client.pcName);
+ timer->start(1000);
+ if(Session.IRC.UseIngameChat)
+ {
+ sendIrcMessage("[Clonk] " + client.toString() + " verbunden.", Config.IRC.IngameChannel, false, false);
+ }
+}
- if(client == Session.Clonk.Admin)
- {
- Session.Clonk.LeaveAdmins.insert(client, QDateTime::currentDateTime());
- afkAdminTimer.stop();
- Session.AfkAdmin = false;
- writeToServer("Rundenadmin wurde freigegeben.\n");
- Session.Clonk.Admin.clear();
- }
+void CRSM::clientRemoved(const ClientInfo& client, const QString& reason)
+{
+ writeToServer(QString(client.nick +" ist ein L34V0R!\n"));
- Session.Clonk.Clients.remove(client.pcName);
+ if(Session.IRC.UseIngameChat)
+ {
+ sendIrcMessage("[Clonk] " + client.toString() + " entfernt (" + reason + ").", Config.IRC.IngameChannel, false, false);
+ }
- if(Session.Clonk.Clients.size() == 0 && ((userlist.length() > 0 && !Session.UserWish) || (Session.State == CRSMSession::Running || Session.State == CRSMSession::Loading)))
- {
- processManager->closeProgFifos();
- }
- else if(Session.Clonk.Clients.size() == 0 && Config.Clonk.Server.EmptyTimer != -1 && (Session.CountDown == -1 || Session.CountDown > Config.Clonk.Server.EmptyTimer))
- {
- writeToServer("/start " + QString::number(Config.Clonk.Server.EmptyTimer) + "\n");
- }
- }
- return;
+ if(client == Session.Clonk.Admin)
+ {
+ Session.Clonk.LeaveAdmins.insert(client, QDateTime::currentDateTime());
+ afkAdminTimer.stop();
+ Session.AfkAdmin = false;
+ writeToServer("Rundenadmin wurde freigegeben.\n");
+ Session.Clonk.Admin.clear();
}
- if((what == "Los geht's!" || what == "Action go!") && Session.State == CRSMSession::Lobby)
+ Session.Clonk.Clients.remove(client.pcName);
+
+ if(Session.Clonk.Clients.size() == 0 && ((userlist.length() > 0 && !Session.UserWish) || (Session.State == CRSMSession::Running || Session.State == CRSMSession::Loading)))
{
- setSessionState(CRSMSession::Loading);
- return;
+ processManager->closeProgFifos();
}
+ else if(Session.Clonk.Clients.size() == 0 && Config.Clonk.Server.EmptyTimer != -1 && (Session.CountDown == -1 || Session.CountDown > Config.Clonk.Server.EmptyTimer))
+ {
+ writeToServer("/start " + QString::number(Config.Clonk.Server.EmptyTimer) + "\n");
+ }
+}
- if(what == "Start!")
+void CRSM::gameLoading()
+{
+ setSessionState(CRSMSession::Loading);
+}
+
+void CRSM::gameStarted()
+{
+ Stats.AddScenarioStart(Session.Scenario.wishClient, scenarioFileName(Session.Scenario.name));
+ if(!Session.League)
{
- Stats.AddScenarioStart(Session.Scenario.wishClient, scenarioFileName(Session.Scenario.name));
- if(!Session.League)
- {
- writeToServer(QString("/set maxplayer 0\n"));
- }
- setSessionState(CRSMSession::Running);
- watchdog();
- return;
+ writeToServer(QString("/set maxplayer 0\n"));
}
+ setSessionState(CRSMSession::Running);
+ watchdog();
+}
- if(what == "Spielstart abgebrochen." || what == "Game start aborted.")
+void CRSM::masterserverError(const QString& msg)
+{
+ userlist.clear();
+ if(autoHost && !hostingIsErrorDeactivated)
{
- Session.CountDown = -1;
- return;
+ hostingIsErrorDeactivated = true;
+ gameRegisterFailTimer.start();
}
+ autoHost = false;
+ static const QString gameRegisterFailMessage = "Aufgrund eines Problems beim Registrieren des Spiels am Masterserver (%1) wird Hosting temporär (für 5 Minuten) deaktiviert.\n";
+ announceInfo(gameRegisterFailMessage.arg(msg));
+}
- for(const QString& text : {"Spiel konnte nicht registriert werden: ", "Could not register game: ", "FATALER FEHLER: Liga konnte nicht initialisiert werden: ", "FATAL ERROR: Could not initialize league: "})
+void CRSM::raw(const QString &line)
+{
+ if(Config.IRC.Use)
{
- if(what.startsWith(text))
- {
- QString reason = what.mid(text.length());
- userlist.clear();
- if(autoHost && !hostingIsErrorDeactivated)
+ foreach(const QString &mess, line.split("\n", QString::SkipEmptyParts))
+ foreach(const QString &mod, ircModIOList)
{
- hostingIsErrorDeactivated = true;
- gameRegisterFailTimer.start();
+ sendIrcMessage(mess, mod, false, true, true);
}
- autoHost = false;
- static const QString gameRegisterFailMessage = "Aufgrund eines Problems beim Registrieren des Spiels am Masterserver (%1) wird Hosting temporär (für 5 Minuten) deaktiviert.\n";
- announceInfo(gameRegisterFailMessage.arg(reason));
- return;
+ }
+ out(line + "\n");
+}
+
+void CRSM::rawTimed(const QString &line, const QTime &time)
+{
+ Q_UNUSED(time);
+ Log.clonkLog(line);
+}
+
+void CRSM::playerRemoved(const QString& name)
+{
+ Q_UNUSED(name);
+}
+
+void CRSM::readServerOutput()
+{
+ QString what(processManager->readLine().trimmed());
+
+ if(Config.Readline.ServerUses)
+ {
+ while(writtenToServer.length() > 0 && what.length() > 0 && writtenToServer.at(0) == what.at(0))
+ {
+ writtenToServer.remove(0, 1);
+ what.remove(0, 1);
+ }
+
+ if(what.at(0) == '>')
+ {
+ what.remove(0, 1);
}
+ what = what.trimmed();
}
+
+ if(what.isEmpty())
+ {
+ return;
+ }
+
+ parser.parseMessage(what);
}
void CRSM::nextScen()
@@ -1608,7 +1549,7 @@ void CRSM::setupCmds()
addCommand("admin clear", &CRSM::noadmin, Clonk | IRC | Management, Admin, "Entzieht dem (IRC-)Rundenadmin seine Rechte, damit jemand anders Rundenadmin sein kann.");
addCommandGroup("client", Clonk | IRC | Management, User, "Verwaltet die verbundenen Clients.");
- addCommand("client list", &CRSM::clientlist, IRC | Management, User, "Listet alle verbundenen Clients auf.");
+ addCommand("client list", &CRSM::clientlist, IRC | Management, User, "Listet alle verbundenen Clients auf."); // TODO: optional player list
addCommand("client kick", &CRSM::passToClonkPcNameGrouped, Clonk | IRC | Management, Admin, "Kickt den angegebenen Client.", "<PC-Name¦Chatnick>");
addCommand("client observer", &CRSM::passToClonkPcNameGrouped, Clonk | IRC | Management, Admin, "Der angegebene Client muss zuschauen.", "<PC-Name¦Chatnick>");
addCommand("client deactivate", &CRSM::passToClonkPcNameGrouped, Clonk | IRC | Management, Admin, "Deaktiviert den angegebenen Client.", "<PC-Name¦Chatnick>");
@@ -2604,7 +2545,7 @@ CMD_FUNCTION_IMPL(admin)
}
else if(client.interface == IRC)
{
- ircCheckUserStatus(client, ClientInfo::ircClient(args), &CRSM::ircSetAdmin);
+ ircCheckUserStatus(client, ClientInfo::ircClient(args), &CRSM::ircSetAdmin); // use ISON?
}
return Success;
}
diff --git a/src/crsm.hpp b/src/crsm.hpp
index 34b0cc8..87886c6 100644
--- a/src/crsm.hpp
+++ b/src/crsm.hpp
@@ -22,10 +22,14 @@
#include "CmdFunctionRef.hpp"
#include "ProcessManager.hpp"
#include "CRSMConfig.hpp"
+#include "CRSMSession.hpp"
#include "CRSMStats.hpp"
#include "CRSMPackCompatibility.hpp"
#include "CRSMLogging.hpp"
+#include "ClonkInterface.hpp"
+#include "PatchedClonkParser.hpp"
+
#define CONFIG_FILE_NAME "CrServerManager.conf"
#define SESSION_FILE_NAME "CrServerManager.session"
#define CUR_SCEN_FILE_NAME "curscen.txt"
@@ -44,22 +48,6 @@
class CRSM;
using IrcCheckCallback = void (CRSM::*)(const ClientInfo&, int, const ClientInfo&);
-class ScenarioSettings {
-public:
- QString name;
- ClientInfo wishClient;
- bool league = false;
- bool randomLeague = false;
-
- ScenarioSettings(const QString& name, bool league = false) : name(name), league(league) {}
- ScenarioSettings(const QString& name, const ClientInfo& client, bool league = false) : name(name), wishClient(client), league(league) {}
- ScenarioSettings() {}
- inline bool operator ==(const ScenarioSettings& other) const
- {
- return name == other.name && league == other.league && randomLeague == other.randomLeague && wishClient == other.wishClient;
- }
-};
-
template<>
class ConfigValue<ScenarioSettings> : public ConfigValueBase {
ScenarioSettings& config;
@@ -88,8 +76,9 @@ public:
QString value() { return Util::joinEscape({ConfigValueBase::getStringValue(config.name), ConfigValueBase::getStringValue(config.league), ConfigValueBase::getStringValue(config.wishClient)}, ':', '|'); }
};
-class CRSM : public QObject
+class CRSM : public QObject, public ClonkOutputInterface
{
+ friend CRSMSession::CRSMSession(CRSM *crsm);
private:
enum IrcModOperations {
ModCheck,
@@ -122,6 +111,18 @@ public:
void start();
bool isOk();
+ void lobbyCountdown(unsigned int seconds);
+ void lobbyCountdownAborted();
+ void watchdog(const QString &id);
+ void clientMessage(ClientInfo &client, const QString &message, ClonkOutputInterface::MessageType type, const QTime& time);
+ void clientConnected(const ClientInfo &client);
+ void clientRemoved(const ClientInfo &client, const QString& reason);
+ void gameLoading();
+ void gameStarted();
+ void masterserverError(const QString &msg);
+ void raw(const QString& line);
+ void rawTimed(const QString& line, const QTime& time);
+ void playerRemoved(const QString &name);
signals:
private slots:
@@ -151,63 +152,13 @@ private slots:
private:
CRSMConfig Config;
-
- struct CRSMSession : public ConfigBase {
- enum SessionState {None = -1, Lobby = 0, Loading = 1, Running = 2};
-
- Boolean League = false;
- SessionState State = None;
- Boolean UserWish = false;
- Boolean AfkAdmin = false;
- Integer CountDown = -1;
- ScenarioSettings Scenario;
-
-
- struct {
- ClientInfo Admin;
- Map(String, ClientInfo) Clients;
- ClientInfo Server;
- QMap<ClientInfo, QDateTime> LeaveAdmins;
- } Clonk;
-
- struct {
- ClientInfo Admin;
- Boolean UseIngameChat = false;
- } IRC;
-
- void clear()
- {
- auto configVals = configValues;
- *this = CRSMSession();
- configValues = configVals;
- }
-
- CRSMSession() : ConfigBase::ConfigBase({
- ConfigVal(League),
- ConfigVal(State),
- ConfigVal(UserWish),
- ConfigVal(CountDown),
- ConfigVal(Scenario),
-
- ConfigVal(Clonk.Admin),
- ConfigVal(Clonk.Clients),
- ConfigVal(Clonk.Server),
-
- ConfigVal(IRC.Admin),
- ConfigVal(IRC.UseIngameChat),
- }) {}
-
- CRSMSession(CRSM* crsm) : CRSMSession()
- {
- addConfigValue("Hosting.UserWishes", mkConfigValue(crsm->userlist, false));
- }
- };
-
CRSMSession Session;
CRSMStats Stats;
CRSMPackCompatibility Packs;
CRSMLogging Log;
+ PatchedClonkParser parser;
+
QList<ScenarioSettings> userlist;
QList<ScenarioSettings> autolist;
QList<ScenarioSettings> nextAutoScens;