diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CRSMSession.cpp | 29 | ||||
| -rw-r--r-- | src/CRSMSession.hpp | 51 | ||||
| -rw-r--r-- | src/ClonkInterface.cpp | 95 | ||||
| -rw-r--r-- | src/ClonkInterface.hpp | 80 | ||||
| -rw-r--r-- | src/CrServerManager.pro | 10 | ||||
| -rw-r--r-- | src/PatchedClonkControl.cpp | 6 | ||||
| -rw-r--r-- | src/PatchedClonkControl.hpp | 11 | ||||
| -rw-r--r-- | src/PatchedClonkParser.cpp | 212 | ||||
| -rw-r--r-- | src/PatchedClonkParser.hpp | 14 | ||||
| -rw-r--r-- | src/crsm.cpp | 359 | ||||
| -rw-r--r-- | src/crsm.hpp | 89 |
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; |
