mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
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.
272 lines
9.6 KiB
C++
272 lines
9.6 KiB
C++
// Cyphesis Online RPG Server and AI Engine
|
|
// Copyright (C) 2011 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 "TaskRuleHandler.h"
|
|
|
|
#include "EntityBuilder.h"
|
|
#include "TaskFactory.h"
|
|
#include "EntityFactory.h"
|
|
|
|
#include "rulesets/PythonScriptFactory.h"
|
|
|
|
#include "common/log.h"
|
|
#include "common/debug.h"
|
|
#include "common/EntityKit.h"
|
|
#include "common/Inheritance.h"
|
|
#include "common/compose.hpp"
|
|
|
|
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 TaskRuleHandler::populateTaskFactory(const std::string & class_name,
|
|
TaskKit * factory,
|
|
const Root & class_desc,
|
|
std::string & dependent,
|
|
std::string & reason)
|
|
{
|
|
// assert(class_name == class_desc->getId());
|
|
|
|
// Establish that this rule has an associated script.
|
|
Element script_attr;
|
|
if (class_desc->copyAttr("script", script_attr) != 0 ||
|
|
!script_attr.isMap()) {
|
|
log(ERROR, compose("Task \"%1\" has no script.", class_name));
|
|
return -1;
|
|
}
|
|
const MapType & script = script_attr.Map();
|
|
std::string script_package;
|
|
std::string script_class;
|
|
|
|
if (getScriptDetails(script, class_name, "Task",
|
|
script_package, script_class) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
Element activation_attr;
|
|
if (class_desc->copyAttr("activation", activation_attr) != 0 ||
|
|
!activation_attr.isMap()) {
|
|
log(ERROR, compose("Task \"%1\" has no activation.", class_name));
|
|
return -1;
|
|
}
|
|
const MapType & activation = activation_attr.Map();
|
|
|
|
MapType::const_iterator act_end = activation.end();
|
|
MapType::const_iterator J = activation.find("tool");
|
|
if (J == act_end || !J->second.isString()) {
|
|
log(ERROR, compose("Task \"%1\" activation has no tool.", class_name));
|
|
return -1;
|
|
}
|
|
const std::string & activation_tool = J->second.String();
|
|
|
|
J = activation.find("operation");
|
|
if (J == act_end || !J->second.isString()) {
|
|
log(ERROR, compose("Task \"%1\" activation has no operation.",
|
|
class_name));
|
|
return -1;
|
|
}
|
|
|
|
const std::string & activation_op = J->second.String();
|
|
|
|
Inheritance & i = Inheritance::instance();
|
|
|
|
J = activation.find("target");
|
|
if (J != act_end) {
|
|
if (!J->second.isString()) {
|
|
log(ERROR, compose("Task \"%1\" activation has \"%2\" target.",
|
|
class_name,
|
|
Element::typeName(J->second.getType())));
|
|
return -1;
|
|
}
|
|
const std::string & target_desc = J->second.String();
|
|
std::string::size_type dot = target_desc.find('.');
|
|
if (dot != 0) {
|
|
// If there is no dot, or the dot is not the first character,
|
|
// then the first part of the string is the class to be matched
|
|
// This works if there is a dot, or if the search returned npos
|
|
std::string target_class = target_desc.substr(0, dot);
|
|
const TypeNode * target_type = i.getType(target_class);
|
|
if (target_type == 0) {
|
|
dependent = target_class;
|
|
reason = compose("Task \"%1\" is activated on target \"%2\" "
|
|
"which does not exist.", class_name,
|
|
target_class);
|
|
return 1;
|
|
}
|
|
factory->setTarget(target_type);
|
|
}
|
|
if (dot != std::string::npos) {
|
|
if (++dot < target_desc.size()) {
|
|
std::string target_property = target_desc.substr(dot);
|
|
factory->setRequireProperty(target_property);
|
|
} else {
|
|
log(WARNING, compose("Task rule \"%1\" provide empty string "
|
|
"for activation property", class_name));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!i.hasClass(activation_tool)) {
|
|
dependent = activation_tool;
|
|
reason = compose("Task \"%1\" is activated by tool \"%2\" which "
|
|
"does not exist.", class_name, activation_tool);
|
|
return 1;
|
|
}
|
|
EntityFactoryBase * tool_factory = dynamic_cast<EntityFactoryBase*>(m_builder->getClassFactory(activation_tool));
|
|
if (tool_factory == 0) {
|
|
log(ERROR, compose("Task class \"%1\" is activated by tool \"%2\" "
|
|
"which is not an entity class.", class_name,
|
|
activation_tool));
|
|
return -1;
|
|
}
|
|
|
|
if (!i.hasClass(activation_op)) {
|
|
dependent = activation_op;
|
|
reason = compose("Task \"%1\" is activated by operation \"%2\" "
|
|
"which does not exist.", class_name,
|
|
activation_op);
|
|
return 1;
|
|
}
|
|
|
|
// FIXME This is the same code as EntityRuleHandler
|
|
if (factory->m_scriptFactory == 0 ||
|
|
factory->m_scriptFactory->package() != script_package) {
|
|
PythonScriptFactory<Task> * ptsf =
|
|
new PythonScriptFactory<Task>(script_package, script_class);
|
|
|
|
if (ptsf->setup() == 0) {
|
|
delete factory->m_scriptFactory;
|
|
factory->m_scriptFactory = ptsf;
|
|
} else {
|
|
log(ERROR, compose("Python class \"%1.%2\" failed to load",
|
|
script_package, script_class));
|
|
delete ptsf;
|
|
return -1;
|
|
}
|
|
} else {
|
|
// FIXME If this fails, that's bad.
|
|
// If this fails the user needs to know, but the old values
|
|
// should remain in place.
|
|
factory->m_scriptFactory->refreshClass();
|
|
}
|
|
|
|
// FIXME This does not check for or remove old activations for this
|
|
// factory
|
|
m_builder->addTaskActivation(activation_tool, activation_op, factory);
|
|
|
|
MapType::iterator L = tool_factory->m_classAttributes.find("operations");
|
|
if (L == tool_factory->m_classAttributes.end()) {
|
|
tool_factory->m_classAttributes["operations"] = ListType(1, activation_op);
|
|
tool_factory->m_attributes["operations"] = ListType(1, activation_op);
|
|
tool_factory->updateProperties();
|
|
} else {
|
|
if (L->second.isList()) {
|
|
ListType::const_iterator M = L->second.List().begin();
|
|
for (; M != L->second.List().end() && *M != activation_op; ++M);
|
|
if (M == L->second.List().end()) {
|
|
L->second.List().push_back(activation_op);
|
|
tool_factory->m_attributes[L->first] = L->second.List();
|
|
tool_factory->updateProperties();
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TaskRuleHandler::installTaskClass(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());
|
|
|
|
if (m_builder->hasTask(class_name)) {
|
|
log(ERROR, compose("Attempt to install task \"%1\" which is already "
|
|
"installed.", class_name));
|
|
return -1;
|
|
}
|
|
|
|
TaskKit * factory = new TaskFactory(class_name);
|
|
|
|
int ret = populateTaskFactory(class_name, factory, class_desc,
|
|
dependent, reason);
|
|
if (ret != 0) {
|
|
delete factory;
|
|
return ret;
|
|
}
|
|
m_builder->installTaskFactory(class_name, factory);
|
|
|
|
Inheritance & i = Inheritance::instance();
|
|
|
|
i.addChild(class_desc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TaskRuleHandler::modifyTaskClass(const std::string & class_name,
|
|
const Root & class_desc)
|
|
{
|
|
assert(class_name == class_desc->getId());
|
|
|
|
TaskKit * factory = m_builder->getTaskFactory(class_name);
|
|
if (factory == 0) {
|
|
log(ERROR, compose("Could not find factory for existing task class "
|
|
"\"%1\"", class_name));
|
|
return -1;
|
|
}
|
|
|
|
std::string dependent, reason;
|
|
int ret = populateTaskFactory(class_name, factory, class_desc,
|
|
dependent, reason);
|
|
if (ret != 0) {
|
|
if (ret > 0) {
|
|
log(ERROR, reason);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TaskRuleHandler::check(const Atlas::Objects::Root & desc)
|
|
{
|
|
assert(!desc->getParents().empty());
|
|
return m_builder->isTask(desc->getParents().front()) ? 0 : -1;
|
|
}
|
|
|
|
int TaskRuleHandler::install(const std::string & name,
|
|
const std::string & parent,
|
|
const Atlas::Objects::Root & description,
|
|
std::string & dependent,
|
|
std::string & reason)
|
|
{
|
|
return installTaskClass(name, parent, description, dependent, reason);
|
|
}
|
|
|
|
int TaskRuleHandler::update(const std::string & name,
|
|
const Atlas::Objects::Root & desc)
|
|
{
|
|
return modifyTaskClass(name, desc);
|
|
}
|