Files
src-1.2/engine/server/library/serverGame/src/shared/object/CellObject.cpp
T

792 lines
23 KiB
C++
Executable File

// ======================================================================
//
// CellObject.cpp
// Copyright 2001 Sony Online Entertainment
// All Rights Reserved.
//
// ======================================================================
#include "serverGame/FirstServerGame.h"
#include "serverGame/CellObject.h"
#include "serverGame/BuildingObject.h"
#include "serverGame/CellController.h"
#include "serverGame/Client.h"
#include "serverGame/ConfigServerGame.h"
#include "serverGame/ContainerInterface.h"
#include "serverGame/CreatureObject.h"
#include "serverGame/ServerObjectTemplate.h"
#include "serverGame/ServerTangibleObjectTemplate.h"
#include "serverGame/ServerWorld.h"
#include "serverScript/GameScriptObject.h"
#include "serverScript/ScriptFunctionTable.h"
#include "serverScript/ScriptParameters.h"
#include "sharedCollision/Floor.h"
#include "sharedCollision/FloorMesh.h"
#include "sharedFoundation/ConstCharCrcLowerString.h"
#include "sharedFoundation/DynamicVariableList.h"
#include "sharedLog/Log.h"
#include "sharedNetworkMessages/MessageQueueGenericValueType.h"
#include "sharedNetworkMessages/UpdateCellPermissionMessage.h"
#include "sharedObject/CellProperty.h"
#include "sharedObject/ContainedByProperty.h"
#include "sharedObject/NetworkIdManager.h"
#include "sharedObject/ObjectTemplate.h"
#include "sharedObject/ObjectTemplateList.h"
#include "sharedObject/PortalProperty.h"
#include "sharedPathfinding/PathGraph.h"
#include "sharedPathfinding/PathNode.h"
#include <algorithm>
const SharedObjectTemplate * CellObject::m_defaultSharedTemplate = nullptr;
// ======================================================================
CellObject::CellObject(const ServerObjectTemplate *newTemplate)
: ServerObject(newTemplate),
m_cellNumber(-1),
m_isPublic(true),
m_allowed(),
m_banned(),
m_cellLabel(),
m_labelLocationOffset()
{
setDebugName("PortalProperty cell object");
CellProperty *cell = new CellProperty(*this);
addProperty(*cell);
m_isPublic.setSourceObject(this);
m_allowed.setSourceObject(this);
m_banned.setSourceObject(this);
addMembersToPackages();
//@todo -- HACK! this is supposed to be set by the database,
// but that code isn't written. Assume all buildings and cells
// are in the client cache file. //-- it is now [8/4/2002 4:20:48 PM],
// this should be implemented in a few days. If you see this comment
// and the code below calls setIsCachedOnClient(true), get with Chris
// and Calan to determine the status of this code.
//setCacheVersion(1);
}
//-----------------------------------------------------------------------
CellObject::~CellObject()
{
}
//-----------------------------------------------------------------------
Controller * CellObject::createDefaultController()
{
Controller *controller = new CellController(this);
setController(controller);
return controller;
}
//-----------------------------------------------------------------------
/**
* Returns a shared template if none was given for this object.
*
* @return the shared template
*/
const SharedObjectTemplate * CellObject::getDefaultSharedTemplate(void) const
{
static const ConstCharCrcLowerString templateName("object/cell/base/shared_cell_default.iff");
if (m_defaultSharedTemplate == nullptr)
{
m_defaultSharedTemplate = safe_cast<const SharedObjectTemplate *>(
ObjectTemplateList::fetch(templateName));
WARNING_STRICT_FATAL(m_defaultSharedTemplate == nullptr, ("Cannot create "
"default shared object template %s", templateName.getString()));
if (m_defaultSharedTemplate != nullptr)
ExitChain::add (removeDefaultTemplate, "CellObject::removeDefaultTemplate");
}
return m_defaultSharedTemplate;
} // CellObject::getDefaultSharedTemplate
//-----------------------------------------------------------------------
/**
* Cleans up the default shared template.
*/
void CellObject::removeDefaultTemplate(void)
{
if (m_defaultSharedTemplate != nullptr)
{
m_defaultSharedTemplate->releaseReference();
m_defaultSharedTemplate = nullptr;
}
} // CellObject::removeDefaultTemplate
// ----------------------------------------------------------------------
void CellObject::sendObjectSpecificBaselinesToClient(Client const &client) const
{
ServerObject::sendObjectSpecificBaselinesToClient(client);
ServerObject const * const so = client.getCharacterObject();
if (so)
{
CreatureObject const * const creature = so->asCreatureObject();
if (creature)
{
UpdateCellPermissionMessage message(getNetworkId(), creature ? isAllowed(*creature) : false);
client.send(message, true);
}
}
}
// ----------------------------------------------------------------------
void CellObject::endBaselines()
{
ServerObject::endBaselines();
// fix the load_with values if they don't match
if ( isAuthoritative() && ( getLoadWith() != ContainerInterface::getLoadWithContainerId(*this)))
{
setLoadWith(ContainerInterface::getLoadWithContainerId(*this));
}
DEBUG_FATAL(m_cellNumber.get() < 1, ("Invalid cell number"));
if (m_cellNumber.get() < 1)
{
LOG("CellProblem", ("Invalid cell number for cell %s", getNetworkId().getValueString().c_str()));
}
Object *container = ContainerInterface::getContainedByObject(*this);
PortalProperty *portalProperty = nullptr;
if (container)
{
portalProperty = container->getPortalProperty();
}
else
{
ContainedByProperty* prop = ContainerInterface::getContainedByProperty(*this);
LOG("CellProblem",("Cell %s could not resolve contained_by of %s", getNetworkId().getValueString().c_str(), prop->getContainedByNetworkId().getValueString().c_str()));
WARNING(true, ("CellProblem Cell %s could not resolve contained_by of %s", getNetworkId().getValueString().c_str(), prop->getContainedByNetworkId().getValueString().c_str()));
}
if (!portalProperty)
{
LOG("CellProblem",("Cell %s is not inside a POB", getNetworkId().getValueString().c_str()));
WARNING_STRICT_FATAL(true,("Cell %s is not inside a POB.\n",getNetworkId().getValueString().c_str()));
return;
}
if (m_cellNumber.get() < portalProperty->getNumberOfCells())
{
portalProperty->cellLoaded(m_cellNumber.get(), *this, false);
}
else
{
// go through cell contents and remove them all. Give them to the PortalProperty to be fixed when it handles EndBaselines
CellProperty *cellProperty = getCellProperty();
if (cellProperty)
{
for (ContainerIterator contents=cellProperty->begin(); contents != cellProperty->end(); ++contents)
{
Object *object = (*contents).getObject();
portalProperty->queueObjectForFixup(*object);
Container::ContainerErrorCode tmp = Container::CEC_Success;
cellProperty->remove(contents, tmp);
}
}
permanentlyDestroy(DeleteReasons::BadContainerTransfer);
}
#ifdef _DEBUG
if (ConfigServerGame::getDebugFloorPathNodeCount())
{
CellProperty * cellProperty = getCellProperty();
if(cellProperty)
{
Floor const * floor = cellProperty->getFloor();
if(floor)
{
FloorMesh const * floorMesh = floor->getFloorMesh();
NOT_NULL(floorMesh);
PathGraph const * graph = safe_cast<PathGraph const *>(floorMesh->getPathGraph());
if(graph)
{
int floorParts = floorMesh->getPartCount();
int graphParts = graph->getPartCount();
if(floorParts != graphParts)
{
const char * floorName = floorMesh->getName();
WARNING(true,("ServerCollisionProperty::ServerCollisionProperty - floor %s doesn't have the same number of parts as its graph (floor %d, graph %d)",floorName,floorParts,graphParts));
}
}
}
}
}
#endif
}
//-----------------------------------------------------------------------
void CellObject::onLoadedFromDatabase()
{
ServerObject::onLoadedFromDatabase();
// We need to run through the set of banned and allowed players
// and make sure the data is "good".
// - At one time players were stored by name instead of by network ID
ObservedPermissionObjectSet* permissionSets[] = { &m_allowed, &m_banned };
for ( unsigned int i = 0; i < sizeof( permissionSets ) / sizeof( permissionSets[0] ); ++i )
{
const CellPermissions::PermissionList& permList = permissionSets[i]->get();
// Temporary list for processing
CellPermissions::PermissionList tempList;
// On the first pass over the permission list, remove the elements that need to be converted
CellPermissions::PermissionList::const_iterator j;
for ( j = permList.begin(); j != permList.end(); ++j )
{
const CellPermissions::PermissionObject& permObject = (*j);
// Check if the string from the database was valid
if ( !permObject.hasBeenConverted() )
{
// The string in the database needs to be converted
tempList.insert( permObject );
}
}
// Take the elements that need to be converted and re-add them to the list
for ( j = tempList.begin(); j != tempList.end(); ++j )
{
const CellPermissions::PermissionObject& permObject = (*j);
// Remove from the archive set so that it gets marked dirty
permissionSets[i]->erase( permObject );
// Add to the archive set so that it gets marked dirty
// NOTE: The permission object constructor should try to convert the string for us
permissionSets[i]->insert( CellPermissions::PermissionObject( permObject.m_permissionString ) );
}
tempList.clear();
// On the second pass over the permission list, validate all the names
for ( j = permList.begin(); j != permList.end(); ++j )
{
const CellPermissions::PermissionObject& permObject = (*j);
// Make sure we can still obtain a name from the permission object
// For example, characters may have been purged
const std::string name = permObject.getName();
if ( name.empty() )
{
// The entry is no longer valid
tempList.insert( permObject );
}
}
// Remove all the invalid entries
for ( j = tempList.begin(); j != tempList.end(); ++j )
{
const CellPermissions::PermissionObject& permObject = (*j);
// Remove from the archive set so that it gets marked dirty
permissionSets[i]->erase( permObject );
}
tempList.clear();
}
}
// ----------------------------------------------------------------------
void CellObject::addAllowed(std::string const &name)
{
if(isAuthoritative())
{
if (!CellPermissions::isOnList(m_allowed.get(), name))
{
CellPermissions::PermissionObject newPermission(name);
// We don't want to add any permissions that can't be converted to names
if ( !newPermission.getName().empty() )
{
m_allowed.insert(name);
}
}
removeBanned(name);
}
else
{
sendControllerMessageToAuthServer(CM_addAllowed, new MessageQueueGenericValueType<std::string>(name));
}
}
// ----------------------------------------------------------------------
void CellObject::removeAllowed(std::string const &name)
{
if(isAuthoritative())
{
m_allowed.erase(name);
}
else
{
sendControllerMessageToAuthServer(CM_removeAllowed, new MessageQueueGenericValueType<std::string>(name));
}
}
// ----------------------------------------------------------------------
void CellObject::removeAllAllowed()
{
if(isAuthoritative())
{
m_allowed.clear();
}
else
{
sendControllerMessageToAuthServer(CM_removeAllAllowed, 0);
}
}
// ----------------------------------------------------------------------
void CellObject::addBanned(std::string const &name)
{
if(isAuthoritative())
{
if (!CellPermissions::isOnList(m_banned.get(), name))
{
CellPermissions::PermissionObject newPermission(name);
// We don't want to add any permissions that can't be converted to names
if ( !newPermission.getName().empty() )
{
m_banned.insert(name);
}
}
removeAllowed(name);
}
else
{
sendControllerMessageToAuthServer(CM_addBanned, new MessageQueueGenericValueType<std::string>(name));
}
}
// ----------------------------------------------------------------------
void CellObject::removeBanned(std::string const &name)
{
if(isAuthoritative())
{
m_banned.erase(name);
}
else
{
sendControllerMessageToAuthServer(CM_removeBanned, new MessageQueueGenericValueType<std::string>(name));
}
}
// ----------------------------------------------------------------------
void CellObject::removeAllBanned()
{
if(isAuthoritative())
{
m_banned.clear();
}
else
{
sendControllerMessageToAuthServer(CM_removeAllBanned, 0);
}
}
// ----------------------------------------------------------------------
ServerObject const *CellObject::getOwner() const
{
Object const * const o = ContainerInterface::getContainedByObject(*this);
if (o)
return o->asServerObject();
return 0;
}
// ----------------------------------------------------------------------
ServerObject *CellObject::getOwner()
{
Object * const o = ContainerInterface::getContainedByObject(*this);
if (o)
return o->asServerObject();
return 0;
}
// ----------------------------------------------------------------------
BuildingObject const *CellObject::getOwnerBuilding() const
{
Object const * const o = ContainerInterface::getContainedByObject(*this);
if (o)
{
ServerObject const * const so = o->asServerObject();
if (so)
return so->asBuildingObject();
}
return 0;
}
// ----------------------------------------------------------------------
BuildingObject *CellObject::getOwnerBuilding()
{
Object * const o = ContainerInterface::getContainedByObject(*this);
if (o)
{
ServerObject * const so = o->asServerObject();
if (so)
return so->asBuildingObject();
}
return 0;
}
// ----------------------------------------------------------------------
ShipObject const *CellObject::getOwnerShip() const
{
Object const * const o = ContainerInterface::getContainedByObject(*this);
if (o)
{
ServerObject const * const so = o->asServerObject();
if (so)
return so->asShipObject();
}
return 0;
}
// ----------------------------------------------------------------------
ShipObject *CellObject::getOwnerShip()
{
Object * const o = ContainerInterface::getContainedByObject(*this);
if (o)
{
ServerObject * const so = o->asServerObject();
if (so)
return so->asShipObject();
}
return 0;
}
// ----------------------------------------------------------------------
bool CellObject::isAllowed(CreatureObject const &who) const
{
if (who.getMasterId() != NetworkId::cms_invalid)
{
ServerObject const *master = ServerWorld::findObjectByNetworkId(who.getMasterId());
if (master && master != &who && master->asCreatureObject())
return isAllowed(*master->asCreatureObject());
}
BuildingObject const *building = getOwnerBuilding();
if (building)
{
if (!building->playersWithAlignedEnemyFlagsAllowed() && (Pvp::hasAnyAlignedTimedEnemyFlag(who) || Pvp::hasAnyBountyDuelEnemyFlag(who)))
return false;
if (building->isOwner(who.getNetworkId(), who.getClient()))
return true;
if (!building->isAllowed(who))
return false;
}
if (getIsPublic())
return !CellPermissions::isOnList(m_banned.get(), who);
else
{
// allow non-player-controlled creatures in private buildings with no owner
if (!who.isPlayerControlled() && building && building->getOwnerId() == NetworkId::cms_invalid)
return true;
// allow vendor-creatures to be placed in the building
if (who.hasCondition(ServerTangibleObjectTemplate::C_vendor))
return true;
return CellPermissions::isOnList(m_allowed.get(), who);
}
}
// ----------------------------------------------------------------------
bool CellObject::getClosestPathNodePos( const ServerObject & object, Vector & outPos ) const
{
const PathNode * closestNode = nullptr;
float closestDistance = 0;
const Vector objectPos = object.getPosition_w();
const CellProperty * cell = ContainerInterface::getCell(*this);
if (cell != nullptr)
{
const Floor * floor = cell->getFloor();
if (floor != nullptr)
{
const FloorMesh * mesh = floor->getFloorMesh();
if (mesh != nullptr)
{
const PathGraph * path = safe_cast<const PathGraph *>(mesh->getPathGraph());
if (path != nullptr)
{
int nodeCount = path->getNodeCount();
for (int i = 0; i < nodeCount; ++i)
{
const PathNode * node = path->getNode(i);
if (node != nullptr)
{
float distance = objectPos.magnitudeBetweenSquared(
rotateTranslate_p2w(node->getPosition_p()));
if (closestNode == nullptr || distance < closestDistance)
{
closestNode = node;
closestDistance = distance;
}
}
}
}
}
}
}
if (closestNode != nullptr)
outPos = closestNode->getPosition_p();
return closestNode != nullptr;
}
// ----------------------------------------------------------------------
bool CellObject::isVisibleOnClient (const Client & /*client*/) const
{
return true;
}
// ----------------------------------------------------------------------
void CellObject::getAttributes (stdvector<std::pair<std::string, Unicode::String> >::fwd &data) const
{
ServerObject::getAttributes(data);
}
// ----------------------------------------------------------------------
bool CellObject::onContainerAboutToLoseItem(ServerObject * destination, ServerObject& item, ServerObject* transferer)
{
//Cells should first check to see if the building will allow the item to be transfered.
ServerObject *obj = getOwner();
if (obj)
{
ScriptParams params;
params.addParam(destination ? destination->getNetworkId() : NetworkId::cms_invalid);
params.addParam(transferer ? transferer->getNetworkId() : NetworkId::cms_invalid);
params.addParam(item.getNetworkId());
if(obj->getScriptObject()->trigAllScripts(Scripting::TRIG_ABOUT_TO_LOSE_ITEM, params) == SCRIPT_OVERRIDE)
{
return false;
}
}
return ServerObject::onContainerAboutToLoseItem(destination, item, transferer);
}
// ----------------------------------------------------------------------
int CellObject::onContainerAboutToGainItem(ServerObject& item, ServerObject* transferer)
{
// If it's a creature trying to move into the cell, see if it's allowed.
CreatureObject * const creatureItem = item.asCreatureObject();
if (creatureItem && !isAllowed(*creatureItem))
return Container::CEC_SilentError;
//Cells should first check to see if the building will allow the item to be transfered.
ServerObject *obj = getOwner();
if (obj)
{
ServerObject const * const source = safe_cast<ServerObject const *>(ContainerInterface::getContainedByObject(item));
NetworkId sourceId = source ? source->getNetworkId() : NetworkId::cms_invalid;
ScriptParams params;
params.addParam(sourceId);
params.addParam(transferer ? transferer->getNetworkId() : NetworkId::cms_invalid);
params.addParam(item.getNetworkId());
if (obj->getScriptObject()->trigAllScripts(Scripting::TRIG_ABOUT_TO_RECEIVE_ITEM, params) == SCRIPT_OVERRIDE)
return Container::CEC_BlockedByScript;
}
return ServerObject::onContainerAboutToGainItem(item, transferer);
}
// ----------------------------------------------------------------------
void CellObject::onContainerLostItem(ServerObject * destination, ServerObject& item, ServerObject* transferer)
{
//Cells should first check to see if the building will allow the item to be transfered.
ServerObject * const obj = getOwner();
if (obj)
{
ScriptParams params;
params.addParam(destination ? destination->getNetworkId() : NetworkId::cms_invalid);
params.addParam(transferer ? transferer->getNetworkId() : NetworkId::cms_invalid);
params.addParam(item.getNetworkId());
obj->getScriptObject()->trigAllScripts(Scripting::TRIG_LOST_ITEM, params);
BuildingObject * const b_obj = getOwnerBuilding();
if (b_obj && item.isPlayerControlled() && destination == nullptr)
{
b_obj->lostPlayer(item);
}
}
ServerObject::onContainerLostItem(destination, item, transferer);
}
// ----------------------------------------------------------------------
void CellObject::onContainerGainItem(ServerObject& item, ServerObject * source, ServerObject* transferer)
{
//Cells should first check to see if the building will allow the item to be transfered.
ServerObject * const obj = getOwner();
if (obj)
{
// The default owner of an object in a building is the building's owner, unless it is a creature, ship, or vendor
if ( !item.asCreatureObject()
&& !item.asShipObject()
&& !item.isVendor())
{
item.setOwnerId(obj->getOwnerId());
}
ScriptParams params;
params.addParam(source ? source->getNetworkId() : NetworkId::cms_invalid);
params.addParam(transferer ? transferer->getNetworkId() : NetworkId::cms_invalid);
params.addParam(item.getNetworkId());
obj->getScriptObject()->trigAllScripts(Scripting::TRIG_RECEIVED_ITEM, params);
BuildingObject * const b_obj = getOwnerBuilding();
if (b_obj && item.isPlayerControlled() && !source)
{
b_obj->gainedPlayer(item);
}
}
ServerObject::onContainerGainItem(item, source, transferer);
}
// ----------------------------------------------------------------------
NetworkId const &CellObject::getOwnerId() const
{
Object const * const o = ContainerInterface::getContainedByObject(*this);
if (o)
{
ServerObject const * const so = o->asServerObject();
if (so)
return so->getOwnerId();
}
return NetworkId::cms_invalid;
}
// ----------------------------------------------------------------------
bool CellObject::isOwner(NetworkId const &id, Client const *client) const
{
Object const * const o = ContainerInterface::getContainedByObject(*this);
if (o)
{
ServerObject const * const so = o->asServerObject();
if (so)
return so->isOwner(id, client);
}
return false;
}
// ----------------------------------------------------------------------
void CellObject::setOwnerId(NetworkId const &id)
{
if (isAuthoritative())
{
Container * const container = ContainerInterface::getContainer(*this);
if (container)
{
for (ContainerIterator i = container->begin(); i != container->end(); ++i)
{
ServerObject * const content = safe_cast<ServerObject *>((*i).getObject());
if (content && !content->asCreatureObject())
content->setOwnerId(id);
}
}
}
//if not authoritative, that's ok. This should only be called when the authoritative building has setOwnerId called on it.
}
// ----------------------------------------------------------------------
void CellObject::onAllContentsLoaded()
{
ServerObject::onAllContentsLoaded();
BuildingObject * owner = getOwnerBuilding();
if (owner)
owner->onCellContentsLoaded();
}
// ----------------------------------------------------------------------
CellObject * CellObject::asCellObject()
{
return this;
}
// ----------------------------------------------------------------------
CellObject const * CellObject::asCellObject() const
{
return this;
}
// ----------------------------------------------------------------------
CellObject * CellObject::getCellObject(NetworkId const & networkId)
{
return CellObject::asCellObject(NetworkIdManager::getObjectById(networkId));
}
// ----------------------------------------------------------------------
CellObject * CellObject::asCellObject(Object * object)
{
ServerObject * const serverObject = (object != nullptr) ? object->asServerObject() : nullptr;
CellObject * const cellObject = (serverObject != nullptr) ? serverObject->asCellObject() : nullptr;
return cellObject;
}
// ----------------------------------------------------------------------
CellObject const * CellObject::asCellObject(Object const * object)
{
ServerObject const * const serverObject = (object != nullptr) ? object->asServerObject() : nullptr;
CellObject const * const cellObject = (serverObject != nullptr) ? serverObject->asCellObject() : nullptr;
return cellObject;
}
// ======================================================================