// Cyphesis Online RPG Server and AI Engine
// Copyright (C) 2000-2005 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
// $Id: MemMap.cpp,v 1.102 2007-12-02 23:49:06 alriddoch Exp $
#include "MemMap.h"
#include "MemEntity.h"
#include "Script.h"
#include "common/id.h"
#include "common/log.h"
#include "common/debug.h"
#include "common/compose.hpp"
#include "common/inheritance.h"
#include <Atlas/Objects/Operation.h>
#include <Atlas/Objects/Anonymous.h>
#include <sstream>
static const bool debug_flag = false;
using Atlas::Message::Element;
using Atlas::Message::MapType;
using Atlas::Objects::Operation::Look;
using Atlas::Objects::Entity::RootEntity;
using Atlas::Objects::Entity::Anonymous;
const TypeNode * MemMap::m_entity_type = 0;
MemEntity * MemMap::addEntity(MemEntity * entity)
{
assert(entity != 0);
assert(!entity->getId().empty());
debug(std::cout << "MemMap::addEntity " << entity << " " << entity->getId()
<< std::endl << std::flush;);
long next = -1;
if (m_checkIterator != m_entities.end()) {
next = m_checkIterator->first;
}
m_entities[entity->getIntId()] = entity;
m_checkIterator = m_entities.find(next);
debug( std::cout << this << std::endl << std::flush;);
std::vector<std::string>::const_iterator I = m_addHooks.begin();
std::vector<std::string>::const_iterator Iend = m_addHooks.end();
for (; I != Iend; ++I) {
m_script->hook(*I, entity);
}
return entity;
}
void MemMap::readEntity(MemEntity * entity, const RootEntity & ent)
// Read the contents of an Atlas message into an entity
{
if (ent->hasAttrFlag(Atlas::Objects::PARENTS_FLAG)) {
const std::list<std::string> & parents = ent->getParents();
if (!parents.empty()) {
if (entity->getType() == m_entity_type) {
const TypeNode * type = Inheritance::instance().getType(parents.front());
if (type != 0) {
entity->setType(type);
}
} else if (entity->getType()->name() != parents.front()) {
debug(std::cout << "Attempting to mutate " << entity->getType()
<< " into " << parents.front()
<< std::endl << std::flush;);
}
}
}
entity->merge(ent->asMessage());
if (ent->hasAttrFlag(Atlas::Objects::Entity::LOC_FLAG)) {
LocatedEntity * old_loc = entity->m_location.m_loc;
const std::string & new_loc_id = ent->getLoc();
// Has LOC been changed?
if (old_loc == 0 || new_loc_id != old_loc->getId()) {
entity->m_location.m_loc = getAdd(new_loc_id);
assert(old_loc != entity->m_location.m_loc);
if (old_loc != 0) {
old_loc->m_contains.erase(entity);
}
entity->m_location.m_loc->m_contains.insert(entity);
}
entity->m_location.readFromEntity(ent);
}
addContents(ent);
}
void MemMap::updateEntity(MemEntity * entity, const RootEntity & ent)
// Update contents of entity an Atlas message.
{
assert(entity != 0);
debug( std::cout << " got " << entity << std::endl << std::flush;);
readEntity(entity, ent);
std::vector<std::string>::const_iterator K = m_updateHooks.begin();
std::vector<std::string>::const_iterator Kend = m_updateHooks.end();
for (; K != Kend; ++K) {
m_script->hook(*K, entity);
}
}
MemEntity * MemMap::newEntity(const std::string & id, long int_id,
const RootEntity & ent)
// Create a new entity from an Atlas message.
{
assert(m_entities.find(int_id) == m_entities.end());
MemEntity * entity = new MemEntity(id, int_id);
entity->setType(m_entity_type);
readEntity(entity, ent);
return addEntity(entity);
}
MemMap::MemMap(Script *& s) : m_checkIterator(m_entities.begin()), m_script(s)
{
if (m_entity_type == 0) {
m_entity_type = Inheritance::instance().getType("game_entity");
assert(m_entity_type != 0);
}
}
void MemMap::sendLooks(OpVector & res)
{
debug( std::cout << "MemMap::sendLooks" << std::endl << std::flush;);
std::list<std::string>::const_iterator I = m_additionsById.begin();
std::list<std::string>::const_iterator Iend = m_additionsById.end();
for (; I != Iend; ++I) {
Look l;
Anonymous look_arg;
look_arg->setId(*I);
l->setArgs1(look_arg);
res.push_back(l);
}
m_additionsById.clear();
}
MemEntity * MemMap::addId(const std::string & id, long int_id)
// Queue the ID of an entity we are interested in
{
assert(!id.empty());
assert(m_entities.find(int_id) == m_entities.end());
debug( std::cout << "MemMap::add_id" << std::endl << std::flush;);
m_additionsById.push_back(id);
MemEntity * entity = new MemEntity(id, int_id);
entity->setType(m_entity_type);
return addEntity(entity);
}
void MemMap::del(const std::string & id)
// Delete an entity from memory
{
debug( std::cout << "MemMap::del(" << id << ")" << std::endl << std::flush;);
long int_id = integerId(id);
MemEntityDict::iterator I = m_entities.find(int_id);
if (I != m_entities.end()) {
MemEntity * ent = I->second;
assert(ent != 0);
long next = -1;
if (m_checkIterator != m_entities.end()) {
next = m_checkIterator->first;
}
m_entities.erase(I);
// Handling re-parenting is done very similarly to Entity::destroy,
// but is slightly different as we tolerate LOC being null.
LocatedEntity * ent_loc = ent->m_location.m_loc;
if (ent_loc != 0) {
// Remove deleted entity from its parents contains
ent_loc->m_contains.erase(ent);
}
// FIXME This is required until MemMap uses parent refcounting
ent->m_location.m_loc = 0;
// Add deleted entities children into its parents contains
LocatedEntitySet::const_iterator K = ent->m_contains.begin();
LocatedEntitySet::const_iterator Kend = ent->m_contains.end();
for (; K != Kend; ++K) {
LocatedEntity * child_ent = *K;
child_ent->m_location.m_loc = ent_loc;
// FIXME adjust pos and:
// FIXME take account of orientation
if (ent_loc != 0) {
ent_loc->m_contains.insert(child_ent);
}
}
if (next != -1) {
m_checkIterator = m_entities.find(next);
} else {
m_checkIterator = m_entities.begin();
}
std::vector<std::string>::const_iterator J = m_deleteHooks.begin();
std::vector<std::string>::const_iterator Jend = m_deleteHooks.end();
for(; J != Jend; ++J) {
m_script->hook(*J, ent);
}
ent->decRef();
}
}
MemEntity * MemMap::get(const std::string & id) const
// Get an entity from memory
{
debug( std::cout << "MemMap::get" << std::endl << std::flush;);
if (id.empty()) {
// This shouldn't really occur, and shouldn't be a problem
log(ERROR, "MemMap::get queried for empty ID string.");
return NULL;
}
long int_id = integerId(id);
MemEntityDict::const_iterator I = m_entities.find(int_id);
if (I != m_entities.end()) {
assert(I->second != 0);
return I->second;
}
return NULL;
}
MemEntity * MemMap::getAdd(const std::string & id)
// Get an entity from memory, or add it if we haven't seen it yet
// This could be implemented by calling get() for all but the the last line
{
debug( std::cout << "MemMap::getAdd(" << id << ")" << std::endl << std::flush;);
if (id.empty()) {
return NULL;
}
long int_id = integerId(id);
if (int_id == -1) {
log(ERROR, String::compose("MemMap::getAdd: Invalid ID \"%1\".", id));
return NULL;
}
MemEntityDict::const_iterator I = m_entities.find(int_id);
if (I != m_entities.end()) {
assert(I->second != 0);
return I->second;
}
return addId(id, int_id);
}
void MemMap::addContents(const RootEntity & ent)
// Iterate over the contains attribute of a message, looking at all the contents
{
if (!ent->hasAttrFlag(Atlas::Objects::Entity::CONTAINS_FLAG)) {
return;
}
const std::list<std::string> & contlist = ent->getContains();
std::list<std::string>::const_iterator Jend = contlist.end();
std::list<std::string>::const_iterator J = contlist.begin();
for (; J != Jend; ++J) {
getAdd(*J);
}
}
MemEntity * MemMap::updateAdd(const RootEntity & ent, const double & d)
// Update an entity in our memory, from an Atlas message
// The mind code relies on this function never sending a Sight to
// be sure that seeing something created does not imply that the created
// entity is visible, as we may have received it because we can see the
// creator.
{
debug( std::cout << "MemMap::updateAdd" << std::endl << std::flush;);
if (!ent->hasAttrFlag(Atlas::Objects::ID_FLAG)) {
log(ERROR, "MemMap::updateAdd, Missing id in updated entity");
return NULL;
}
const std::string & id = ent->getId();
if (id.empty()) {
log(ERROR, "MemMap::updateAdd, Empty ID in updated entity.");
return NULL;
}
long int_id = integerId(id);
if (int_id == -1) {
log(ERROR, String::compose("MemMap::updateAdd: Invalid ID \"%1\".", id));
return NULL;
}
MemEntityDict::const_iterator I = m_entities.find(int_id);
MemEntity * entity;
if (I == m_entities.end()) {
entity = newEntity(id, int_id, ent);
} else {
entity = I->second;
updateEntity(entity, ent);
}
entity->update(d);
return entity;
}
MemEntityVector MemMap::findByType(const std::string & what)
// Find an entity in our memory of a certain type
{
MemEntityVector res;
MemEntityDict::const_iterator Iend = m_entities.end();
for (MemEntityDict::const_iterator I = m_entities.begin(); I != Iend; ++I) {
MemEntity * item = I->second;
debug( std::cout << "F" << what << ":" << item->getType() << ":" << item->getId() << std::endl << std::flush;);
if (item->isVisible() && item->getType()->name() == what) {
res.push_back(I->second);
}
}
return res;
}
MemEntityVector MemMap::findByLocation(const Location & loc, double radius,
const std::string & what)
// Find an entity in our memory in a certain place
// FIXME Don't return by value
{
MemEntityVector res;
#if 0
MemEntityDict::const_iterator Iend = m_entities.end();
for (MemEntityDict::const_iterator I = m_entities.begin(); I != Iend; ++I) {
MemEntity * item = I->second;
if (!item->isVisible()) {
continue;
}
const Location & oloc = I->second->m_location;
if (!loc.isValid() || !oloc.isValid()) {
continue;
}
if ((oloc.m_loc->getId() == loc.m_loc->getId()) &&
(squareDistance(loc.pos(), oloc.pos()) < (radius * radius))) {
res.push_back(I->second);
}
}
return res;
#else
LocatedEntity * place = loc.m_loc;
MemEntity * place_by_id = get(place->getId());
if (place != place_by_id) {
log(ERROR, "WTF!");
return res;
}
LocatedEntitySet::const_iterator I = place->m_contains.begin();
LocatedEntitySet::const_iterator Iend = place->m_contains.end();
float square_range = radius * radius;
for (; I != Iend; ++I) {
assert(*I != 0);
MemEntity * item = dynamic_cast<MemEntity *>(*I);
if (item == 0) {
log(ERROR, "Weird entity in memory");
continue;
}
if (!item->isVisible() || item->getType()->name() != what) {
continue;
}
if (squareDistance(loc.pos(), item->m_location.pos()) < square_range) {
res.push_back(item);
}
}
return res;
#endif
}
const Element MemMap::asMessage()
{
MapType omap;
MemEntityDict::const_iterator Iend = m_entities.end();
for (MemEntityDict::const_iterator I = m_entities.begin(); I != Iend; ++I) {
I->second->addToMessage((omap[I->second->getId()] = MapType()).asMap());
}
return Element(omap);
}
void MemMap::check(const double & time)
{
MemEntityDict::const_iterator entities_end = m_entities.end();
if (m_checkIterator == entities_end) {
m_checkIterator = m_entities.begin();
} else {
MemEntity * me = m_checkIterator->second;
assert(me != 0);
if (!me->isVisible() && ((time - me->lastSeen()) > 600) && (me->m_contains.empty())) {
debug(std::cout << me->getId() << "|" << me->getType()->name()
<< " is a waste of space" << std::endl << std::flush;);
MemEntityDict::const_iterator J = m_checkIterator;
long next = -1;
if (++J != entities_end) {
next = J->first;
}
m_entities.erase(m_checkIterator);
// Remove deleted entity from its parents contains attribute
if (me->m_location.m_loc != 0) {
me->m_location.m_loc->m_contains.erase(me);
}
// FIXME This is required until MemMap uses parent refcounting
me->m_location.m_loc = 0;
if (next != -1) {
m_checkIterator = m_entities.find(next);
} else {
m_checkIterator = m_entities.begin();
}
// attribute of its its parent.
me->decRef();
} else {
debug(std::cout << me->getId() << "|" << me->getType()->name() << "|"
<< me->lastSeen() << "|" << me->isVisible()
<< " is fine" << std::endl << std::flush;);
++m_checkIterator;
}
}
}
void MemMap::flush()
{
debug(std::cout << "Flushing memory with " << m_entities.size()
<< " memories" << std::endl << std::flush;);
MemEntityDict::const_iterator Iend = m_entities.end();
for (MemEntityDict::const_iterator I = m_entities.begin(); I != Iend; ++I) {
// FIXME This is required until MemMap uses parent refcounting
I->second->m_location.m_loc = 0;
I->second->decRef();
}
}
syntax highlighted by Code2HTML, v. 0.9.1