/*
* The Mana Server
* Copyright (C) 2013 The Mana World Development Team
*
* This file is part of The Mana Server.
*
* The Mana Server is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* The Mana Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with The Mana Server. If not, see .
*/
#include "resources/settingsmanager.h"
#include "configuration.h"
#include "resources/attributes.h"
#include "resources/hairdb.h"
#include "resources/itemdb.h"
#include "resources/monsterdb.h"
#include "resources/specialdb.h"
#include "resources/npcdb.h"
#include "resources/emotedb.h"
#include "statuseffect.h"
#include "units.h"
#include "net/net.h"
#include "utils/xml.h"
#include "utils/path.h"
#include "log.h"
namespace SettingsManager
{
static std::string mSettingsFile;
static std::set mIncludedFiles;
static bool loadFile(const std::string &filename);
void load()
{
// initialize managers
paths.clear();
Attributes::init();
hairDB.init();
itemDb->init();
MonsterDB::init();
SpecialDB::init();
NPCDB::init();
EmoteDB::init();
StatusEffect::init();
Units::init();
// load stuff from settings
if (!loadFile("settings.xml"))
{
// fall back to loading certain known files for TMW compatibility
loadFile("paths.xml");
loadFile("items.xml");
loadFile("monsters.xml");
loadFile("npcs.xml");
loadFile("emotes.xml");
loadFile("status-effects.xml");
loadFile("itemcolors.xml");
loadFile("units.xml");
}
Attributes::checkStatus();
hairDB.checkStatus();
itemDb->checkStatus();
MonsterDB::checkStatus();
SpecialDB::checkStatus();
NPCDB::checkStatus();
EmoteDB::checkStatus();
StatusEffect::checkStatus();
Units::checkStatus();
if (Net::getNetworkType() == ServerType::MANASERV)
{
Attributes::informItemDB();
}
}
void unload()
{
StatusEffect::unload();
EmoteDB::unload();
NPCDB::unload();
SpecialDB::unload();
MonsterDB::unload();
if (itemDb)
itemDb->unload();
hairDB.unload();
Attributes::unload();
}
/**
* Loads a settings file.
*/
static bool loadFile(const std::string &filename)
{
logger->log("Loading game settings from %s", filename.c_str());
XML::Document doc(filename);
xmlNodePtr node = doc.rootNode();
// add file to include set
mIncludedFiles.insert(filename);
// FIXME: check root node's name when bjorn decides it's time
if (!node /*|| !xmlStrEqual(node->name, BAD_CAST "settings") */)
{
logger->log("Settings Manager: %s is not a valid settings file!", filename.c_str());
return false;
}
if (xmlStrEqual(node->name, BAD_CAST "monsters"))
{
if (XML::hasProperty(node, "offset"))
{
MonsterDB::setMonsterIdOffset(XML::getProperty(node, "offset", 0));
}
}
// go through every node
for_each_xml_child_node(childNode, node)
{
if (childNode->type != XML_ELEMENT_NODE)
continue;
if (xmlStrEqual(childNode->name, BAD_CAST "include"))
{
// include an other file
std::string includeFile = XML::getProperty(childNode, "file", std::string());
if (!includeFile.empty())
{
// build absolute path path
const utils::splittedPath splittedPath = utils::splitFileNameAndPath(filename);
includeFile = utils::cleanPath(utils::joinPaths(splittedPath.path, includeFile));
}
else
{
// try to get name property, which has an absolute value
includeFile = XML::getProperty(childNode, "name", std::string());
}
// check if file property was given
if (!includeFile.empty())
{
// check if we're not entering a loop
if (mIncludedFiles.find(includeFile) != mIncludedFiles.end())
{
logger->log("Warning: Circular include loop detecting while including %s from %s", includeFile.c_str(), filename.c_str());
}
else
{
loadFile(includeFile);
}
}
else
{
logger->log("Warning: element without 'file' or 'name' attribute in %s", filename.c_str());
}
}
else if (xmlStrEqual(childNode->name, BAD_CAST "option"))
{
// options from paths.xml
std::string name = XML::getProperty(childNode, "name", std::string());
std::string value = XML::getProperty(childNode, "value", std::string());
if (!name.empty())
paths.setValue(name, value);
else
logger->log("Warning: option without a name found in %s", filename.c_str());
}
else if (xmlStrEqual(childNode->name, BAD_CAST "attribute"))
{
// map config
Attributes::readAttributeNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "points"))
{
Attributes::readPointsNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "color"))
{
hairDB.readHairColorNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "list"))
{
// todo: consider if we need a "color DB", but in tmwa clientdata
// I only see hair colors in the itemcolors.xml file.
const std::string name = XML::getProperty(childNode, "name", std::string());
if (name == "hair")
{
for_each_xml_child_node(hairColorNode, childNode)
{
if (xmlStrEqual(hairColorNode->name, BAD_CAST "color"))
hairDB.readHairColorNode(hairColorNode, filename);
}
}
}
else if (xmlStrEqual(childNode->name, BAD_CAST "item"))
{
itemDb->readItemNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "monster"))
{
MonsterDB::readMonsterNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "special-set"))
{
SpecialDB::readSpecialSetNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "npc"))
{
NPCDB::readNPCNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "emote"))
{
EmoteDB::readEmoteNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "status-effect") || xmlStrEqual(childNode->name, BAD_CAST "stun-effect"))
{
StatusEffect::readStatusEffectNode(childNode, filename);
}
else if (xmlStrEqual(childNode->name, BAD_CAST "unit"))
{
Units::readUnitNode(childNode, filename);
}
else
{
// compatibility stuff with older configs/games
if (xmlStrEqual(node->name, BAD_CAST "specials") && xmlStrEqual(childNode->name, BAD_CAST "set"))
{
// specials.xml:/specials/set
SpecialDB::readSpecialSetNode(childNode, filename);
}
}
}
mIncludedFiles.erase(filename);
return true;
}
}