#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CmdFunctionRef.hpp" #include "ProcessManager.hpp" #include "CRSMConfig.hpp" #include "CRSMStats.hpp" #include "CRSMPackCompatibility.hpp" #include "CRSMLogging.hpp" #define CONFIG_FILE_NAME "CrServerManager.conf" #define SESSION_FILE_NAME "CrServerManager.session" #define CUR_SCEN_FILE_NAME "curscen.txt" #define LAST_SCEN_FILE_NAME "lastscen.txt" #define SCOREBOARD_FILE_NAME Config.Auto.Volatile.Clonk.Directory + "scoreboard.html" #define IRC_CHECK_CALLBACK(name) void name(const ClientInfo& requester, int status, const ClientInfo& subject) #define IRC_CHECK_CALLBACK_IMPL(name) void CRSM::name(const ClientInfo& requester, int status, const ClientInfo& subject) { (void)requester; (void)status; (void)subject; #ifdef Q_OS_LINUX64 #define C4GROUP_EXECUTABLE "c4group64" #else #define C4GROUP_EXECUTABLE "c4group" #endif 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 : public ConfigValueBase { ScenarioSettings& config; public: ConfigValue(ScenarioSettings& config) : config(config) { } void setValue(const QString& val) { QStringList parts = Util::splitEscaped(val, ':', '|'); if(parts.length() != 3) { throw ConfigException("Cannot read corrupt ScenarioSettings"); } else { config.name = ConfigValueBase::getValue(parts.first()); parts.removeFirst(); config.league = ConfigValueBase::getValue(parts.first()); parts.removeFirst(); config.randomLeague = false; config.wishClient = ConfigValueBase::getValue(parts.first()); } } QString value() { return Util::joinEscape({ConfigValueBase::getStringValue(config.name), ConfigValueBase::getStringValue(config.league), ConfigValueBase::getStringValue(config.wishClient)}, ':', '|'); } }; class CRSM : public QObject { private: enum IrcModOperations { ModCheck, ModInfo, SkipScen, ClearUserList, SkipCurrentScen, Autohost, NoAutohost, ModHelp, Kill, IO, CheckOnly, NewAlias, AliasWishes, IngameChat }; enum RespondType { Normal, Notice, Private, PrivateNotice }; Q_OBJECT public: explicit CRSM(QObject *parent = 0); ~CRSM(); void start(); bool isOk(); signals: private slots: void readServerOutput(); void nextScen(); void scenarioFinished(); void ircMessageReceived(IrcMessage* message); void ircConnected(); void greet(QString pcName); void newManagementConnection(); void newManagementData(); void managementConnectionDisconnected(); void dccChatRequest(const ClientInfo &client, QString extraArgs = ""); void newDCCConnection(); void newDCCData(); void disconnectedDCCConnection(); void updateNextAutoScens(); void reconnectIrc(); void enableAutoHosting(); void afkAdminTimeout(); 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; QMap 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(IRC.Admin), ConfigVal(IRC.UseIngameChat), }) {} CRSMSession(CRSM* crsm) : CRSMSession() { addConfigValue("Hosting.UserWishes", mkConfigValue(crsm->userlist, false)); } }; CRSMSession Session; CRSMStats Stats; CRSMPackCompatibility Packs; CRSMLogging Log; QList userlist; QList autolist; QList nextAutoScens; QStringList args; QStringList ircModChecks; QStringList ircMods; QMap>> ircStatusFifos; QMap>> ircModFifos; QMap ircSayQueryFifos; QString aliasWishEditor = ""; QString currentAliasWish = ""; int current; bool finish; IrcConnection *connection = 0; bool autoHost = true; QSignalMapper greetMapper; QFile *logfile; QTextCodec *codec; QStringList ircModIOList; QList ircModWatchList; QString writtenToServer; QMap cmds; QStringList cmdGroups; ProcessManager* processManager = nullptr; QTcpServer managementServer; QMap managementConnections; QTimer gameRegisterFailTimer; QTimer afkAdminTimer; bool ok = false; bool hostingIsErrorDeactivated = false; struct DCCConnection { ClientInfo client; QTcpSocket* socket; }; QMap dccSocketConnections; QMap dccNickConnections; QMap dccConnectionIdentifiers; QTcpServer dccServer; void startScen(const ScenarioSettings& scen, QStringList); void readConfig(); void readScenarios(); void listC4Folders(); void cleanUp(); QString caseInsensitive(QString name, QString dir = "", QString trySuffix = ""); QString scenPath(QString scenName); QString listScenarios(QString commandArgs); QString printQueue(); void ircCheckModCmd(const QString &nick, CmdFunctionRef func, QString arg = ""); QString skipScen(int pos); bool skipCurrent(); void writeToServer(const QString& message); void writeConfig(); QString addAliasWish(const QString& param); void informModsAboutAliasWish(); void editAliasWishes(); void editAliasWishes(const QString &message); void stopAliasWishEditing(); QString ircActivateIngameChat(bool activated = true); QStringList listC4Folder(const QString &path); void ircSetIngameChannelTopic(); void setSessionState(CRSMSession::SessionState state); void addCommand(const QString& name, CmdFunction func, int interfaces = Clonk | IRC, UserType userType = User, const QString& shortDescription = "", const QString &argList = "", const QString &longDescription = ""); inline void addCommandGroup(const QString& name, int interfaces = Clonk | IRC, UserType userType = User, const QString& shortDescription = "", const QString &longDescription = "", CmdFunction defaultFunc = &CRSM::groupinfo); bool cmdExists(const QString& name, ClientInterface interface); CmdFunctionRef* findCommand(const QString& cmd, ClientInterface interface, QString& args); CmdFunctionRef* findCommand(QStringList &&cmd, ClientInterface interface, QString& args); bool cmd(const QString &cmd, const ClientInfo& client); void rightsFailMessage(const ClientInfo& info, UserType minUserType); UserType clientUserType(const ClientInfo& client); void setupCmds(); void respond(const ClientInfo& client, const QString& message, const RespondType type = Normal, bool allowDCCNotice = false); void ircCheckUserStatus(const ClientInfo& requester, const ClientInfo& subject, IrcCheckCallback callback); void setIngameAdmin(const ClientInfo &client, const QString& newAdmin); QFile outputBuffer; void out(const QString& text); void replayOutputBuffer(QTcpSocket *socket, bool clear = true); bool scenAllowed(const ScenarioSettings& scen, const ClientInfo& client, UserType userType); void kick(const QString& pcName, const QString& reason = ""); void prepareAndConnectIrc(); ClientInfo& getClientInfo(const QString& pcName, int cuid, const QString& user); ClientInfo* parseClientInfo(QString& message); void exit(); void applyConfig(); QString scenarioFileName(QString name); CmdResult callCommand(const CmdFunctionRef& func, const QString& args, const ClientInfo& client, UserType userType); void writeFiles(bool writeSession = false, bool writeNoConfig = false); void checkActivity(ClientInfo& client); int findWishFromUser(const ClientInfo& client); QString getCommand(const QString& message); bool isChannelName(const QString& name); void removeCommandSuffixes(QString& command); void substituteCommandAlias(QString& command); QString clientModName(const ClientInfo& client); void sendIrcMessage(const QString& message, const QString& target, bool action, bool notice, bool noticeOrDCC = false); bool greetAllowed(const ClientInfo& client); void handleIrcMessage(const ClientInfo& client, QString message, const QString& target, bool privateMessage, bool action, bool own = false); CMD_FUNCTION(help); CMD_FUNCTION(passToClonk); CMD_FUNCTION(passToClonkOnOff); CMD_FUNCTION(passToClonkNumeric); CMD_FUNCTION(passToClonkNumericOrEmpty); CMD_FUNCTION(afkAdmin); CMD_FUNCTION(admin); CMD_FUNCTION(getAdmin); CMD_FUNCTION(host); CMD_FUNCTION(list); CMD_FUNCTION(queue); CMD_FUNCTION(aliaswish); CMD_FUNCTION(ircchat); CMD_FUNCTION(autohost); CMD_FUNCTION(skip); CMD_FUNCTION(next); CMD_FUNCTION(kill); CMD_FUNCTION(clear); CMD_FUNCTION(aliaswishes); CMD_FUNCTION(newalias); CMD_FUNCTION(deletealias); CMD_FUNCTION(modinfo); CMD_FUNCTION(io); CMD_FUNCTION(passToClonkPcName); CMD_FUNCTION(ingameadmin); CMD_FUNCTION(ircadmin); CMD_FUNCTION(noadmin); CMD_FUNCTION(clientlist); CMD_FUNCTION(setRaw); CMD_FUNCTION(nodebug); CMD_FUNCTION(join); CMD_FUNCTION(leave); CMD_FUNCTION(exit); CMD_FUNCTION(exitDetach); CMD_FUNCTION(exitUpdate); CMD_FUNCTION(exitAfter); CMD_FUNCTION(reload); CMD_FUNCTION(saveConfig); CMD_FUNCTION(reconnectIrc); CMD_FUNCTION(groupinfo); CMD_FUNCTION(grouphelp); CMD_FUNCTION(relist); CMD_FUNCTION(getConfigValue); CMD_FUNCTION(setConfigValue); CMD_FUNCTION(ircSay); CMD_FUNCTION(ircWatch); CMD_FUNCTION(ircRaw); CMD_FUNCTION(packsList); CMD_FUNCTION(packsDirectory); CMD_FUNCTION(packsVersionsAdd); CMD_FUNCTION(packsVersionsDelete); CMD_FUNCTION(packsVersionsDefault); CMD_FUNCTION(packsScenariosAdd); CMD_FUNCTION(packsScenariosDelete); CMD_FUNCTION(packsScenariosList); CMD_FUNCTION(packsScenOptionsGet); CMD_FUNCTION(packsScenOptionsSet); CMD_FUNCTION(dccConnect); CMD_FUNCTION(dccDisconnect); CMD_FUNCTION(dccIdentify); IRC_CHECK_CALLBACK(ircSetAdmin); IRC_CHECK_CALLBACK(ircModCmd); IRC_CHECK_CALLBACK(ircSayQuery); };