summaryrefslogtreecommitdiff
path: root/src/configuration.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/configuration.cpp')
-rw-r--r--src/configuration.cpp451
1 files changed, 370 insertions, 81 deletions
diff --git a/src/configuration.cpp b/src/configuration.cpp
index 5fc4eabd..ead228c4 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -1,7 +1,7 @@
/*
* The Mana Client
* Copyright (C) 2004-2009 The Mana World Development Team
- * Copyright (C) 2009-2012 The Mana Developers
+ * Copyright (C) 2009-2024 The Mana Developers
*
* This file is part of The Mana Client.
*
@@ -33,16 +33,6 @@ void ConfigurationObject::setValue(const std::string &key,
mOptions[key] = value;
}
-void Configuration::setValue(const std::string &key, const std::string &value)
-{
- ConfigurationObject::setValue(key, value);
-
- // Notify listeners
- Event event(Event::ConfigOptionChanged);
- event.setString("option", key);
- event.trigger(Event::ConfigChannel);
-}
-
std::string ConfigurationObject::getValue(const std::string &key,
const std::string &deflt) const
{
@@ -70,19 +60,8 @@ double ConfigurationObject::getValue(const std::string &key,
return iter != mOptions.end() ? atof(iter->second.c_str()) : deflt;
}
-void ConfigurationObject::deleteList(std::list<ConfigurationObject*> &list)
-{
- for (auto element : list)
- delete element;
-
- list.clear();
-}
-
void ConfigurationObject::clear()
{
- for (auto &[_, list] : mContainerOptions)
- deleteList(list);
-
mOptions.clear();
}
@@ -212,33 +191,13 @@ void ConfigurationObject::initFromXML(XML::Node parent_node)
for (auto node : parent_node.children())
{
- if (node.name() == "list")
+ if (node.name() == "option")
{
- // List option handling.
std::string name = node.getProperty("name", std::string());
- for (auto subnode : node.children())
- {
- if (subnode.name() == name)
- {
- auto *cobj = new ConfigurationObject;
-
- cobj->initFromXML(subnode); // Recurse
-
- mContainerOptions[name].push_back(cobj);
- }
- }
-
- }
- else if (node.name() == "option")
- {
- // Single option handling.
- std::string name = node.getProperty("name", std::string());
- std::string value = node.getProperty("value", std::string());
-
if (!name.empty())
- mOptions[name] = value;
- } // Otherwise ignore
+ mOptions[name] = node.getProperty("value", std::string());
+ }
}
}
@@ -251,15 +210,15 @@ void Configuration::init(const std::string &filename, bool useResManager)
else
mConfigPath = filename;
- if (!doc.rootNode())
+ XML::Node rootNode = doc.rootNode();
+
+ if (!rootNode)
{
logger->log("Couldn't open configuration file: %s", filename.c_str());
return;
}
- XML::Node rootNode = doc.rootNode();
-
- if (!rootNode || rootNode.name() != "configuration")
+ if (rootNode.name() != "configuration")
{
logger->log("Warning: No configuration file (%s)", filename.c_str());
return;
@@ -268,57 +227,387 @@ void Configuration::init(const std::string &filename, bool useResManager)
initFromXML(rootNode);
}
-void ConfigurationObject::writeToXML(XML::Writer &writer) const
+
+template<typename T>
+struct Option
+{
+ Option(const char *name, const T &value, const T &defaultValue)
+ : name(name), value(value), defaultValue(defaultValue)
+ {}
+
+ const char *name;
+ const T &value;
+ const T &defaultValue;
+};
+
+template<typename T>
+static void serialize(XML::Writer &writer, const Option<T> &option)
+{
+ if (option.value == option.defaultValue)
+ return;
+
+ writer.startElement("option");
+ writer.addAttribute("name", option.name);
+ writer.addAttribute("value", option.value);
+ writer.endElement();
+}
+
+static void serialize(XML::Writer &writer, const ItemShortcutEntry &itemShortcut)
+{
+ writer.startElement("itemshortcut");
+ writer.addAttribute("index", itemShortcut.index);
+ writer.addAttribute("id", itemShortcut.itemId);
+ writer.endElement();
+}
+
+static void serialize(XML::Writer &writer, const EmoteShortcutEntry &emoteShortcut)
+{
+ writer.startElement("emoteshortcut");
+ writer.addAttribute("index", emoteShortcut.index);
+ writer.addAttribute("id", emoteShortcut.emoteId);
+ writer.endElement();
+}
+
+static void serialize(XML::Writer &writer, const Outfit &outfit)
+{
+ writer.startElement("outfit");
+ writer.addAttribute("index", outfit.index);
+ writer.addAttribute("items", outfit.items);
+ writer.addAttribute("unequip", outfit.unequip);
+ writer.endElement();
+}
+
+static void serialize(XML::Writer &writer, const UserColor &color)
+{
+ if (!color.color.empty())
+ writer.addAttribute("color", color.color);
+
+ writer.addAttribute("gradient", color.gradient);
+
+ if (color.delay)
+ writer.addAttribute("delay", *color.delay);
+}
+
+static void serialize(XML::Writer &writer, const WindowState &state)
{
- for (auto &[name, value] : mOptions)
+ if (state.x) writer.addAttribute("x", *state.x);
+ if (state.y) writer.addAttribute("y", *state.y);
+ if (state.width) writer.addAttribute("width", *state.width);
+ if (state.height) writer.addAttribute("height", *state.height);
+ if (state.visible) writer.addAttribute("visible", *state.visible);
+ if (state.sticky) writer.addAttribute("sticky", *state.sticky);
+}
+
+static const char *serverTypeToString(ServerType type)
+{
+ switch (type)
{
- writer.startElement("option");
- writer.addAttribute("name", name);
- writer.addAttribute("value", value);
- writer.endElement();
+ case ServerType::TMWATHENA:
+ return "TmwAthena";
+ case ServerType::MANASERV:
+ return "ManaServ";
+ default:
+ return "";
}
+}
- for (auto &[name, list] : mContainerOptions)
- {
- writer.startElement("list");
- writer.addAttribute("name", name);
+static void serialize(XML::Writer &writer, const ServerInfo &server)
+{
+ writer.startElement("server");
- // Recurse on all elements
- for (auto element : list)
- {
- writer.startElement(name.c_str());
- element->writeToXML(writer);
- writer.endElement();
- }
+ writer.addAttribute("name", server.name);
+ writer.addAttribute("type", serverTypeToString(server.type));
+
+ writer.startElement("connection");
+ writer.addAttribute("hostname", server.hostname);
+ writer.addAttribute("port", server.port);
+ writer.endElement(); // connection
+
+ if (!server.description.empty())
+ {
+ writer.startElement("description");
+ writer.writeText(server.description);
+ writer.endElement();
+ }
+ if (!server.persistentIp)
+ {
+ writer.startElement("persistentIp");
+ writer.writeText(server.persistentIp ? "1" : "0");
writer.endElement();
}
+
+ writer.endElement(); // server
+}
+
+template<typename T>
+void serdeOptions(T option)
+{
+ option("OverlayDetail", &Config::overlayDetail);
+ option("speechBubblecolor", &Config::speechBubblecolor);
+ option("speechBubbleAlpha", &Config::speechBubbleAlpha);
+ option("speech", &Config::speech);
+ option("visiblenames", &Config::visibleNames);
+ option("showgender", &Config::showGender);
+ option("showMonstersTakedDamage", &Config::showMonstersTakedDamage);
+ option("showWarps", &Config::showWarps);
+ option("particleMaxCount", &Config::particleMaxCount);
+ option("particleFastPhysics", &Config::particleFastPhysics);
+ option("particleEmitterSkip", &Config::particleEmitterSkip);
+ option("particleeffects", &Config::particleEffects);
+ option("logToStandardOut", &Config::logToStandardOut);
+ option("opengl", &Config::opengl);
+ option("vsync", &Config::vsync);
+ option("windowmode", &Config::windowMode);
+ option("screenwidth", &Config::screenWidth);
+ option("screenheight", &Config::screenHeight);
+ option("scale", &Config::scale);
+ option("sound", &Config::sound);
+ option("sfxVolume", &Config::sfxVolume);
+ option("notificationsVolume", &Config::notificationsVolume);
+ option("musicVolume", &Config::musicVolume);
+ option("fpslimit", &Config::fpsLimit);
+
+ option("remember", &Config::remember);
+ option("username", &Config::username);
+ option("lastCharacter", &Config::lastCharacter);
+ option("updatehost", &Config::updatehost);
+ option("screenshotDirectory", &Config::screenshotDirectory);
+ option("screenshotDirectorySuffix", &Config::screenshotDirectorySuffix);
+ option("useScreenshotDirectorySuffix", &Config::useScreenshotDirectorySuffix);
+
+ option("EnableSync", &Config::enableSync);
+
+ option("joystickEnabled", &Config::joystickEnabled);
+ option("upTolerance", &Config::upTolerance);
+ option("downTolerance", &Config::downTolerance);
+ option("leftTolerance", &Config::leftTolerance);
+ option("rightTolerance", &Config::rightTolerance);
+
+ option("logNpcInGui", &Config::logNpcInGui);
+ option("download-music", &Config::downloadMusic);
+ option("guialpha", &Config::guiAlpha);
+ option("ChatLogLength", &Config::chatLogLength);
+ option("enableChatLog", &Config::enableChatLog);
+ option("whispertab", &Config::whisperTab);
+ option("customcursor", &Config::customCursor);
+ option("showownname", &Config::showOwnName);
+ option("showpickupparticle", &Config::showPickupParticle);
+ option("showpickupchat", &Config::showPickupChat);
+ option("showMinimap", &Config::showMinimap);
+ option("fontSize", &Config::fontSize);
+ option("ReturnToggles", &Config::returnTogglesChat);
+ option("ScrollLaziness", &Config::scrollLaziness);
+ option("ScrollRadius", &Config::scrollRadius);
+ option("ScrollCenterOffsetX", &Config::scrollCenterOffsetX);
+ option("ScrollCenterOffsetY", &Config::scrollCenterOffsetY);
+ option("onlineServerList", &Config::onlineServerList);
+ option("theme", &Config::theme);
+ option("disableTransparency", &Config::disableTransparency);
+
+ option("persistent-player-list", &Config::persistentPlayerList);
+ option("player-ignore-strategy", &Config::playerIgnoreStrategy);
+ option("default-player-permissions", &Config::defaultPlayerPermissions);
}
-void Configuration::write()
+void serialize(XML::Writer &writer, const Config &config)
{
- // Do not attempt to write to file that cannot be opened for writing
- FILE *testFile = fopen(mConfigPath.c_str(), "w");
- if (!testFile)
+ const Config defaults;
+ auto serializeOption = [&](const char *name, auto member) {
+ serialize(writer, Option { name, config.*member, defaults.*member });
+ };
+
+ writer.startElement("configuration");
+
+ serdeOptions(serializeOption);
+
+ for (const auto &[name, value] : config.unknownOptions)
+ serialize(writer, Option { name.c_str(), value, std::string() });
+
+ for (const auto &[action, key] : config.keys)
{
- logger->log("Configuration::write() couldn't open %s for writing",
- mConfigPath.c_str());
- return;
+ writer.startElement("key");
+ writer.addAttribute("action", action);
+ writer.addAttribute("key", key);
+ writer.endElement();
}
- fclose(testFile);
+ for (auto &itemShortcut : config.itemShortcuts)
+ serialize(writer, itemShortcut);
+ for (auto &emoteShortcut : config.emoteShortcuts)
+ serialize(writer, emoteShortcut);
- XML::Writer writer(mConfigPath);
+ for (auto &outfit : config.outfits)
+ serialize(writer, outfit);
- if (!writer.isValid())
+ for (auto &[type, color] : config.colors)
{
- logger->log("Configuration::write() error while creating writer");
- return;
+ writer.startElement("color");
+ writer.addAttribute("type", type);
+
+ serialize(writer, color);
+
+ writer.endElement();
}
- logger->log("Configuration::write() writing configuration...");
+ for (const auto &[name, state] : config.windows)
+ {
+ writer.startElement("window");
+ writer.addAttribute("name", name);
- writer.startElement("configuration");
- writeToXML(writer);
+ serialize(writer, state);
+
+ writer.endElement(); // window
+ }
+
+ for (const auto &server : config.servers)
+ {
+ if (server.save && server.isValid())
+ serialize(writer, server);
+ }
+
+ for (const auto &[name, relation] : config.players)
+ {
+ writer.startElement("player");
+ writer.addAttribute("name", name);
+ writer.addAttribute("relation", static_cast<int>(relation));
+ writer.endElement();
+ }
+
+ writer.endElement(); // configuration
+}
+
+void deserialize(XML::Node node, ItemShortcutEntry &itemShortcut)
+{
+ node.attribute("index", itemShortcut.index);
+ node.attribute("id", itemShortcut.itemId);
+}
+
+void deserialize(XML::Node node, EmoteShortcutEntry &emoteShortcut)
+{
+ node.attribute("index", emoteShortcut.index);
+ node.attribute("id", emoteShortcut.emoteId);
+}
+
+void deserialize(XML::Node node, Outfit &outfit)
+{
+ node.attribute("index", outfit.index);
+ node.attribute("items", outfit.items);
+ node.attribute("unequip", outfit.unequip);
+}
+
+void deserialize(XML::Node node, UserColor &color)
+{
+ node.attribute("color", color.color);
+ node.attribute("gradient", color.gradient);
+ node.attribute("delay", color.delay);
+}
+
+void deserialize(XML::Node node, WindowState &state)
+{
+ node.attribute("x", state.x);
+ node.attribute("y", state.y);
+ node.attribute("width", state.width);
+ node.attribute("height", state.height);
+ node.attribute("visible", state.visible);
+ node.attribute("sticky", state.sticky);
+}
+
+void deserialize(XML::Node node, ServerInfo &server)
+{
+ node.attribute("name", server.name);
+
+ std::string type;
+ node.attribute("type", type);
+ server.type = ServerInfo::parseType(type);
+
+ for (auto node : node.children()) {
+ if (node.name() == "connection") {
+ node.attribute("hostname", server.hostname);
+ node.attribute("port", server.port);
+ } else if (node.name() == "description") {
+ server.description = node.textContent();
+ } else if (node.name() == "persistentIp") {
+ const std::string value { node.textContent() };
+ server.persistentIp = getBoolFromString(value, server.persistentIp);
+ }
+ }
+}
+
+void deserialize(XML::Node node, Config &config)
+{
+ std::map<std::string, std::string> options;
+
+ for (auto node : node.children()) {
+ if (node.name() == "option") {
+ std::string name;
+ if (!node.attribute("name", name))
+ continue;
+ node.attribute("value", options[name]);
+ } else if (node.name() == "list") {
+ // Backwards compatibility for old configuration files
+ for (auto node : node.children()) {
+ if (node.name() == "player") {
+ std::string playerName;
+ PlayerRelation relation = PlayerRelation::NEUTRAL;
+
+ for (auto node : node.children()) {
+ if (node.name() == "option") {
+ std::string optionName;
+
+ if (node.attribute("name", optionName)) {
+ if (optionName == "name")
+ node.attribute("value", playerName);
+ else if (optionName == "relation")
+ node.attribute("value", relation);
+ }
+ }
+ }
+
+ if (!playerName.empty())
+ config.players[playerName] = relation;
+ }
+ }
+ } else if (node.name() == "key") {
+ std::string action;
+ node.attribute("action", action);
+ if (!action.empty())
+ node.attribute("key", config.keys[action]);
+ } else if (node.name() == "itemshortcut") {
+ deserialize(node, config.itemShortcuts.emplace_back());
+ } else if (node.name() == "emoteshortcut") {
+ deserialize(node, config.emoteShortcuts.emplace_back());
+ } else if (node.name() == "outfit") {
+ deserialize(node, config.outfits.emplace_back());
+ } else if (node.name() == "color") {
+ std::string type;
+ node.attribute("type", type);
+ deserialize(node, config.colors[type]);
+ } else if (node.name() == "window") {
+ std::string name;
+ node.attribute("name", name);
+ deserialize(node, config.windows[name]);
+ } else if (node.name() == "player") {
+ std::string name;
+ node.attribute("name", name);
+ if (!name.empty())
+ node.attribute("relation", config.players[name]);
+ } else if (node.name() == "server") {
+ deserialize(node, config.servers.emplace_back());
+ }
+ }
+
+ auto deserializeOption = [&](const char *name, auto member) {
+ auto it = options.find(name);
+ if (it == options.end())
+ return;
+
+ fromString(it->second.data(), config.*member);
+ options.erase(it);
+ };
+
+ serdeOptions(deserializeOption);
+
+ config.unknownOptions = std::move(options);
}