mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-07-31 01:15:48 -04:00
702 lines
23 KiB
C++
Executable File
702 lines
23 KiB
C++
Executable File
// ======================================================================
|
|
//
|
|
// PlanetProxyObject.cpp
|
|
// copyright (c) 2001 Sony Online Entertainment
|
|
//
|
|
// ======================================================================
|
|
|
|
#include "FirstPlanetServer.h"
|
|
#include "PlanetProxyObject.h"
|
|
|
|
#include "ConfigPlanetServer.h"
|
|
#include "GameServerData.h"
|
|
#include "PlanetServer.h"
|
|
#include "QuadtreeNode.h"
|
|
#include "Scene.h"
|
|
#include "WatcherConnection.h"
|
|
#include "serverNetworkMessages/LoadObjectMessage.h"
|
|
#include "serverNetworkMessages/SetAuthoritativeMessage.h"
|
|
#include "serverNetworkMessages/UnloadProxyMessage.h"
|
|
#include "sharedFoundation/Clock.h"
|
|
#include "sharedFoundation/ExitChain.h"
|
|
#include "sharedFoundation/MemoryBlockManager.h"
|
|
#include "sharedFoundation/Tag.h"
|
|
#include "sharedLog/Log.h"
|
|
#include "sharedMathArchive/TransformArchive.h"
|
|
|
|
#include <algorithm>
|
|
|
|
// ======================================================================
|
|
|
|
MemoryBlockManager* PlanetProxyObject::memoryBlockManager;
|
|
|
|
// ======================================================================
|
|
|
|
PlanetProxyObject::PlanetProxyObject(const NetworkId &objectId) :
|
|
m_objectId(objectId),
|
|
m_containedBy(NetworkId::cms_invalid),
|
|
m_x(0),
|
|
m_y(0),
|
|
m_z(0),
|
|
m_authoritativeServer(0),
|
|
m_lastReportedServer(0),
|
|
m_interestRadius(0),
|
|
m_quadtreeNode(nullptr),
|
|
m_authTransferTimeMs(Clock::timeMs()),
|
|
m_contents(nullptr),
|
|
m_level(0),
|
|
m_hibernating(false),
|
|
m_templateCrc(0),
|
|
m_aiActivity(-1),
|
|
m_creationType(-1),
|
|
m_objectTypeTag(0)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
PlanetProxyObject::~PlanetProxyObject()
|
|
{
|
|
if (PlanetServer::getInstance().isWatcherPresent())
|
|
{
|
|
outputStatusToAll(true);
|
|
}
|
|
m_quadtreeNode = 0;
|
|
|
|
if (m_contents)
|
|
{
|
|
for (std::vector<NetworkId>::const_iterator i = m_contents->begin(); i != m_contents->end(); ++i)
|
|
WARNING(true, ("Object %s was deleted without removing contained object %s first", m_objectId.getValueString().c_str(), i->getValueString().c_str()));
|
|
delete m_contents;
|
|
m_contents = 0;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Update an object with new data sent from a game server.
|
|
* Checks to see what changed, then takes appropriate actions.
|
|
*/
|
|
void PlanetProxyObject::update(int x, int y, int z, NetworkId containedBy, uint32 authoritativeServer, int interestRadius, int objectTypeTag, int const level, bool const hibernating, uint32 const templateCrc, int const aiActivity, int const creationType)
|
|
{
|
|
WARNING_DEBUG_FATAL(m_objectId == NetworkId::cms_invalid, ("Object ID was 0."));
|
|
bool watcherUpdateNeeded = false;
|
|
|
|
m_level = level;
|
|
m_hibernating = hibernating;
|
|
m_templateCrc = templateCrc;
|
|
m_aiActivity = aiActivity;
|
|
m_creationType = creationType;
|
|
|
|
if ((m_authoritativeServer != 0) && (authoritativeServer != m_authoritativeServer) && (containedBy == NetworkId::cms_invalid))
|
|
{
|
|
static unsigned long authTransferSanityCheckTimeMs = ConfigPlanetServer::getAuthTransferSanityCheckTimeMs();
|
|
if (Clock::timeMs() - m_authTransferTimeMs > authTransferSanityCheckTimeMs)
|
|
{
|
|
DEBUG_WARNING(true, ("Resending auth transfer for %s to %lu due to receiving a stale position update.", m_objectId.getValueString().c_str(), authoritativeServer));
|
|
sendAuthorityChange(authoritativeServer, m_authoritativeServer, false);
|
|
return;
|
|
}
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("Ignoring update for %s from %lu because server was not authoritative.", m_objectId.getValueString().c_str(), authoritativeServer));
|
|
return;
|
|
}
|
|
|
|
if (interestRadius > ConfigPlanetServer::getMaxInterestRadius())
|
|
interestRadius = ConfigPlanetServer::getMaxInterestRadius();
|
|
|
|
if (interestRadius > 0 && PlanetServer::getInstance().isInTutorialMode())
|
|
interestRadius = 1;
|
|
|
|
if (containedBy != NetworkId::cms_invalid)
|
|
{
|
|
// get coordinates from container
|
|
PlanetProxyObject *container = Scene::getInstance().findObjectByID(containedBy);
|
|
if (container)
|
|
{
|
|
x = container->getX();
|
|
y = container->getY();
|
|
z = container->getZ();
|
|
}
|
|
else
|
|
{
|
|
LOG("PlanetUpdate", ("Game server said object %s was contained by object %s, but we could not find the container.", m_objectId.getValueString().c_str(), containedBy.getValueString().c_str()));
|
|
containedBy = NetworkId::cms_invalid; // so that we'll try again when the object moves again, in case we just got objects out-of-order from the game server
|
|
}
|
|
}
|
|
|
|
std::vector<uint32> oldProxyList;
|
|
|
|
Node *newNode = Scene::getInstance().findNodeByPosition(x, z);
|
|
NOT_NULL(newNode);
|
|
if (newNode != m_quadtreeNode || m_interestRadius != interestRadius || m_authoritativeServer != authoritativeServer || m_containedBy != containedBy)
|
|
{
|
|
watcherUpdateNeeded = true;
|
|
|
|
// For debugging, update the contents of the containers
|
|
if (ConfigPlanetServer::getEnableContentsChecking() && (m_containedBy != containedBy))
|
|
updateContentsTracking(containedBy);
|
|
|
|
bool const firstUpdate = (m_quadtreeNode == nullptr);
|
|
|
|
if (m_quadtreeNode != nullptr) // if this is the first update for this object, m_quadtreeNode will be nullptr because it hasn't been placed anywhere yet
|
|
{
|
|
removeServerStatistics();
|
|
|
|
// remember old list of proxies
|
|
getServers(oldProxyList);
|
|
|
|
// remove from the old location
|
|
m_quadtreeNode->removeObject(this);
|
|
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("unsubscribing nodes for object id=[%s]", m_objectId.getValueString().c_str()));
|
|
unsubscribeSurroundingNodes(false);
|
|
}
|
|
|
|
// update data
|
|
m_quadtreeNode = newNode;
|
|
m_x = x;
|
|
m_y = y;
|
|
m_z = z;
|
|
m_interestRadius = interestRadius;
|
|
m_objectTypeTag = objectTypeTag;
|
|
m_authoritativeServer = authoritativeServer;
|
|
m_containedBy = containedBy;
|
|
|
|
// make sure the destination is loaded
|
|
if (!newNode->isLoaded())
|
|
{
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("Loading node %s because object %s entered it.", newNode->getDebugNodeString().c_str(), m_objectId.getValueString().c_str()));
|
|
newNode->load();
|
|
}
|
|
|
|
// check if we're on the right server
|
|
if (!isAuthorityOk())
|
|
{
|
|
uint32 preferredServer = m_quadtreeNode->getPreferredServer();
|
|
if (m_containedBy == NetworkId::cms_invalid)
|
|
{
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("Changing authority for object %s from %lu to %lu because node %s is authoritative on that server.",
|
|
m_objectId.getValueString().c_str(), m_authoritativeServer, preferredServer, m_quadtreeNode->getDebugNodeString().c_str()));
|
|
changeAuthority(preferredServer, false, false); // changes m_authoritativeServer and sends message
|
|
|
|
// we received the object for the first time, and we had to
|
|
// transfer the object's authority, so we need to make sure
|
|
// to unload the object on the current authoritative server,
|
|
// if necessary
|
|
if (firstUpdate)
|
|
oldProxyList.push_back(authoritativeServer);
|
|
}
|
|
else
|
|
{
|
|
WARNING(true, ("Programmer bug: Object %s is in container %s, which is node %s. The object should be authoritative on server %i but it is authoritative on server %i.",
|
|
m_objectId.getValueString().c_str(), m_containedBy.getValueString().c_str(), m_quadtreeNode->getDebugNodeString().c_str(), m_authoritativeServer, preferredServer));
|
|
}
|
|
}
|
|
|
|
// place in the new location
|
|
newNode->addObject(this);
|
|
|
|
if (interestRadius > 0)
|
|
{
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("subscribing nodes for object id=[%s]", m_objectId.getValueString().c_str()));
|
|
subscribeSurroundingNodes();
|
|
}
|
|
|
|
// check against new interested servers, and send proxy/unproxy as needed
|
|
if (m_containedBy == NetworkId::cms_invalid) // we don't manage proxies for containers
|
|
{
|
|
std::vector<uint32> newProxyList;
|
|
getServers(newProxyList);
|
|
changeProxies(oldProxyList, newProxyList);
|
|
}
|
|
|
|
addServerStatistics();
|
|
}
|
|
else
|
|
{
|
|
if (m_interestRadius > 0 || (abs(m_x - x) > 25) || (abs(m_z - z) > 25))
|
|
watcherUpdateNeeded = true;
|
|
}
|
|
|
|
if (PlanetServer::getInstance().isWatcherPresent() && watcherUpdateNeeded)
|
|
{
|
|
m_x = x;
|
|
m_y = y;
|
|
m_z = z;
|
|
m_objectTypeTag = objectTypeTag;
|
|
|
|
outputStatusToAll(false);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::unsubscribeSurroundingNodes(bool clearZeroSubscriptions)
|
|
{
|
|
if (m_interestRadius <= 0)
|
|
return;
|
|
|
|
Scene::NodeListType nodelist;
|
|
Scene::getInstance().findIntersection(nodelist, m_x, m_z, m_interestRadius);
|
|
|
|
for (Scene::NodeListType::iterator i = nodelist.begin(); i != nodelist.end(); ++i)
|
|
{
|
|
(*i)->unsubscribeServer(m_authoritativeServer, 1, clearZeroSubscriptions);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::subscribeSurroundingNodes()
|
|
{
|
|
if (m_interestRadius <= 0)
|
|
return;
|
|
|
|
NOT_NULL(m_quadtreeNode);
|
|
Scene::NodeListType nodelist;
|
|
Scene::getInstance().findIntersection(nodelist, m_x, m_z, m_interestRadius);
|
|
|
|
for (Scene::NodeListType::iterator i = nodelist.begin(); i != nodelist.end(); ++i)
|
|
{
|
|
(*i)->subscribeServer(m_authoritativeServer, 1);
|
|
uint32 serverId = (*i)->getPreferredServer();
|
|
if (serverId != m_authoritativeServer)
|
|
m_quadtreeNode->subscribeServer(serverId, 0); // make sure if we can see them, they can see us
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Restore the object's authority to the specified game server.
|
|
*/
|
|
void PlanetProxyObject::restoreAuthority(uint32 gameServer)
|
|
{
|
|
m_authoritativeServer = gameServer;
|
|
m_lastReportedServer = gameServer;
|
|
|
|
// Send queued messages
|
|
PlanetServer::getInstance().sendQueuedMessagesForObject(*this);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Make the object authortiative on a different game server. Sends a
|
|
* message to the game servers to change authority and updates
|
|
* m_authoritativeServer.
|
|
*/
|
|
void PlanetProxyObject::changeAuthority(uint32 newGameServer, bool handlingCrash, bool forcedByGame)
|
|
{
|
|
if (handlingCrash)
|
|
sendAuthorityChange(newGameServer, newGameServer, true);
|
|
else if (!forcedByGame)
|
|
sendAuthorityChange(m_authoritativeServer, newGameServer, false);
|
|
m_authoritativeServer = newGameServer;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Make the object authoritative on a different game server and adjust the
|
|
* subscription counts.
|
|
*/
|
|
void PlanetProxyObject::changeAuthorityAndSubscriptions(uint32 newGameServer)
|
|
{
|
|
unsubscribeSurroundingNodes(false);
|
|
changeAuthority(newGameServer, false, false);
|
|
subscribeSurroundingNodes();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Called when an object should be unloaded from the world. The object
|
|
* should be removed from the active game servers, but not deleted from
|
|
* the database.
|
|
*
|
|
* This function also removes the PlanetProxyObject from all data structures.
|
|
* On exit, it is legal to delete the PlanetProxyObject.
|
|
*
|
|
* @param sendToGameServer Set to true if ForceUnloadObjectMessage should be
|
|
* sent to the game server. In some cases there is no need to tell the game
|
|
* server to unload the object (such as when handling a crash, for example).
|
|
*/
|
|
void PlanetProxyObject::unload(bool tellGameServer)
|
|
{
|
|
if (m_quadtreeNode)
|
|
m_quadtreeNode->removeObject(this);
|
|
|
|
Scene::getInstance().removeObjectFromMap(m_objectId, tellGameServer);
|
|
|
|
if (tellGameServer)
|
|
{
|
|
PlanetServer::getInstance().unloadObject(m_objectId, m_authoritativeServer);
|
|
}
|
|
|
|
removeServerStatistics();
|
|
|
|
if (ConfigPlanetServer::getEnableContentsChecking() && m_containedBy != NetworkId::cms_invalid)
|
|
{
|
|
PlanetProxyObject *container = Scene::getInstance().findObjectByID(m_containedBy);
|
|
if (container)
|
|
container->removeContainedObject(m_objectId);
|
|
else
|
|
WARNING(true, ("Unloading object %s in container %s, but the container could not be found.", m_objectId.getValueString().c_str(), m_containedBy.getValueString().c_str()));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Output the object's position to a watcher.
|
|
* @param conn the watcher
|
|
* @param deleteObject if true, tell the watcher to delete this object.
|
|
*/
|
|
void PlanetProxyObject::outputStatus(WatcherConnection &conn, bool deleteObject) const
|
|
{
|
|
conn.addObjectUpdate(m_objectId, m_x, m_z, m_authoritativeServer, m_interestRadius, deleteObject, m_objectTypeTag, m_level, m_hibernating, m_templateCrc, m_aiActivity, m_creationType);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::outputStatusToAll(bool deleteObject) const
|
|
{
|
|
const PlanetServer::WatcherList &connections = PlanetServer::getInstance().getWatchers();
|
|
for (PlanetServer::WatcherList::const_iterator i = connections.begin(); i != connections.end(); ++i)
|
|
{
|
|
outputStatus(**i, deleteObject);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Update the GameServerData with statistics about this object.
|
|
*/
|
|
void PlanetProxyObject::addServerStatistics() const
|
|
{
|
|
GameServerData *data = PlanetServer::getInstance().getGameServerData(m_authoritativeServer);
|
|
if (data)
|
|
{
|
|
data->adjustObjectCount(1);
|
|
if (m_interestRadius != 0)
|
|
{
|
|
data->adjustInterestObjectCount(1);
|
|
if (isCreature())
|
|
data->adjustInterestCreatureObjectCount(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Remove statistics about this object from the GameServerData.
|
|
* Should exactly undo addServerStatistics().
|
|
*/
|
|
void PlanetProxyObject::removeServerStatistics() const
|
|
{
|
|
GameServerData *data = PlanetServer::getInstance().getGameServerData(m_authoritativeServer);
|
|
if (data)
|
|
{
|
|
data->adjustObjectCount(-1);
|
|
if (m_interestRadius != 0)
|
|
{
|
|
data->adjustInterestObjectCount(-1);
|
|
if (isCreature())
|
|
data->adjustInterestCreatureObjectCount(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Set up memory pool
|
|
*/
|
|
void PlanetProxyObject::install()
|
|
{
|
|
memoryBlockManager = new MemoryBlockManager("PlanetProxyObject::memoryBlockManager", true, sizeof(PlanetProxyObject), 0, 0, 0);
|
|
ExitChain::add(&remove, "PlanetProxyObject::remove");
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::remove()
|
|
{
|
|
WARNING_DEBUG_FATAL(!memoryBlockManager, ("PlanetProxyObject is not installed"));
|
|
delete memoryBlockManager;
|
|
memoryBlockManager = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void * PlanetProxyObject::operator new(size_t size)
|
|
{
|
|
UNREF(size);
|
|
|
|
WARNING_DEBUG_FATAL(!memoryBlockManager, ("PlanetProxyObject is not installed"));
|
|
|
|
// do not try to alloc a descendant class with this allocator
|
|
WARNING_DEBUG_FATAL(size != sizeof(PlanetProxyObject), ("bad size"));
|
|
|
|
return memoryBlockManager->allocate();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::operator delete(void* pointer)
|
|
{
|
|
WARNING_DEBUG_FATAL(!memoryBlockManager, ("PlanetProxyObject is not installed"));
|
|
memoryBlockManager->free(pointer);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::sendAuthorityChange(uint32 currentAuthServer, uint32 newAuthServer, bool handlingCrash)
|
|
{
|
|
DEBUG_WARNING(!isAuthorityClean(), ("Object %s attempted to change authority before previous authority change was complete.", getObjectId().getValueString().c_str()));
|
|
|
|
SetAuthoritativeMessage const auth(getObjectId(), newAuthServer, false, handlingCrash, NetworkId::cms_invalid, Transform::identity, false);
|
|
PlanetServer::getInstance().sendToGameServer(currentAuthServer, auth);
|
|
m_authTransferTimeMs = Clock::timeMs();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::sendAddProxy(uint32 proxyServer)
|
|
{
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("Gameserver %lu needs a proxy of object %s", proxyServer, m_objectId.getValueString().c_str()));
|
|
if (isAuthorityClean())
|
|
{
|
|
LoadObjectMessage const loadMsg(getObjectId(), proxyServer);
|
|
PlanetServer::getInstance().sendToGameServer(m_authoritativeServer, loadMsg);
|
|
}
|
|
else
|
|
{
|
|
LoadObjectMessage * const loadMsg = new LoadObjectMessage(getObjectId(), proxyServer);
|
|
PlanetServer::getInstance().queueMessageForObject(getObjectId(), loadMsg);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::sendRemoveProxy(uint32 proxyServer)
|
|
{
|
|
if (ConfigPlanetServer::getLogObjectLoading())
|
|
LOG("ObjectLoading", ("Gameserver %lu no longer needs a proxy of object %s", proxyServer, m_objectId.getValueString().c_str()));
|
|
if (isAuthorityClean())
|
|
{
|
|
UnloadProxyMessage msg(getObjectId(), proxyServer);
|
|
PlanetServer::getInstance().sendToGameServer(m_authoritativeServer, msg);
|
|
}
|
|
else
|
|
{
|
|
UnloadProxyMessage *msg = new UnloadProxyMessage(getObjectId(), proxyServer);
|
|
PlanetServer::getInstance().queueMessageForObject(getObjectId(), msg);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Called when we receieve a message about the object from a game server.
|
|
* Used to keep track of which server is sending us updates, and to send
|
|
* any messages that were waiting for an authority change.
|
|
*/
|
|
void PlanetProxyObject::onReceivedMessageFromServer(uint32 server)
|
|
{
|
|
if (m_lastReportedServer != m_authoritativeServer && server == m_authoritativeServer)
|
|
// we've received a message from the new authoritative server. Send queued messages:
|
|
PlanetServer::getInstance().sendQueuedMessagesForObject(*this);
|
|
|
|
m_lastReportedServer = server;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Get the list of servers that should have proxies of this object.
|
|
*/
|
|
void PlanetProxyObject::getServers(std::vector<uint32> &serverList) const
|
|
{
|
|
Node *effectiveNode;
|
|
if (m_containedBy == NetworkId::cms_invalid)
|
|
{
|
|
effectiveNode = m_quadtreeNode;
|
|
}
|
|
else
|
|
{
|
|
PlanetProxyObject *container = Scene::getInstance().findObjectByID(m_containedBy);
|
|
WARNING_STRICT_FATAL(!container, ("Object %s is in container %s, which couldn't be found", m_objectId.getValueString().c_str(), m_containedBy.getValueString().c_str()));
|
|
if (container)
|
|
effectiveNode = container->getNode();
|
|
else
|
|
effectiveNode = m_quadtreeNode;
|
|
}
|
|
|
|
if (effectiveNode)
|
|
effectiveNode->getServers(serverList);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Given a list of servers that have a proxy and a list of servers
|
|
* that should have a proxy, send messages to update the servers.
|
|
* Note: O(n^2), assumes both lists are small, otherwise it would be better
|
|
* to sort them first.
|
|
*/
|
|
void PlanetProxyObject::changeProxies(const std::vector<uint32> &oldServers, const std::vector<uint32> &newServers)
|
|
{
|
|
{
|
|
for (std::vector<uint32>::const_iterator i = oldServers.begin(); i != oldServers.end(); ++i)
|
|
{
|
|
if (*i != m_authoritativeServer)
|
|
{
|
|
bool removeNeeded = true;
|
|
for (std::vector<uint32>::const_iterator j = newServers.begin(); j != newServers.end(); ++j)
|
|
{
|
|
if (*i == *j)
|
|
{
|
|
removeNeeded = false;
|
|
break;
|
|
}
|
|
}
|
|
if (removeNeeded)
|
|
sendRemoveProxy(*i);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
for (std::vector<uint32>::const_iterator i = newServers.begin(); i != newServers.end(); ++i)
|
|
{
|
|
if (*i != m_authoritativeServer)
|
|
{
|
|
bool addNeeded = true;
|
|
for (std::vector<uint32>::const_iterator j = oldServers.begin(); j != oldServers.end(); ++j)
|
|
{
|
|
if (*i == *j)
|
|
{
|
|
addNeeded = false;
|
|
break;
|
|
}
|
|
}
|
|
if (addNeeded)
|
|
sendAddProxy(*i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Check whether we are on a server that's acceptable for where we are.
|
|
*/
|
|
bool PlanetProxyObject::isAuthorityOk() const
|
|
{
|
|
if (m_authoritativeServer == m_quadtreeNode->getPreferredServer())
|
|
return true;
|
|
if (m_quadtreeNode->isBorderNode() && m_quadtreeNode->isServerSubscribed(m_authoritativeServer))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Check for whether a desired server would be OK for where we are.
|
|
* The server is OK if it is the preferred server. If the object is
|
|
* a creature, the server is also OK if the node is a border node and
|
|
* the server has a subscription to the node. Non-creatures must always
|
|
* be on the preferred server.
|
|
*/
|
|
bool PlanetProxyObject::wouldAuthorityBeOk(uint32 possibleServer) const
|
|
{
|
|
if (possibleServer == m_quadtreeNode->getPreferredServer())
|
|
return true;
|
|
if (isCreature() && m_quadtreeNode->isBorderNode() && m_quadtreeNode->isServerSubscribed(possibleServer))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::addContainedObject(const NetworkId &theObject)
|
|
{
|
|
if (!m_contents)
|
|
{
|
|
m_contents = new std::vector<NetworkId>;
|
|
}
|
|
for (std::vector<NetworkId>::const_iterator i = m_contents->begin(); i != m_contents->end(); ++i)
|
|
{
|
|
if (*i == theObject)
|
|
WARNING(true, ("Object %s was placed in container %s twice.", theObject.getValueString().c_str(), m_objectId.getValueString().c_str()));
|
|
}
|
|
m_contents->push_back(theObject);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void PlanetProxyObject::removeContainedObject(const NetworkId &theObject)
|
|
{
|
|
if (!m_contents)
|
|
{
|
|
WARNING(true, ("Attempted to remove object %s from object %s, but that object has no contents.", theObject.getValueString().c_str(), m_objectId.getValueString().c_str()));
|
|
return;
|
|
}
|
|
std::vector<NetworkId>::iterator newEnd = std::remove(m_contents->begin(), m_contents->end(), theObject);
|
|
if (newEnd == m_contents->end())
|
|
{
|
|
WARNING(true, ("Attempted to remove object %s from object %s, but it was not in the container.", theObject.getValueString().c_str(), m_objectId.getValueString().c_str()));
|
|
return;
|
|
}
|
|
m_contents->erase(newEnd, m_contents->end());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Helper function to update the contents tracking lists, in order to
|
|
* debug various authority problems
|
|
*/
|
|
void PlanetProxyObject::updateContentsTracking(const NetworkId &newContainedBy)
|
|
{
|
|
if (m_containedBy != NetworkId::cms_invalid)
|
|
{
|
|
PlanetProxyObject *container = Scene::getInstance().findObjectByID(m_containedBy);
|
|
if (container)
|
|
container->removeContainedObject(m_objectId);
|
|
else
|
|
WARNING(true, ("Removing object %s from container %s, but the container could not be found.", m_objectId.getValueString().c_str(), m_containedBy.getValueString().c_str()));
|
|
}
|
|
|
|
if (newContainedBy != NetworkId::cms_invalid)
|
|
{
|
|
PlanetProxyObject *container = Scene::getInstance().findObjectByID(newContainedBy);
|
|
if (container)
|
|
container->addContainedObject(m_objectId);
|
|
else
|
|
WARNING(true, ("Adding object %s to container %s, but the container could not be found.", m_objectId.getValueString().c_str(), newContainedBy.getValueString().c_str()));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool PlanetProxyObject::isCreature() const
|
|
{
|
|
return m_objectTypeTag == TAG(C, R, E, O);
|
|
}
|
|
|
|
// ======================================================================
|