// ====================================================================== // // BuildingObject.cpp // Copyright 2002 Sony Online Entertainment // // ====================================================================== #include "serverGame/FirstServerGame.h" #include "serverGame/BuildingObject.h" #include "serverGame/BuildingController.h" #include "serverGame/CellObject.h" #include "serverGame/CityInterface.h" #include "serverGame/Client.h" #include "serverGame/ConfigServerGame.h" #include "serverGame/ContainerInterface.h" #include "serverGame/CreatureObject.h" #include "serverGame/GameServer.h" #include "serverGame/MessageToQueue.h" #include "serverGame/NewbieTutorial.h" #include "serverGame/ObjectTracker.h" #include "serverGame/PlayerObject.h" #include "serverGame/PortalTriggerVolume.h" #include "serverGame/PositionUpdateTracker.h" #include "serverGame/Pvp.h" #include "serverGame/ServerBuildingObjectTemplate.h" #include "serverGame/ServerCreatureObjectTemplate.h" #include "serverGame/ServerTangibleObjectTemplate.h" #include "serverGame/ServerWorld.h" #include "serverNetworkMessages/MessageToPayload.h" #include "serverPathfinding/ServerPathfindingNotification.h" #include "serverScript/GameScriptObject.h" #include "serverScript/ScriptParameters.h" #include "serverUtility/ServerClock.h" #include "sharedFoundation/ConstCharCrcLowerString.h" #include "sharedFoundation/DynamicVariableList.h" #include "sharedGame/SharedBuildingObjectTemplate.h" #include "sharedGame/SharedObjectTemplate.h" #include "sharedLog/Log.h" #include "sharedNetworkMessages/MessageQueueGenericValueType.h" #include "sharedObject/AlterResult.h" #include "sharedObject/CellProperty.h" #include "sharedObject/Container.h" #include "sharedObject/LayerProperty.h" #include "sharedObject/ObjectTemplateList.h" #include "sharedObject/Portal.h" #include "sharedObject/PortalProperty.h" #include "sharedObject/PortalPropertyTemplate.h" #include "sharedObject/VolumeContainer.h" #include "sharedTerrain/TerrainModificationHelper.h" #include "sharedUtility/DataTable.h" #include "sharedUtility/DataTableManager.h" #include #include // ====================================================================== const std::string CLONE_RESPAWN_TABLE("datatables/structure/municipal/cloning_facility_respawn.iff"); namespace BuildingObjectNamespace { BuildingObject::AllBuildingsSet g_buildingList; bool hasPlayer(const BuildingObject& building) { const Container * container = ContainerInterface::getContainer(building); if (container) { for (ContainerConstIterator i = container->begin(); i != container->end(); ++i) { const ServerObject * cell = safe_cast((*i).getObject()); DEBUG_FATAL(!cell, ("Null cell in building list")); if (cell) { const Container * cellContainer = ContainerInterface::getContainer(*cell); if (cellContainer) { for (ContainerConstIterator j = cellContainer->begin(); j != cellContainer->end(); ++j) { const ServerObject * obj = safe_cast((*j).getObject()); if (obj && obj->isPlayerControlled()) { return true; } } } } } } return false; } void unloadCellsContents(BuildingObject& building) { Container *container = ContainerInterface::getContainer(building); if (container) { for (ContainerIterator i = container->begin(); i != container->end(); ++i) { ServerObject * cell = safe_cast((*i).getObject()); DEBUG_FATAL(!cell, ("Null cell in building list")); if (cell) { cell->unloadPersistedContents(); } } } } void updateLoadWithToCell(const ServerObject& cell, ServerObject& obj) { if (!obj.isPlayerControlled()) { obj.setLoadWith(cell.getNetworkId()); PositionUpdateTracker::positionChanged(obj); if (obj.getLoadContents()) { Container * objContainer = ContainerInterface::getContainer(obj); if (objContainer) { for (ContainerIterator i = objContainer->begin(); i != objContainer->end(); ++i) { ServerObject* childObject = safe_cast((*i).getObject()); if (childObject) { updateLoadWithToCell(cell, *childObject); } } } } } } } using namespace BuildingObjectNamespace; // ====================================================================== const SharedObjectTemplate * BuildingObject::m_defaultSharedTemplate = nullptr; // ====================================================================== BuildingObject::BuildingObject(const ServerBuildingObjectTemplate* newTemplate) : TangibleObject(newTemplate), m_maintenanceCost(newTemplate->getMaintenanceCost()), m_timeLastChecked(0.f), m_cityId(0), m_isPublic(newTemplate->getIsPublic()), m_allowed(), m_banned(), m_contentsLoaded(false), m_checkCreatePortalVolume(true), m_unloadTime(0) { 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 setCacheVersion(1), get with Chris // and Calan to determine the status of this code. //setCacheVersion(1); // All buildings get server pathfinding notifications addNotification( ServerPathfindingNotification::getInstance() ); //-- create test layer (this should load from the template's layer filename) const SharedBuildingObjectTemplate* const sharedObjectTemplate = safe_cast (getSharedTemplate ()); if (sharedObjectTemplate->getTerrainModificationFileName ().length () != 0) { TerrainGenerator::Layer * layer = TerrainModificationHelper::importLayer (sharedObjectTemplate->getTerrainModificationFileName ().c_str ()); WARNING (!layer, ("Layer %s not found", sharedObjectTemplate->getTerrainModificationFileName ().c_str ())); if (layer) { setLayer(layer); } } IGNORE_RETURN(g_buildingList.insert(this)); ObjectTracker::addBuilding(); } // ---------------------------------------------------------------------- BuildingObject::~BuildingObject() { if (isInWorld()) removeFromWorld(); IGNORE_RETURN(g_buildingList.erase(this)); ObjectTracker::removeBuilding(); } // ---------------------------------------------------------------------- float BuildingObject::alter(float time) { if (m_checkCreatePortalVolume) { bool playerPlaced = isPlayerPlaced(); // Trigger volumes are centered on the object, which may be unrelated to the extents of the object, but // we need the portal trigger volume to encompass the extents of the object. if (!playerPlaced) { Sphere const &extent = getSphereExtent(); float radius = extent.getRadius() + getTransform_o2w().getPosition_p().magnitudeBetween(extent.getCenter()) + 5.0f; if (radius > 0 && !isSpaceDungeon() && ServerWorld::getSceneId() != NewbieTutorial::getSceneId()) { TriggerVolume* t = new PortalTriggerVolume(*this, radius); addTriggerVolume(t); } } m_checkCreatePortalVolume = false; } const unsigned long gameTime = ServerClock::getInstance().getGameTimeSeconds(); if ((m_unloadTime > 0) && (gameTime >= m_unloadTime)) { if ( hasPlayer (*this) ) { m_unloadTime = gameTime + static_cast(ConfigServerGame::getUnloadHouseContentsTime()); } else { m_unloadTime = 0; // safety check to make absolutely sure we don't unload a building which shouldn't be unloaded if (isPlayerPlaced() && ConfigServerGame::getDemandLoadHouseContents() && isAuthoritative()) { unloadCellsContents( *this ); m_contentsLoaded = false; } } } float result = TangibleObject::alter(time); AlterResult::incorporateAlterResult(result, ((m_unloadTime > gameTime) ? static_cast(m_unloadTime - gameTime) : result)); return result; } // ---------------------------------------------------------------------- /** * Initializes a newly created object. */ void BuildingObject::initializeFirstTimeObject() { TangibleObject::initializeFirstTimeObject(); } //------------------------------------------------------------------------------------------ void BuildingObject::endBaselines() { TangibleObject::endBaselines(); } //----------------------------------------------------------------------- void BuildingObject::onLoadedFromDatabase() { TangibleObject::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(); } // check to see if building is a declared building inside a city limit if (ConfigServerGame::getEnableCityCitizenshipFixup()) { if (!isInWorldCell()) return; NetworkId residenceOf; if (!getObjVars().getItem("player_structure.residence.building", residenceOf)) return; if (residenceOf != getOwnerId()) return; Vector const posWorld = getPosition_w(); int const cityId = CityInterface::getCityAtLocation(ServerWorld::getSceneId(), static_cast(posWorld.x), static_cast(posWorld.z), 0); if (cityId <= 0) return; GameServer::reportClusterStartupResidenceStructure(cityId, getNetworkId(), residenceOf); } } // ---------------------------------------------------------------------- /** * Returns a shared template if none was given for this object. * * @return the shared template */ const SharedObjectTemplate * BuildingObject::getDefaultSharedTemplate(void) const { static const ConstCharCrcLowerString templateName("object/building/base/shared_building_default.iff"); if (m_defaultSharedTemplate == nullptr) { m_defaultSharedTemplate = safe_cast( 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, "BuildingObject::removeDefaultTemplate"); } return m_defaultSharedTemplate; } // BuildingObject::getDefaultSharedTemplate //----------------------------------------------------------------------- /** * Cleans up the default shared template. */ void BuildingObject::removeDefaultTemplate(void) { if (m_defaultSharedTemplate != nullptr) { m_defaultSharedTemplate->releaseReference(); m_defaultSharedTemplate = nullptr; } } // BuildingObject::removeDefaultTemplate // ---------------------------------------------------------------------- /** * Creates a default controller for this object. * * @return the object's controller */ Controller* BuildingObject::createDefaultController(void) { Controller* _controller = new BuildingController(this); setController(_controller); return _controller; } // BuildingObject::createDefaultController // ---------------------------------------------------------------------- void BuildingObject::expelObject(ServerObject &who) { PortalProperty *portalProperty = getPortalProperty(); if (portalProperty) { ServerController * controller = dynamic_cast(who.getController()); if (controller) { CellProperty *parentCell = getParentCell(); ServerObject *destinationCellObject = nullptr; if (!parentCell->isWorldCell()) destinationCellObject = safe_cast(&parentCell->getOwner()); Transform destinationTransform(Transform::IF_none); destinationTransform.multiply(getTransform_o2w(), portalProperty->getEjectionLocationTransform()); controller->teleport(destinationTransform, destinationCellObject); } } } // ---------------------------------------------------------------------- void BuildingObject::onPermanentlyDestroyed() { ServerPathfindingNotification::destroyBuilding(this); GameServer::reportBuildingObjectDestroyed(getNetworkId()); //Check for vendors since they must be explicitly killed Container *container = ContainerInterface::getContainer(*this); if (container) { for (ContainerIterator i = container->begin(); i != container->end(); ++i) { ServerObject *cell = safe_cast((*i).getObject()); if (cell) { Container *cellContainer = ContainerInterface::getContainer(*cell); if (cellContainer) { for (ContainerIterator j = cellContainer->begin(); j != cellContainer->end(); ++j) { TangibleObject *child = dynamic_cast((*j).getObject()); if (child && isPlayerPlaced() && child->hasCondition(static_cast(ServerTangibleObjectTemplate::C_vendor))) { IGNORE_RETURN(child->permanentlyDestroy(DeleteReasons::ContainerDeleted)); } else if (child && child->getGameObjectType() == static_cast(SharedObjectTemplate::GOT_corpse)) { expelObject(*child); } } //for contents of cells } } } //for cells } } // ---------------------------------------------------------------------- void BuildingObject::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(name)); } } // ---------------------------------------------------------------------- void BuildingObject::removeAllowed(std::string const &name) { if(isAuthoritative()) { m_allowed.erase(name); } else { sendControllerMessageToAuthServer(CM_removeAllowed, new MessageQueueGenericValueType(name)); } } // ---------------------------------------------------------------------- void BuildingObject::removeAllAllowed() { if(isAuthoritative()) { m_allowed.clear(); } else { sendControllerMessageToAuthServer(CM_removeAllAllowed, 0); } } // ---------------------------------------------------------------------- void BuildingObject::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(name)); } } // ---------------------------------------------------------------------- void BuildingObject::removeBanned(std::string const &name) { if(isAuthoritative()) { m_banned.erase(name); } else { sendControllerMessageToAuthServer(CM_removeBanned, new MessageQueueGenericValueType(name)); } } // ---------------------------------------------------------------------- void BuildingObject::removeAllBanned() { if(isAuthoritative()) { m_banned.clear(); } else { sendControllerMessageToAuthServer(CM_removeAllBanned, 0); } } // ---------------------------------------------------------------------- bool BuildingObject::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()); } if (!playersWithAlignedEnemyFlagsAllowed() && (Pvp::hasAnyAlignedTimedEnemyFlag(who) || Pvp::hasAnyBountyDuelEnemyFlag(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() && getOwnerId() == NetworkId::cms_invalid) return true; // allow vendor-creatures to be placed in the building if (who.hasCondition(static_cast(ServerTangibleObjectTemplate::C_vendor))) return true; return CellPermissions::isOnList(m_allowed.get(), who); } } //------------------------------------------------------------------------------------------ bool BuildingObject::isPlayerPlaced() const { // only non-gold/non-buildout buildings can be player placed return (getObjVars().hasItem("player_structure") && (getCacheVersion() == 0) && (getNetworkId().getValue() > static_cast(0))); } //------------------------------------------------------------------------------------------ bool BuildingObject::isSpaceDungeon() const { return getObjVars().hasItem("space_dungeon.name"); } // ---------------------------------------------------------------------- bool BuildingObject::playersWithAlignedEnemyFlagsAllowed() const { return !isPlayerPlaced() || getObjVars().hasItem("playersWithAlignedEnemyFlagsAllowed"); } // ---------------------------------------------------------------------- bool BuildingObject::isVisibleOnClient (const Client & /*client*/) const { return true; } // ---------------------------------------------------------------------- void BuildingObject::getAttributes(stdvector >::fwd &data) const { TangibleObject::getAttributes(data); } // ---------------------------------------------------------------------- BuildingObject * BuildingObject::asBuildingObject() { return this; } //----------------------------------------------------------------------- BuildingObject const * BuildingObject::asBuildingObject() const { return this; } // ---------------------------------------------------------------------- void BuildingObject::onAddedToWorld() { TangibleObject::onAddedToWorld(); m_checkCreatePortalVolume = true; scheduleForAlter(); Container* container = ContainerInterface::getContainer(*this); if (container) { ContainerIterator i = container->begin(); for(; i != container->end(); ++i) { ServerObject * obj = safe_cast((*i).getObject()); if (obj && obj->isPlayerControlled()) { gainedPlayer(*obj); } } } } // ---------------------------------------------------------------------- void BuildingObject::onAllContentsLoaded() { TangibleObject::onAllContentsLoaded(); m_contentsLoaded = true; ServerObject * containedBy = safe_cast(ContainerInterface::getFirstParentInWorld(*this)); bool fixHouseSceneIds = true; if (containedBy) { const NetworkId& owner = containedBy->getLoadWith(); CreatureObject * characterOwner = CreatureObject::getCreatureObject(owner); if (characterOwner) { LOG("CustomerService", ("CharacterTransfer: packed house loaded for building (%s) contained by %s.", getNetworkId().getValueString().c_str(), PlayerObject::getAccountDescription(characterOwner).c_str())); Client * const client = containedBy->getClient(); if (client) { CreatureObject * const containedByCreature = containedBy->asCreatureObject(); if (containedByCreature && containedByCreature->getLoadCTSPackedHouses()) { containedByCreature->packedHouseLoaded(); fixHouseSceneIds = false; } } } } if(fixHouseSceneIds && !getObjVars().hasItem("packed_house.fix_contained_scene_ids")) { setSceneIdOnThisAndContents(getSceneId()); setObjVarItem("packed_house.fix_contained_scene_ids", 1); } NetworkId owner; if(getObjVars().getItem("purge_process.rollup_on_load", owner)) rollupStructure(owner, false); } //------------------------------------------------------------------------------------------ void BuildingObject::onCellContentsLoaded() { if (!isPlayerPlaced()) return; bool doFixup = true; Container *container = ContainerInterface::getContainer(*this); if (container) { for (ContainerIterator i = container->begin(); i != container->end(); ++i) { ServerObject * cell = safe_cast((*i).getObject()); if (!cell || !cell->areContentsLoaded()) { doFixup = false; break; } } } if (doFixup) { fixUpPobItemLimit(); onAllContentsLoaded(); } } //------------------------------------------------------------------------------------------ void BuildingObject::loadContents(bool immediate /*= false*/) { if (isAuthoritative()) { // delaying the loading of the house's contents is useful to emulate a live // environment where it takes significantly longer to load a house's contents if (!immediate && (ConfigServerGame::getLoadHouseContentsDelaySeconds() > 0)) { // don't need to queue up multiple C++LoadBuildingContents messages; // only one is necessary to delay load the house's contents if (timeUntilMessageTo("C++LoadBuildingContents") < 0) { MessageToQueue::getInstance().sendMessageToC(getNetworkId(), "C++LoadBuildingContents", "", ConfigServerGame::getLoadHouseContentsDelaySeconds(), false); } return; } Container *container = ContainerInterface::getContainer(*this); if (container) { //Demand load house cells for (ContainerIterator i = container->begin(); i != container->end(); ++i) { ServerObject * cell = safe_cast((*i).getObject()); DEBUG_FATAL(!cell, ("Null cell in building list")); if (cell) { if (cell->getLoadContents()) { cell->setLoadContents(false); Container * cellContainer = ContainerInterface::getContainer(*cell); if (cellContainer) { for (ContainerIterator j = cellContainer->begin(); j != cellContainer->end(); ++j) { ServerObject * obj = safe_cast((*j).getObject()); if (obj) { updateLoadWithToCell(*cell, *obj); } } } } } } //Demand load contents of house cells for (ContainerIterator j = container->begin(); j != container->end(); ++j) { ServerObject * cell = safe_cast((*j).getObject()); DEBUG_FATAL(!cell, ("Null cell in building list")); if (cell) { if (cell->areContentsLoaded()) break; cell->loadAllContentsFromDB(); m_unloadTime = ServerClock::getInstance().getGameTimeSeconds() + static_cast(ConfigServerGame::getUnloadHouseContentsTime()); scheduleForAlter(); } } } } } // ---------------------------------------------------------------------- void BuildingObject::gainedPlayer(ServerObject const &) { if (isPlayerPlaced()) { if (ConfigServerGame::getDemandLoadHouseContents()) { loadContents(); } else { int version = 0; IGNORE_RETURN(getObjVars().getItem("player_structure.version", version)); int newVersion = version; if (version < 3) { fixUpPobItemLimit(); newVersion = 3; } IGNORE_RETURN(setObjVarItem("player_structure.version", newVersion)); } } } // ---------------------------------------------------------------------- void BuildingObject::lostPlayer(ServerObject const &) { if (isPlayerPlaced() && ConfigServerGame::getDemandLoadHouseContents() && isAuthoritative()) { m_unloadTime = ServerClock::getInstance().getGameTimeSeconds() + static_cast(ConfigServerGame::getUnloadHouseContentsTime()); scheduleForAlter(); } } //------------------------------------------------------------------------------------------ void BuildingObject::changeTeleportDestination(Vector & position, float & yaw) const { if (!isAuthoritative()) { WARNING(true, ("BuildingObject::changeTeleportDestination called on " "non-authoritative building %s", getNetworkId().getValueString().c_str())); return; } // call a script trigger that will give us the index of the cloning facility // tube to respawn in, if any int index = -1; ScriptParams params; params.addParam(index); IGNORE_RETURN(const_cast(getScriptObject())->trigAllScripts( Scripting::TRIG_GET_RESPAWN_LOC, params)); index = params.getIntParam(0); if (index >= 0) { DataTable * respawnTable = DataTableManager::getTable(CLONE_RESPAWN_TABLE, true); if (respawnTable != nullptr) { int row = respawnTable->searchColumnString(0, getTemplateName()); if (row >= 0) { char buffer[32]; sprintf(buffer, "TUBE%d_X", index+1); int column = respawnTable->findColumnNumber(buffer); if (column >= 0) { position.x = respawnTable->getFloatValue(column, row); position.z = respawnTable->getFloatValue(column+1, row); yaw = convertDegreesToRadians(respawnTable->getFloatValue(column+2, row)); } } } } } // ---------------------------------------------------------------------- void BuildingObject::setIsPublic(bool isPublic) { if (isAuthoritative()) m_isPublic.set(isPublic); else sendControllerMessageToAuthServer(CM_setBuildingIsPublic, new MessageQueueGenericValueType(isPublic)); } // ---------------------------------------------------------------------- void BuildingObject::setCityId(int cityId) { if (isAuthoritative()) m_cityId.set(cityId); else sendControllerMessageToAuthServer(CM_setBuildingCityId, new MessageQueueGenericValueType(cityId)); } // ---------------------------------------------------------------------- void BuildingObject::handleCMessageTo(const MessageToPayload &message) { if (message.getMethod() == "C++PurgeStructure") { NetworkId owner(message.getDataAsString()); LOG("CustomerService",("Purge: Purging building %s",getNetworkId().getValueString().c_str())); if (m_contentsLoaded.get()) rollupStructure(owner, false); else { IGNORE_RETURN(setObjVarItem("purge_process.rollup_on_load", owner)); loadContents(); } } else if (message.getMethod() == "C++CharacterSheetInfoResidenceLocationReq") { if (!message.getPackedDataVector().empty()) { // get network id of the player asking for the information std::string const packedData(message.getPackedDataVector().begin(), message.getPackedDataVector().end()); NetworkId const idRequester(packedData); if (idRequester.isValid() && MessageToQueue::isInstalled()) { Vector const loc = getPosition_w(); char buffer[256]; snprintf(buffer, sizeof(buffer)-1, "%s %d %d %d", ServerWorld::getSceneId().c_str(), static_cast(loc.x), static_cast(loc.y), static_cast(loc.z)); buffer[sizeof(buffer)-1] = '\0'; MessageToQueue::getInstance().sendMessageToC(idRequester, "C++CharacterSheetInfoResidenceLocationRsp", buffer, 0, false); } } } else if (message.getMethod() == "C++CityShareGroupPickupPointTravelCost") { if (!message.getPackedDataVector().empty()) { std::string const packedData(message.getPackedDataVector().begin(), message.getPackedDataVector().end()); int const cityShareOfGroupPickupPointTravelCost = atoi(packedData.c_str()); if (cityShareOfGroupPickupPointTravelCost > 0) transferBankCreditsFrom("GroupPickupPointTravel", cityShareOfGroupPickupPointTravelCost); } } else if (message.getMethod() == "C++LoadBuildingContents") { loadContents(true); } else TangibleObject::handleCMessageTo(message); } //----------------------------------------------------------------------- const BuildingObject::AllBuildingsSet & BuildingObject::getAllBuildings() { return g_buildingList; } //----------------------------------------------------------------------- bool BuildingObject::unloadContents() { // make sure there are no players in the building and its valid to have its contents unloaded if (!hasPlayer(*this) && isPlayerPlaced() && ConfigServerGame::getDemandLoadHouseContents() && isAuthoritative()) { m_unloadTime = 0; unloadCellsContents(*this); m_contentsLoaded = false; } return !m_contentsLoaded.get(); } // ======================================================================