cyphesis/server/EntityRuleHandler.cpp
Erik Ogenvik 45555fd698 Use custom format for archetypes.
This required the EntityKit to be refactored, so that most fields are
pushed into EntityFactory. Due to the nature of how the rule handlers
and the factories interact the code is pretty messy. It works, but it's
tangled and not really clean.
2013-11-15 21:39:35 +01:00

235 lines
8.5 KiB
C++

// Cyphesis Online RPG Server and AI Engine
// Copyright (C) 2009 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 "EntityRuleHandler.h"
#include "EntityBuilder.h"
#include "EntityFactory.h"
#include "Player.h"
#include "rulesets/PythonScriptFactory.h"
#include "common/log.h"
#include "common/debug.h"
#include "common/compose.hpp"
#include "common/EntityKit.h"
#include <iostream>
using Atlas::Message::Element;
using Atlas::Message::MapType;
using Atlas::Message::ListType;
using Atlas::Objects::Root;
using String::compose;
static const bool debug_flag = false;
int EntityRuleHandler::installEntityClass(const std::string & class_name,
const std::string & parent,
const Root & class_desc,
std::string & dependent,
std::string & reason)
{
assert(class_name == class_desc->getId());
// Get the new factory for this rule
EntityFactoryBase * parent_factory = dynamic_cast<EntityFactoryBase*>(m_builder->getClassFactory(parent));
if (parent_factory == 0) {
debug(std::cout << "class \"" << class_name
<< "\" has non existant parent \"" << parent
<< "\". Waiting." << std::endl << std::flush;);
dependent = parent;
reason = compose("Entity rule \"%1\" has parent \"%2\" which does "
"not exist.", class_name, parent);
return 1;
}
EntityFactoryBase * factory = parent_factory->duplicateFactory();
if (factory == 0) {
log(ERROR,
compose("Attempt to install rule \"%1\" which has parent \"%2\" "
"which cannot be instantiated", class_name, parent));
return -1;
}
assert(factory->m_parent == parent_factory);
if (populateEntityFactory(class_name, factory,
class_desc->asMessage(),
dependent,
reason) != 0) {
delete factory;
return -1;
}
debug(std::cout << "INSTALLING " << class_name << ":" << parent
<< std::endl << std::flush;);
// Install the factory in place.
if (m_builder->installFactory(class_name, class_desc, factory) != 0) {
delete factory;
return -1;
}
factory->addProperties();
// Add it as a child to its parent.
parent_factory->m_children.insert(factory);
return 0;
}
int EntityRuleHandler::modifyEntityClass(const std::string & class_name,
const Root & class_desc)
{
assert(class_name == class_desc->getId());
EntityFactoryBase* factory = dynamic_cast<EntityFactoryBase*>(m_builder->getClassFactory(class_name));
if (factory == 0) {
log(ERROR, compose("Could not find factory for existing entity class "
"\"%1\".", class_name));
return -1;
}
assert(factory != 0);
MapType backup_attributes = factory->m_attributes,
backup_class_attributes = factory->m_classAttributes;
// Copy the defaults from the parent. In populateEntityFactory this may be
// overriden with the defaults for this class.
if (factory->m_parent != 0) {
factory->m_attributes = factory->m_parent->m_attributes;
} else {
// This is non fatal, but nice to know it has happened.
// This should only happen if the client attempted to modify the
// type data for a core hard coded type.
log(ERROR, compose("EntityRuleHandler::modifyEntityClass: \"%1\" modified "
"by client, but has no parent factory.",
class_name));
factory->m_attributes = MapType();
}
factory->m_classAttributes = MapType();
std::string dependent, reason;
if (populateEntityFactory(class_name, factory,
class_desc->asMessage(),
dependent, reason) != 0) {
factory->m_attributes = backup_attributes;
factory->m_classAttributes = backup_class_attributes;
return -1;
}
factory->updateProperties();
return 0;
}
int EntityRuleHandler::populateEntityFactory(const std::string & class_name,
EntityFactoryBase * factory,
const MapType & class_desc,
std::string & dependent,
std::string & reason)
{
// assert(class_name == class_desc->getId());
// Establish whether this rule has an associated script, and
// if so, use it.
MapType::const_iterator J = class_desc.find("script");
MapType::const_iterator Jend = class_desc.end();
if (J != Jend && J->second.isMap()) {
const MapType & script = J->second.asMap();
std::string script_package;
std::string script_class;
if (getScriptDetails(script, class_name, "Entity",
script_package, script_class) != 0) {
return -1;
}
if (factory->m_scriptFactory == 0 ||
factory->m_scriptFactory->package() != script_package) {
PythonScriptFactory<LocatedEntity> * psf =
new PythonScriptFactory<LocatedEntity>(script_package,
script_class);
if (psf->setup() == 0) {
delete factory->m_scriptFactory;
factory->m_scriptFactory = psf;
} else {
log(ERROR, compose("Python class \"%1.%2\" failed to load",
script_package, script_class));
delete psf;
return -1;
}
} else {
// FIXME If this fails, that's bad.
factory->m_scriptFactory->refreshClass();
}
}
// Store the default attribute for entities create by this rule.
J = class_desc.find("attributes");
if (J != Jend && J->second.isMap()) {
const MapType & attrs = J->second.asMap();
MapType::const_iterator Kend = attrs.end();
for (MapType::const_iterator K = attrs.begin(); K != Kend; ++K) {
if (!K->second.isMap()) {
log(ERROR, compose("Attribute description in rule %1 is not a "
"map.", class_name));
continue;
}
const MapType & attr = K->second.asMap();
MapType::const_iterator L = attr.find("default");
if (L != attr.end()) {
// Store this value in the defaults for this class
factory->m_classAttributes[K->first] = L->second;
// and merge it with the defaults inherited from the parent
factory->m_attributes[K->first] = L->second;
}
}
}
// Check whether it should be available to players as a playable character.
J = class_desc.find("playable");
if (J != Jend && J->second.isInt()) {
Player::playableTypes.insert(class_name);
}
return 0;
}
int EntityRuleHandler::check(const Atlas::Objects::Root & desc)
{
assert(!desc->getParents().empty());
if (desc->getObjtype() != "class") {
return -1;
}
return m_builder->isTask(desc->getParents().front()) ? -1 : 0;
}
int EntityRuleHandler::install(const std::string & name,
const std::string & parent,
const Atlas::Objects::Root & description,
std::string & dependent,
std::string & reason)
{
return installEntityClass(name, parent, description, dependent, reason);
}
int EntityRuleHandler::update(const std::string & name,
const Atlas::Objects::Root & desc)
{
return modifyEntityClass(name, desc);
}