421 lines
10 KiB
C++
421 lines
10 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program 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 Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
#include "stdpch.h"
|
|
#include "stat_db_tree.h"
|
|
#include "game_share/utils.h"
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
|
|
|
|
// ****************************************************************************
|
|
// CStatDBTableLeaf
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
void CStatDBTableLeaf::playerAdd(NLMISC::CEntityId playerId, sint32 val)
|
|
{
|
|
TPlayerValues::iterator it = _PlayerValues.find(playerId);
|
|
if (it != _PlayerValues.end())
|
|
{
|
|
sint32 & playerValue = (*it).second;
|
|
|
|
// avoid overflow
|
|
if (val > 0 && (playerValue + val) < playerValue)
|
|
{
|
|
playerValue = 0x7FFFFFFF;
|
|
return;
|
|
}
|
|
|
|
// add the value and remove the entry if the result is <= 0
|
|
playerValue += val;
|
|
if (playerValue <= 0)
|
|
{
|
|
_PlayerValues.erase(it);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (val > 0)
|
|
{
|
|
_PlayerValues[playerId] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBTableLeaf::playerSet(NLMISC::CEntityId playerId, sint32 val)
|
|
{
|
|
TPlayerValues::iterator it = _PlayerValues.find(playerId);
|
|
if (it != _PlayerValues.end())
|
|
{
|
|
sint32 & playerValue = (*it).second;
|
|
|
|
// set the value and remove the entry if the result is <= 0
|
|
if (val > 0)
|
|
{
|
|
playerValue = val;
|
|
}
|
|
else
|
|
{
|
|
_PlayerValues.erase(it);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (val > 0)
|
|
{
|
|
_PlayerValues[playerId] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBTableLeaf::guildAdd(EGSPD::TGuildId guildId, sint32 val)
|
|
{
|
|
TGuildValues::iterator it = _GuildValues.find(guildId);
|
|
if (it != _GuildValues.end())
|
|
{
|
|
sint32 & guildValue = (*it).second;
|
|
|
|
// avoid overflow
|
|
if (val > 0 && (guildValue + val) < guildValue)
|
|
{
|
|
guildValue = 0x7FFFFFFF;
|
|
return;
|
|
}
|
|
|
|
// add the value and remove the entry if the result is <= 0
|
|
guildValue += val;
|
|
if (guildValue <= 0)
|
|
{
|
|
_GuildValues.erase(it);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (val > 0)
|
|
{
|
|
_GuildValues[guildId] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBTableLeaf::guildSet(EGSPD::TGuildId guildId, sint32 val)
|
|
{
|
|
TGuildValues::iterator it = _GuildValues.find(guildId);
|
|
if (it != _GuildValues.end())
|
|
{
|
|
sint32 & guildValue = (*it).second;
|
|
|
|
// set the value and remove the entry if the result is <= 0
|
|
if (val > 0)
|
|
{
|
|
guildValue = val;
|
|
}
|
|
else
|
|
{
|
|
_GuildValues.erase(it);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (val > 0)
|
|
{
|
|
_GuildValues[guildId] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
bool CStatDBTableLeaf::playerGet(NLMISC::CEntityId playerId, sint32 & val) const
|
|
{
|
|
TPlayerValues::const_iterator it = _PlayerValues.find(playerId);
|
|
if (it == _PlayerValues.end())
|
|
return false;
|
|
|
|
val = (*it).second;
|
|
return true;
|
|
}
|
|
|
|
// ****************************************************************************
|
|
bool CStatDBTableLeaf::guildGet(EGSPD::TGuildId guildId, sint32 & val) const
|
|
{
|
|
TGuildValues::const_iterator it = _GuildValues.find(guildId);
|
|
if (it == _GuildValues.end())
|
|
return false;
|
|
|
|
val = (*it).second;
|
|
return true;
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBTableLeaf::removePlayer(NLMISC::CEntityId playerId)
|
|
{
|
|
TPlayerValues::iterator it = _PlayerValues.find(playerId);
|
|
if (it != _PlayerValues.end())
|
|
_PlayerValues.erase(it);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBTableLeaf::removeGuild(EGSPD::TGuildId guildId)
|
|
{
|
|
TGuildValues::iterator it = _GuildValues.find(guildId);
|
|
if (it != _GuildValues.end())
|
|
_GuildValues.erase(it);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
// CStatDBBranch
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
bool CStatDBBranch::setNode(const std::string & path, IStatDBNodePtr node)
|
|
{
|
|
BOMB_IF(path.empty(), "empty path!", return false);
|
|
BOMB_IF(node == NULL, "NULL node!", return false);
|
|
|
|
string token;
|
|
string rest;
|
|
splitPath(path, token, rest);
|
|
|
|
if (!isValidToken(token))
|
|
return false;
|
|
|
|
if (rest.empty())
|
|
{
|
|
_Children.add(make_pair(token, node));
|
|
return true;
|
|
}
|
|
|
|
IStatDBNodePtr nextNode;
|
|
TChildren::iterator it = _Children.find(token);
|
|
if (it == _Children.end())
|
|
{
|
|
nextNode = new CStatDBBranch;
|
|
_Children.add(make_pair(token, nextNode));
|
|
}
|
|
else
|
|
{
|
|
nextNode = (*it).second;
|
|
}
|
|
|
|
return nextNode->setNode(rest, node);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
IStatDBNodePtr CStatDBBranch::getNode(const std::string & path)
|
|
{
|
|
BOMB_IF(path.empty(), "empty path!", return NULL);
|
|
|
|
string token;
|
|
string rest;
|
|
splitPath(path, token, rest);
|
|
|
|
TChildren::iterator it = _Children.find(token);
|
|
if (it == _Children.end())
|
|
return NULL;
|
|
|
|
if (rest.empty())
|
|
return (*it).second;
|
|
|
|
return (*it).second->getNode(rest);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBBranch::getNodes(const std::string & pathPattern, std::vector<CMatchingNode> & matchingNodes,
|
|
const std::string & currentPath)
|
|
{
|
|
BOMB_IF(pathPattern.empty(), "empty path!", return);
|
|
|
|
// DO NOT clear the vector 'matchingNodes' here because this method is recursive
|
|
|
|
string token;
|
|
string rest;
|
|
splitPath(pathPattern, token, rest);
|
|
|
|
string pathPrefix = currentPath;
|
|
if (!pathPrefix.empty())
|
|
pathPrefix += ".";
|
|
|
|
// check wildcard
|
|
if (token == "*")
|
|
{
|
|
if (rest.empty())
|
|
{
|
|
for (TChildren::iterator it = _Children.begin(); it != _Children.end(); ++it)
|
|
{
|
|
CMatchingNode matchingNode;
|
|
matchingNode.Path = pathPrefix + (*it).first;
|
|
matchingNode.Node = (*it).second;
|
|
matchingNodes.push_back(matchingNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (TChildren::iterator it = _Children.begin(); it != _Children.end(); ++it)
|
|
{
|
|
(*it).second->getNodes(rest, matchingNodes, pathPrefix + (*it).first);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
TChildren::iterator it = _Children.find(token);
|
|
if (it == _Children.end())
|
|
return;
|
|
|
|
if (rest.empty())
|
|
{
|
|
CMatchingNode matchingNode;
|
|
matchingNode.Path = pathPrefix + (*it).first;
|
|
matchingNode.Node = (*it).second;
|
|
matchingNodes.push_back(matchingNode);
|
|
}
|
|
else
|
|
{
|
|
(*it).second->getNodes(rest, matchingNodes, pathPrefix + (*it).first);
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
IStatDBNodePtr CStatDBBranch::removeNode(const std::string & path)
|
|
{
|
|
BOMB_IF(path.empty(), "empty path!", return NULL);
|
|
|
|
string token;
|
|
string rest;
|
|
splitPath(path, token, rest);
|
|
|
|
TChildren::iterator it = _Children.find(token);
|
|
if (it == _Children.end())
|
|
return NULL;
|
|
|
|
if (rest.empty())
|
|
{
|
|
IStatDBNodePtr removedNode = (*it).second;
|
|
_Children.del(it);
|
|
return removedNode;
|
|
}
|
|
|
|
return (*it).second->removeNode(rest);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBBranch::acceptVisitor(CStatDBNodeVisitor & visitor, const std::string & currentPath)
|
|
{
|
|
string childPathPrefix;
|
|
if (!currentPath.empty())
|
|
childPathPrefix = currentPath + ".";
|
|
|
|
visitor.visitBranch(this, currentPath);
|
|
|
|
for (TChildren::iterator it = _Children.begin(); it != _Children.end(); ++it)
|
|
{
|
|
(*it).second->acceptVisitor(visitor, childPathPrefix + (*it).first);
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
bool CStatDBBranch::isValidToken(const std::string & token) const
|
|
{
|
|
if (token.empty())
|
|
return false;
|
|
|
|
for (string::const_iterator it = token.begin(); it != token.end(); ++it)
|
|
{
|
|
if (!isalnum(*it) && (*it) != '_')
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBBranch::splitPath(const std::string & path, std::string & token, std::string & rest) const
|
|
{
|
|
string::size_type i = path.find('.');
|
|
if (i == string::npos)
|
|
{
|
|
token = path;
|
|
rest.clear();
|
|
}
|
|
else
|
|
{
|
|
token = path.substr(0, i);
|
|
rest = path.substr(i+1);
|
|
}
|
|
}
|
|
|
|
// ****************************************************************************
|
|
// CStatDBEntitiesRemoval
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
void CStatDBEntitiesRemoval::addPlayerToRemove(NLMISC::CEntityId playerId)
|
|
{
|
|
for (uint i = 0; i < _PlayersToRemove.size(); i++)
|
|
{
|
|
if (_PlayersToRemove[i] == playerId)
|
|
return;
|
|
}
|
|
|
|
_PlayersToRemove.push_back(playerId);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBEntitiesRemoval::addGuildToRemove(EGSPD::TGuildId guildId)
|
|
{
|
|
for (uint i = 0; i < _GuildsToRemove.size(); i++)
|
|
{
|
|
if (_GuildsToRemove[i] == guildId)
|
|
return;
|
|
}
|
|
|
|
_GuildsToRemove.push_back(guildId);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBEntitiesRemoval::processRemoval(IStatDBNodePtr root)
|
|
{
|
|
nlassert(root != NULL);
|
|
|
|
if (_PlayersToRemove.empty() && _GuildsToRemove.empty())
|
|
return;
|
|
|
|
root->acceptVisitor(*this, "");
|
|
|
|
_PlayersToRemove.clear();
|
|
_GuildsToRemove.clear();
|
|
}
|
|
|
|
// ****************************************************************************
|
|
void CStatDBEntitiesRemoval::visitTableLeaf(CStatDBTableLeaf * tableLeaf, const std::string & path)
|
|
{
|
|
for (uint i = 0; i < _PlayersToRemove.size(); i++)
|
|
{
|
|
tableLeaf->removePlayer(_PlayersToRemove[i]);
|
|
}
|
|
|
|
for (uint i = 0; i < _GuildsToRemove.size(); i++)
|
|
{
|
|
tableLeaf->removeGuild(_GuildsToRemove[i]);
|
|
}
|
|
}
|
|
|