// Cyphesis Online RPG Server and AI Engine // Copyright (C) 2000,2001 Alistair Riddoch // // This program 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 // (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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "ExternalMind.h" #include "rulesets/LocatedEntity.h" #include "common/BaseWorld.h" #include "common/compose.hpp" #include "common/Link.h" #include "common/log.h" #include #include #include using Atlas::Message::Element; using Atlas::Objects::Root; using Atlas::Objects::smart_dynamic_cast; using Atlas::Objects::Entity::RootEntity; using Atlas::Objects::Entity::Anonymous; using Atlas::Objects::Operation::Sight; using Atlas::Objects::Operation::Delete; using Atlas::Objects::Operation::Imaginary; static const double character_expire_time = 60 * 60; // 1 hour void ExternalMind::deleteEntity(const std::string & id, bool forceDelete) { Delete d; Anonymous del_arg; del_arg->setId(id); d->setArgs1(del_arg); if (forceDelete) { //We can force a delete by directing the Delete op to the world; //this bypasses any property which might prevent the deletion. d->setTo("0"); } else { d->setTo(id); } m_entity.sendWorld(d); } void ExternalMind::purgeEntity(const LocatedEntity & ent, bool forceDelete) { if (ent.m_contains != 0) { LocatedEntitySet::const_iterator I = ent.m_contains->begin(); LocatedEntitySet::const_iterator Iend = ent.m_contains->end(); for (; I != Iend; ++I) { LocatedEntity * child = *I; assert(child != 0); purgeEntity(*child); } } deleteEntity(ent.getId(), forceDelete); } ExternalMind::ExternalMind(LocatedEntity & e) : Router(e.getId(), e.getIntId()), m_external(0), m_entity(e), m_lossTime(0.) { } ExternalMind::~ExternalMind() { } void ExternalMind::externalOperation(const Operation & op, Link &) { log(ERROR, String::compose("%1 called", __PRETTY_FUNCTION__)); } void ExternalMind::operation(const Operation & op, OpVector & res) { //TODO: remove this hacked in fix. //So, what's happening here is that if the external connection has been disconnected, and //the entity either is marked as ephemeral, or has been inactive for an hour, it is //deleted along with all of its inventory. This is to prevent the starting position //being spammed by abandoned characters. Now, there are a lot of better ways to handle this. //One solution is to make sure that all new entities are created in an instanced location. //Thus players have to actively move from the instanced location to the main world, which //would make sure that only those players which are active end up in the real world. //Another solution is to do something with the entity when the connection is cut; perhaps move //it to limbo or some other place. All of these solutions are better than just deleting it. if (m_external == 0) { if (m_entity.getFlags() & entity_ephem) { // If this entity no longer has a connection, and is ephemeral // we should delete it. if (op->getClassNo() != Atlas::Objects::Operation::DELETE_NO) { purgeEntity(m_entity); } } // if (BaseWorld::instance().getTime() - m_lossTime > character_expire_time) { // if (op->getClassNo() != Atlas::Objects::Operation::DELETE_NO) { // //reset m_lossTime since it's not a given that the entity will be deleted // //(properties such as respawnable might intervene) // m_lossTime = BaseWorld::instance().getTime(); // purgeEntity(m_entity); // } // } return; } m_external->send(op); // Here we see if there is anything we should be sending the user // extra info about. The initial demo implementation checks for // Set ops which make the characters status less than 0.1, and sends // emotes that the character is hungry. const std::vector & args = op->getArgs(); if (op->getClassNo() == Atlas::Objects::Operation::SIGHT_NO && !args.empty()) { Operation sub_op = smart_dynamic_cast(args.front()); if (sub_op.isValid()) { const std::vector & sub_args = sub_op->getArgs(); if (sub_op->getClassNo() == Atlas::Objects::Operation::SET_NO && !sub_args.empty()) { const Root & arg = sub_args.front(); Element status_value; if (arg->getId() == getId() and arg->copyAttr("status", status_value) == 0 and status_value.isFloat() and status_value.Float() < 0.1) { Anonymous imaginary_arg; imaginary_arg->setId(getId()); if (status_value.Float() < 0.01) { imaginary_arg->setAttr("description", "is starving."); } else { imaginary_arg->setAttr("description", "is hungry."); } Imaginary imaginary; imaginary->setTo(getId()); imaginary->setFrom(getId()); imaginary->setArgs1(imaginary_arg); Sight sight; sight->setTo(getId()); sight->setFrom(getId()); sight->setArgs1(imaginary); m_external->send(sight); } } } } } const std::string & ExternalMind::connectionId() { assert(m_external != 0); return m_external->getId(); } void ExternalMind::linkUp(Link * c) { m_external = c; if (c == 0) { m_lossTime = BaseWorld::instance().getTime(); } }