#pragma once #include #include #include #include #include #include #include using CRSMConfigException = std::logic_error; namespace CRSMConfigValueType { enum Type { None, String, Integer, Boolean, List, Map }; } class CRSMConfigValueBase { public: virtual void setValue(const QString&) { } virtual QString value() { return ""; } virtual CRSMConfigValueType::Type type() { return CRSMConfigValueType::Type::None; } template static Type getValue(const QString& value); template static QString getStringValue(Type val); }; template class CRSMConfigValue : public CRSMConfigValueBase { public: // CRSMConfigValue(Type&) { } virtual void setValue(const QString&) { throw CRSMConfigException("Cannot assign"); } virtual QString value() { throw CRSMConfigException("Cannot get the value"); } }; using String = QString; using Integer = int; using Port = ushort; using Boolean = bool; #define List(Type) QList #define Map(KeyType, ValueType) QMap #define ConfigValue(Value) {#Value, mkConfigValue(Value)} #define ConfigValueEx(Value, ...) {#Value, mkConfigValue(Value, __VA_ARGS__)} template<> class CRSMConfigValue : public CRSMConfigValueBase { String& config; bool trimmedQuotes = false; public: CRSMConfigValue(String& config) : config(config) { } virtual void setValue(const QString& value) { config = trimQuotes(QString(value), trimmedQuotes); } virtual QString value() { if(trimmedQuotes) { return "\"" + config + "\""; } else { return config; } } virtual CRSMConfigValueType::Type type() { return CRSMConfigValueType::Type::String; } private: QString& trimQuotes(QString&& string, bool& trimmed) { trimmed = false; if(string.length() >= 2 && string.at(0) == '"' && string.at(string.length() - 1) == '"') { string.remove(0, 1); string.remove(string.length() - 1, 1); trimmed = true; } return string; } }; template class CRSMConfigValue::value>::type> : public CRSMConfigValueBase { Type& config; public: CRSMConfigValue(Type& config) : config(config) { } virtual void setValue(const QString& value) { bool ok = true; long long longLongVal = value.toLongLong(&ok, 0); if(!ok) throw CRSMConfigException("\"" + value.toStdString() + "\" could not be parsed as an Integer"); if(longLongVal < std::numeric_limits::min() || longLongVal > std::numeric_limits::max()) throw CRSMConfigException((QString::number(longLongVal) + " is out of range (" + QString::number(std::numeric_limits::min()) + " - " + QString::number(std::numeric_limits::max()) + ")").toStdString()); config = static_cast(longLongVal); } virtual QString value() { return QString::number(config); } virtual CRSMConfigValueType::Type type() { return CRSMConfigValueType::Type::Integer; } }; template<> class CRSMConfigValue : public CRSMConfigValueBase { Boolean& config; public: CRSMConfigValue(Boolean& config) : config(config) { } virtual void setValue(const QString& value) { if(value == "true") { config = true; } else if(value == "false") { config = false; } else { throw CRSMConfigException("\"" + value.toStdString() + "\" could not be parsed as a Boolean"); } } virtual QString value() { return config ? "true" : "false"; } virtual CRSMConfigValueType::Type type() { return CRSMConfigValueType::Type::Boolean; } }; class CRSMConfigValueList : public CRSMConfigValueBase { public: virtual void append(const QString& entry) = 0; virtual void remove(const QString& entry) = 0; virtual QStringList getValues() = 0; }; class CRSMConfigValueMap : public CRSMConfigValueBase { public: virtual void setKeyValue(const QString& key, const QString& value) = 0; virtual void remove(const QString& key) = 0; virtual QMap getValues() = 0; }; template class CRSMConfigValue> : public CRSMConfigValueList { using ListType = QList; ListType& config; const bool deduplicate; public: CRSMConfigValue(ListType& config, bool deduplicate = true) : config(config), deduplicate(deduplicate) {} virtual ListType& list() { return config; } virtual CRSMConfigValueType::Type type() { return CRSMConfigValueType::Type::List; } virtual void append(const QString &entry) { Type val = CRSMConfigValueBase::getValue(entry); if(!deduplicate || !config.contains(val)) { config.append(val); } } virtual void remove(const QString &entry) { config.removeAll(CRSMConfigValueBase::getValue(entry)); } virtual QStringList getValues() { QStringList ret; foreach(Type val, config) { ret.append(CRSMConfigValueBase::getStringValue(val)); } return ret; } }; template class CRSMConfigValue> : public CRSMConfigValueMap { using MapType = QMap; MapType& config; public: CRSMConfigValue(MapType& config) : config(config) {} virtual MapType& map() { return config; } virtual CRSMConfigValueType::Type type() { return CRSMConfigValueType::Type::Map; } virtual void setKeyValue(const QString& key, const QString& value) { config[CRSMConfigValueBase::getValue(key)] = CRSMConfigValueBase::getValue(value); } virtual QMap getValues() { QMap ret; foreach(KeyType key, config.keys()) { ret[CRSMConfigValueBase::getStringValue(key)] = CRSMConfigValueBase::getStringValue(config[key]); } return ret; } virtual void remove(const QString &key) { config.remove(CRSMConfigValueBase::getValue(key)); } }; template class CRSMConfigValue::value>::type> : public CRSMConfigValue::type> { using UndType = typename std::underlying_type::type; public: CRSMConfigValue(Type& config) : CRSMConfigValue::CRSMConfigValue((UndType&)config) {} }; template CRSMConfigValue* mkConfigValue(Type& Value, Types... values) { return new CRSMConfigValue(Value, values...); } template Type CRSMConfigValueBase::getValue(const QString& value) { Type ret; CRSMConfigValue val(ret); val.setValue(value); return ret; } template QString CRSMConfigValueBase::getStringValue(Type value) { CRSMConfigValue val(value); return val.value(); } class CRSMConfigBase { public: explicit CRSMConfigBase(QMap configValues) : configValues(configValues) {} void setConfigValue(const QString& name, const QString& value); QString getConfigValueLine(const QString& name); void addConfigListEntry(const QString& name, const QString& value); void removeConfigMapListEntry(const QString &name, const QString &value); void setConfigMapValue(const QString& name, const QString& key, const QString& value); QString read(const QString& fileName, bool writeDefault = true); bool write(const QString& fileName); void setConfigLine(const QString& line); protected: void addConfigValue(QString name, CRSMConfigValueBase *value); CRSMConfigValueBase& getConfigValue(const QString &name); QMap configValues; }; class CRSMConfig : public CRSMConfigBase { public: struct { Port ManagementPort = 9372; String ListFolder = "ScenarioLists"; String CommandSign = "!"; } CRSM; struct { struct { String Arguments = "/config:config /fullscreen /lobby:300 /nosignup Objects.c4d"; String Config = "config"; String Executable = "clonk-server"; List(String) IgnoreFolders = {"Network", "Records.c4f", "Savegames.c4f"}; Integer EmptyTimer = 60; } Server; struct { struct { Integer Count = 5; Integer Time = 3; } AntiFlood; Map(Integer, String) Moderators; Integer RegainAdminTime = 120; } Chat; } Clonk; struct { Boolean Auto = true; Boolean RandomizeAuto = true; Integer MaxWishesPerScen = 2; Integer MaxWishesPerUser = 2; Integer UserListLength = 5; Map(String, String) Alias; } Hosting; struct { Boolean Use = false; String Server = "irc.euirc.net"; String Nick = "CRSM"; String Password = ""; String RealName = "Dedicated Clonk server powered by CRSM"; String Channel = "#crsm"; Boolean UseIngameChat = false; String IngameChannel = "#crsm-ingame"; String QuitMessage = "Dedicated Clonk server powered by CRSM"; Integer ReconnectDelay = 10; String ScenListMessage = "A list of available scenarios is available ingame or in a lobby."; Map(String, String) Moderators; } IRC; struct { struct { String ReattachId = ""; } ProcessManager; struct { struct { String Directory; String ServerNick; String ServerPCName; } Clonk; } Volatile; struct { Map(String, String) AliasWishes; } Hosting; } Auto; struct { Boolean ServerUses = false; Boolean PromptEnabled = false; Integer RereadLimit = 50; } Readline; explicit CRSMConfig() : CRSMConfigBase({ ConfigValue(CRSM.ManagementPort), ConfigValue(CRSM.ListFolder), ConfigValue(CRSM.CommandSign), ConfigValue(Clonk.Server.Arguments), ConfigValue(Clonk.Server.Config), ConfigValue(Clonk.Server.Executable), ConfigValue(Clonk.Server.EmptyTimer), ConfigValue(Clonk.Server.IgnoreFolders), ConfigValue(Clonk.Chat.AntiFlood.Count), ConfigValue(Clonk.Chat.AntiFlood.Time), ConfigValue(Clonk.Chat.Moderators), ConfigValue(Clonk.Chat.RegainAdminTime), ConfigValue(Hosting.Auto), ConfigValue(Hosting.RandomizeAuto), ConfigValue(Hosting.MaxWishesPerScen), ConfigValue(Hosting.MaxWishesPerUser), ConfigValue(Hosting.UserListLength), ConfigValue(Hosting.Alias), ConfigValue(IRC.Use), ConfigValue(IRC.Server), ConfigValue(IRC.Nick), ConfigValue(IRC.Password), ConfigValue(IRC.RealName), ConfigValue(IRC.Channel), ConfigValue(IRC.UseIngameChat), ConfigValue(IRC.IngameChannel), ConfigValue(IRC.QuitMessage), ConfigValue(IRC.ReconnectDelay), ConfigValue(IRC.ScenListMessage), ConfigValue(IRC.Moderators), ConfigValue(Auto.ProcessManager.ReattachId), ConfigValue(Auto.Hosting.AliasWishes), ConfigValue(Readline.ServerUses), ConfigValue(Readline.PromptEnabled), ConfigValue(Readline.RereadLimit) }) {} void clear(); };