diff --git a/aiclient/CMakeLists.txt b/aiclient/CMakeLists.txt index 75d62a8ea..dd0fa6365 100644 --- a/aiclient/CMakeLists.txt +++ b/aiclient/CMakeLists.txt @@ -5,7 +5,7 @@ add_executable(cyaiclient PossessionClient.cpp aiclient.cpp PossessionAccount.cpp - MindRegistry.h PendingMind.cpp PendingMind.h) + MindRegistry.h) target_link_libraries(cyaiclient diff --git a/aiclient/PendingMind.cpp b/aiclient/PendingMind.cpp deleted file mode 100644 index a870d6204..000000000 --- a/aiclient/PendingMind.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include - -#include - -/* - Copyright (C) 2018 Erik Ogenvik - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "PendingMind.h" -#include "MindRegistry.h" -#include "common/log.h" -#include "common/Inheritance.h" -#include "common/debug.h" -#include "common/id.h" -#include "common/ScriptKit.h" -#include "common/Setup.h" - -#include "rulesets/MindFactory.h" -#include "rulesets/BaseMind.h" -#include -#include - -static const bool debug_flag = false; - -using Atlas::Message::Element; -using Atlas::Message::MapType; -using Atlas::Objects::Root; -using Atlas::Objects::Operation::RootOperation; -using Atlas::Objects::Operation::Look; -using Atlas::Objects::Entity::RootEntity; -using Atlas::Objects::Entity::Anonymous; - -PendingMind::PendingMind(std::string entityId, std::string mindId, MindRegistry& locatedEntityRegistry, const MindKit& mindFactory) - : m_entityId(std::move(entityId)), - m_mindId(std::move(mindId)), - m_mindRegistry(locatedEntityRegistry), - m_mindFactory(mindFactory), - m_serialNoCounter(0), - m_typeResolver(new TypeResolver()) -{ - m_typeResolver->m_typeProviderId = m_mindId; -} - -void PendingMind::init(OpVector& res) -{ - Look look; - Root lookArg; - lookArg->setId(m_entityId); - look->setArgs1(lookArg); - look->setFrom(m_mindId); - look->setSerialno(++m_serialNoCounter); - res.push_back(look); -} - -void PendingMind::operation(const Operation& op, OpVector& res) -{ - if (op->getClassNo() == Atlas::Objects::Operation::INFO_NO) { - auto resolvedTypes = m_typeResolver->InfoOperation(op, res); - for (auto& type : resolvedTypes) { - if (type->name() == m_typeName) { - //Got the type of the entity, we can continue with creation - createEntity(res, type); - } - } - } else { - if (op->isDefaultRefno() || op->getRefno() != m_serialNoCounter) { - m_pendingOperations.emplace_back(op); - } else { - m_pendingOperations.push_back(op); - if (op->getClassNo() != Atlas::Objects::Operation::SIGHT_NO || op->getArgs().empty()) { - log(ERROR, "Malformed initial sight when setting up mind."); - return; - } - - auto ent = Atlas::Objects::smart_dynamic_cast(op->getArgs().front()); - - if (!ent || ent->isDefaultParent()) { - log(ERROR, "Malformed initial sight when setting up mind."); - return; - } - - m_typeName = ent->getParent(); - - auto typeNode = Inheritance::instance().getType(m_typeName); - - if (!typeNode) { - m_typeResolver->requestType(m_typeName, res); - } else { - createEntity(res, typeNode); - } - } - } -} - -void PendingMind::createEntity(OpVector& res, const TypeNode* type) -{ - debug(std::cout << String::compose("Got info on account, creating mind for entity with id %1 of type %2.", m_entityId, type->name()) << std::endl;); - log(INFO, String::compose("Creating mind for entity with id %1 of type '%2'. Name '%3'. Mind id: %4", m_entityId, type->name(), m_entitySight->getName(), m_mindId)); - Ref mind = m_mindFactory.newMind(m_entityId, integerId(m_entityId)); - mind->setMindId(m_mindId); - mind->setType(type); - //Move the type resolver from us to the mind. - mind->setTypeResolver(std::move(m_typeResolver)); - mind->getMap()->addEntity(mind); - - m_mindRegistry.addLocatedEntity(mind); - - if (m_mindFactory.m_scriptFactory != nullptr) { - debug(std::cout << "Adding script to entity." << std::endl;); - m_mindFactory.m_scriptFactory->addScript(mind.get()); - } - - OpVector mindRes; - - //Send the Sight operation we just got on to the mind, since it contains info about the entity. - mind->operation(m_entitySight, mindRes); - - //Also send a "Setup" op to the mind, which will trigger any setup hooks. - Atlas::Objects::Operation::Setup s; - Anonymous setup_arg; - setup_arg->setName("mind"); - s->setTo(m_mindId); - s->setArgs1(setup_arg); - mind->operation(s, mindRes); - - //Mark all resulting ops as coming from the mind. - for (auto& resOp : mindRes) { - resOp->setFrom(m_mindId); - res.push_back(resOp); - } - - //Start by sending a unspecified "Look". This tells the server to send us a bootstrapped view. - Look l; - l->setFrom(m_mindId); - res.push_back(l); - - if (!m_pendingOperations.empty()) { - log(INFO, String::compose("Sending %1 pending operations to the mind.", m_pendingOperations.size())); - for (const auto& op : m_pendingOperations) { - mind->operation(op, res); - } - m_pendingOperations.clear(); - } - - m_mindRegistry.removePendingMind(m_mindId); -} diff --git a/aiclient/PendingMind.h b/aiclient/PendingMind.h deleted file mode 100644 index afbd33601..000000000 --- a/aiclient/PendingMind.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (C) 2018 Erik Ogenvik - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef CYPHESIS_PENDINGMIND_H -#define CYPHESIS_PENDINGMIND_H - - -#include "common/Router.h" -#include "rulesets/TypeResolver.h" - -#include -#include -class MindRegistry; -class MindKit; - -/** - * This class is used to represent a mind that's in the process of being created, where we have the entity data - * but are missing the type data. Once all type data has been received an actual BaseMind instance will be created - * and this instance destroyed. - */ -class PendingMind -{ - public: - PendingMind(std::string entityId, std::string mindId, MindRegistry& locatedEntityRegistry, const MindKit& mindFactory); - - void operation(const Operation& op, OpVector& res); - - void init(OpVector& res); - - private: - - std::string m_entityId; - std::string m_mindId; - MindRegistry& m_mindRegistry; - const MindKit& m_mindFactory; - - std::string m_typeName; - - Atlas::Objects::Operation::Sight m_entitySight; - - std::vector m_pendingOperations; - - long m_serialNoCounter; - - std::unique_ptr m_typeResolver; - - void createEntity(OpVector& res, const TypeNode* type); -}; - - -#endif //CYPHESIS_PENDINGMIND_H diff --git a/aiclient/PossessionAccount.cpp b/aiclient/PossessionAccount.cpp index b8d99595b..4f20f3e55 100644 --- a/aiclient/PossessionAccount.cpp +++ b/aiclient/PossessionAccount.cpp @@ -51,12 +51,12 @@ using Atlas::Objects::Operation::Login; using Atlas::Objects::Entity::RootEntity; using Atlas::Objects::Entity::Anonymous; -PossessionAccount::PossessionAccount(const std::string& id, long intId, MindRegistry& locatedEntityRegistry, const MindKit& mindFactory) : - Router(id, intId), - mLocatedEntityRegistry(locatedEntityRegistry), - m_mindFactory(mindFactory), - m_serialNoCounter(1) +PossessionAccount::PossessionAccount(const std::string& id, long intId, const MindKit& mindFactory) : + Router(id, intId), + m_mindFactory(mindFactory), + m_serialNoCounter(1) { + assert(m_mindFactory.m_scriptFactory); } @@ -77,50 +77,62 @@ void PossessionAccount::enablePossession(OpVector& res) res.push_back(set); } -void PossessionAccount::operation(const Operation & op, OpVector & res) +void PossessionAccount::operation(const Operation& op, OpVector& res) { if (!op->isDefaultRefno()) { auto I = m_callbacks.find(op->getRefno()); if (I != m_callbacks.end()) { I->second(op, res); m_callbacks.erase(I); - } - } else { - if (op->getClassNo() == Atlas::Objects::Operation::POSSESS_NO) { - PossessOperation(op, res); - } else if (op->getClassNo() == Atlas::Objects::Operation::APPEARANCE_NO) { - //Ignore appearance ops, since they just signal other accounts being connected - } else if (op->getClassNo() == Atlas::Objects::Operation::DISAPPEARANCE_NO) { - //Ignore disappearance ops, since they just signal other accounts being disconnected - } else if (op->getClassNo() == Atlas::Objects::Operation::INFO_NO) { - //Ignore info ops, since they just signal other accounts doing things + return; } else { - log(NOTICE, String::compose("Unknown operation %1 in PossessionAccount", op->getParent())); + log(WARNING, String::compose("Got %1 op with refno %2, but there's no callback registered for that refno.", op->getParent(), op->getRefno())); } } + if (!op->isDefaultTo()) { + auto I = m_minds.find(op->getTo()); + if (I != m_minds.end()) { + I->second->operation(op, res); + return; + } - if (!op->isDefaultRefno() && m_possessionRefNumbers.find(op->getRefno()) != m_possessionRefNumbers.end()) { - m_possessionRefNumbers.erase(op->getRefno()); - createMind(op, res); + I = m_entitiesWithMinds.find(op->getTo()); + if (I != m_entitiesWithMinds.end()) { + I->second->operation(op, res); + return; + } + + } + + if (op->getClassNo() == Atlas::Objects::Operation::POSSESS_NO) { + PossessOperation(op, res); + } else if (op->getClassNo() == Atlas::Objects::Operation::APPEARANCE_NO) { + //Ignore appearance ops, since they just signal other accounts being connected + } else if (op->getClassNo() == Atlas::Objects::Operation::DISAPPEARANCE_NO) { + //Ignore disappearance ops, since they just signal other accounts being disconnected + } else if (op->getClassNo() == Atlas::Objects::Operation::INFO_NO) { + //Send info ops on to all minds + for (auto& entry : m_minds) { + entry.second->operation(op, res); + } } else { - - + log(NOTICE, String::compose("Unknown operation %1 in PossessionAccount", op->getParent())); } } -void PossessionAccount::externalOperation(const Operation & op, Link &) +void PossessionAccount::externalOperation(const Operation& op, Link&) { } -void PossessionAccount::PossessOperation(const Operation& op, OpVector & res) +void PossessionAccount::PossessOperation(const Operation& op, OpVector& res) { debug(std::cout << "Got possession request." << std::endl;); auto args = op->getArgs(); if (!args.empty()) { - const Root & arg = args.front(); + const Root& arg = args.front(); Element possessKeyElement; if (arg->copyAttr("possess_key", possessKeyElement) == 0 && possessKeyElement.isString()) { @@ -149,9 +161,6 @@ void PossessionAccount::takePossession(OpVector& res, const std::string& possess possess->setSerialno(m_serialNoCounter++); m_callbacks[possess->getSerialno()] = [this](const Operation& op, OpVector& res) { -// std::cout << "PossessionAccount::createMind {" << std::endl; -// debug_dump(op, std::cout); -// std::cout << "}" << std::endl << std::flush; if (op->getClassNo() != Atlas::Objects::Operation::INFO_NO) { log(ERROR, "Malformed possession response: not an info."); @@ -187,92 +196,48 @@ void PossessionAccount::takePossession(OpVector& res, const std::string& possess auto entityId = I->second.String(); - mLocatedEntityRegistry.addPendingMind(entityId, ent->getId(), res); - -// //Create an entity, but do not "start" it yet. We need to fetch the type first. -// auto mind = m_mindFactory.newMind(entityId, std::stol(entityId)); -// mind->setMindId(ent->getId()); -// -// mLocatedEntityRegistry.addLocatedEntity(mind); -// -// mind->setup(res); + createMindInstance(res, ent->getId(), entityId); }; res.push_back(possess); } -void PossessionAccount::createMind(const Operation & op, OpVector & res) +void PossessionAccount::createMindInstance(OpVector& res, const std::string& mindId, const std::string& entityId) { + log(INFO, String::compose("Creating mind instance for entity id %1 with mind id %2.", entityId, mindId)); + Ref mind = m_mindFactory.newMind(mindId, entityId); + m_minds.emplace(mindId, mind); + m_entitiesWithMinds.emplace(entityId, mind); - std::cout << "PossessionAccount::createMind {" << std::endl; - debug_dump(op, std::cout); - std::cout << "}" << std::endl << std::flush; - - const std::vector& args = op->getArgs(); - if (args.empty()) { - log(ERROR, "no args character create/take response"); - return; - } - - RootEntity ent = Atlas::Objects::smart_dynamic_cast(args.front()); - if (!ent.isValid()) { - log(ERROR, "malformed character create/take response"); - return; - } - - if (ent->isDefaultParent()) { - log(ERROR, "malformed character create/take response"); - return; - } - - - - std::string entityId = ent->getId(); - std::string entityType = ent->getParent(); - - auto type = Inheritance::instance().getType(entityType); - if (!type) { - - } else { - createMindInstance(op, res, entityId, type, ent); - } + m_mindFactory.m_scriptFactory->addScript(mind.get()); -} - -void PossessionAccount::createMindInstance(const Operation & op, OpVector & res, const std::string& entityId, const TypeNode* type, const RootEntity& ent) { - debug(std::cout << String::compose("Got info on account, creating mind for entity with id %1 of type %2.", entityId, type->name()) << std::endl;); - log(INFO, String::compose("Creating mind for entity with id %1 of type '%2'. Name '%3'.", entityId, type->name(), ent->getName())); - BaseMind* mind = m_mindFactory.newMind(entityId, integerId(entityId)); - mLocatedEntityRegistry.addLocatedEntity(mind); - mind->setType(type); - - if (m_mindFactory.m_scriptFactory != nullptr) { - debug(std::cout << "Adding script to entity." << std::endl;); - m_mindFactory.m_scriptFactory->addScript(mind); - } - - OpVector mindRes; - - //Send the Sight operation we just got on to the mind, since it contains info about the entity. - mind->operation(op, mindRes); - - //Also send a "Setup" op to the mind, which will trigger any setup hooks. - Atlas::Objects::Operation::Setup s; - Anonymous setup_arg; - setup_arg->setName("mind"); - s->setTo(ent->getId()); - s->setArgs1(setup_arg); - mind->operation(s, mindRes); - - //Mark all resulting ops as coming from the mind. - for (auto& resOp : mindRes) { - resOp->setFrom(entityId); - res.push_back(resOp); - } - - //Start by sending a unspecified "Look". This tells the server to send us a bootstrapped view. - Look l; - l->setFrom(entityId); - res.push_back(l); + mind->init(res); +// +// +// +// +// OpVector mindRes; +// +// //Send the Sight operation we just got on to the mind, since it contains info about the entity. +// mind->operation(op, mindRes); +// +// //Also send a "Setup" op to the mind, which will trigger any setup hooks. +// Atlas::Objects::Operation::Setup s; +// Anonymous setup_arg; +// setup_arg->setName("mind"); +// s->setTo(ent->getId()); +// s->setArgs1(setup_arg); +// mind->operation(s, mindRes); +// +// //Mark all resulting ops as coming from the mind. +// for (auto& resOp : mindRes) { +// resOp->setFrom(entityId); +// res.push_back(resOp); +// } +// +// //Start by sending a unspecified "Look". This tells the server to send us a bootstrapped view. +// Look l; +// l->setFrom(entityId); +// res.push_back(l); } diff --git a/aiclient/PossessionAccount.h b/aiclient/PossessionAccount.h index fcf0f08ed..36683ec60 100644 --- a/aiclient/PossessionAccount.h +++ b/aiclient/PossessionAccount.h @@ -19,19 +19,24 @@ #define AICLIENT_POSSESSIONACCOUNT_H_ #include "common/Router.h" +#include "rulesets/BaseMind.h" #include #include #include +#include class MindKit; + class MindRegistry; + class TypeNode; -class PossessionAccount: public Router +class PossessionAccount : public Router { public: - PossessionAccount(const std::string& id, long intId, MindRegistry& locatedEntityRegistry, const MindKit& mindFactory); + PossessionAccount(const std::string& id, long intId, const MindKit& mindFactory); + ~PossessionAccount() override = default; /** @@ -39,25 +44,34 @@ class PossessionAccount: public Router */ void enablePossession(OpVector& res); - void operation(const Operation & op, OpVector & res) override; - void externalOperation(const Operation & op, Link &) override; + void operation(const Operation& op, OpVector& res) override; + + void externalOperation(const Operation& op, Link&) override; + + const std::unordered_map>& getMinds() const { + return m_minds; + }; protected: - MindRegistry& mLocatedEntityRegistry; + std::unordered_map> m_minds; + std::unordered_map> m_entitiesWithMinds; + const MindKit& m_mindFactory; int m_serialNoCounter; std::unordered_set m_possessionRefNumbers; - std::map> m_callbacks; + std::map> m_callbacks; - void PossessOperation(const Operation & op, OpVector & res); + void PossessOperation(const Operation& op, OpVector& res); void takePossession(OpVector& res, const std::string& possessEntityId, const std::string& possessKey); - void createMind(const Operation & op, OpVector & res); - void createMindInstance(const Operation & op, OpVector & res, const std::string& entityId, const TypeNode* type, const Atlas::Objects::Entity::RootEntity& ent) ; - void registerOpCallback(std::function callback); + void createMind(const Operation& op, OpVector& res); + + void createMindInstance(OpVector& res, const std::string& mindId, const std::string& entityId); + + void registerOpCallback(std::function callback); }; diff --git a/aiclient/PossessionClient.cpp b/aiclient/PossessionClient.cpp index f305695df..051302342 100644 --- a/aiclient/PossessionClient.cpp +++ b/aiclient/PossessionClient.cpp @@ -69,20 +69,10 @@ void PossessionClient::markQueueAsClean() m_operationsDispatcher.markQueueAsClean(); } -void PossessionClient::addLocatedEntity(Ref entity) -{ - m_minds.insert(std::make_pair(entity->getMindId(), std::move(entity))); -} - -void PossessionClient::removeLocatedEntity(Ref entity) -{ - m_minds.erase(entity->getMindId()); -} - void PossessionClient::createAccount(const std::string& accountId) { log(INFO, "Creating possession account on server."); - m_account = new PossessionAccount(accountId, integerId(accountId), *this, m_mindFactory); + m_account = new PossessionAccount(accountId, integerId(accountId), m_mindFactory); OpVector res; m_account->enablePossession(res); for (auto& op : res) { @@ -105,9 +95,6 @@ void PossessionClient::operationFromEntity(const Operation& op, Ref lo send(resOp); } } - if (locatedEntity->isDestroyed()) { - removeLocatedEntity(std::move(locatedEntity)); - } } } @@ -119,54 +106,57 @@ void PossessionClient::operation(const Operation& op, OpVector& res) std::cout << "}" << std::endl << std::flush; } - if (op->isDefaultTo() || op->getTo() == m_account->getId()) { - OpVector accountRes; - m_account->operation(op, accountRes); - for (auto& resOp : accountRes) { - //All resulting ops should go out to the server, except for Ticks which we'll keep ourselves. - if (resOp->getClassNo() == Atlas::Objects::Operation::TICK_NO) { - auto I = m_minds.find(resOp->getFrom()); - if (I != m_minds.end()) { - resOp->setTo(resOp->getFrom()); - m_operationsDispatcher.addOperationToQueue(resOp, I->second); - } - } else { - res.push_back(resOp); + OpVector accountRes; + m_account->operation(op, accountRes); + for (auto& resOp : accountRes) { + //Any op with both "from" and "to" set should be re-sent. + if ((!resOp->isDefaultTo() && !resOp->isDefaultFrom())) { + auto& minds = getMinds(); + auto I = minds.find(resOp->getTo()); + if (I != minds.end()) { + m_operationsDispatcher.addOperationToQueue(resOp, I->second); } - } - } else { - auto mindI = m_minds.find(op->getTo()); - if (mindI != m_minds.end()) { - OpVector mindRes; - auto mind = mindI->second; - mind->operation(op, mindRes); - for (auto& resOp : mindRes) { - resOp->setFrom(mind->getId()); - //All resulting ops should go out to the server, except for Ticks which we'll keep ourselves. - if (resOp->getClassNo() == Atlas::Objects::Operation::TICK_NO) { - resOp->setTo(mind->getId()); - m_operationsDispatcher.addOperationToQueue(resOp, mind); - } else { - res.push_back(resOp); - } - } - - if (mind->isDestroyed()) { - removeLocatedEntity(mind); - } - } else { - auto pendingMindI = m_pendingMinds.find(op->getTo()); - if (pendingMindI != m_pendingMinds.end()) { - pendingMindI->second.operation(op, res); - } else if (op->getTo() == m_account->getId()){ - m_account->operation(op, res); - } else { - log(WARNING, String::compose("Got %1 operation with unknown 'to' (%2).", op->getParent(), op->getTo())); - } + res.push_back(resOp); } } +// +// +// if (op->isDefaultTo() || op->getTo() == m_account->getId()) { +// } else { +// auto mindI = m_minds.find(op->getTo()); +// if (mindI != m_minds.end()) { +// OpVector mindRes; +// auto mind = mindI->second; +// mind->operation(op, mindRes); +// for (auto& resOp : mindRes) { +// resOp->setFrom(mind->getId()); +// //All resulting ops should go out to the server, except for Ticks which we'll keep ourselves. +// if (resOp->getClassNo() == Atlas::Objects::Operation::TICK_NO) { +// resOp->setTo(mind->getId()); +// m_operationsDispatcher.addOperationToQueue(resOp, mind); +// } else { +// res.push_back(resOp); +// } +// } +// +// if (mind->isDestroyed()) { +// removeLocatedEntity(mind); +// } +// +// } else { +// auto pendingMindI = m_pendingMinds.find(op->getTo()); +// if (pendingMindI != m_pendingMinds.end()) { +// pendingMindI->second.operation(op, res); +// } else if (op->getTo() == m_account->getId()){ +// m_account->operation(op, res); +// } else { +// log(WARNING, String::compose("Got %1 operation with unknown 'to' (%2).", op->getParent(), op->getTo())); +// } +// } +// } + if (debug_flag) { for (auto resOp : res) { std::cout << "PossessionClient::operation sent {" << std::endl; @@ -184,14 +174,7 @@ double PossessionClient::getTime() const return (double) (time.seconds()) + (double) time.microseconds() / 1000000.; } -void PossessionClient::addPendingMind(std::string entityId, std::string mindId, OpVector& res) -{ - auto result = m_pendingMinds.emplace(entityId, PendingMind(entityId, mindId, *this, m_mindFactory)); - result.first->second.init(res); -} - -void PossessionClient::removePendingMind(std::string mindId) -{ - m_pendingMinds.erase(mindId); -} +const std::unordered_map>& PossessionClient::getMinds() const { + return m_account->getMinds(); +}; diff --git a/aiclient/PossessionClient.h b/aiclient/PossessionClient.h index a319b7837..c44e3c109 100644 --- a/aiclient/PossessionClient.h +++ b/aiclient/PossessionClient.h @@ -21,7 +21,6 @@ #include "BaseClient.h" #include "MindRegistry.h" -#include "PendingMind.h" #include "rulesets/BaseMind.h" #include "common/OperationsDispatcher.h" #include "common/OperationsDispatcher_impl.h" @@ -35,7 +34,7 @@ class Inheritance; /** * Manages possession requests from the server and spawns new AI clients. */ -class PossessionClient: public BaseClient, public MindRegistry +class PossessionClient: public BaseClient { public: explicit PossessionClient(MindKit& mindFactory); @@ -49,17 +48,7 @@ class PossessionClient: public BaseClient, public MindRegistry void createAccount(const std::string& accountId); - void addLocatedEntity(Ref mind) override; - - void removeLocatedEntity(Ref mind) override; - - void addPendingMind(std::string entityId, std::string mindId, OpVector& res) override; - - void removePendingMind(std::string mindId) override; - - const std::unordered_map>& getMinds() const { - return m_minds; - }; + const std::unordered_map>& getMinds() const; protected: @@ -74,10 +63,6 @@ class PossessionClient: public BaseClient, public MindRegistry OperationsDispatcher m_operationsDispatcher; - std::unordered_map> m_minds; - - std::unordered_map m_pendingMinds; - std::unique_ptr m_inheritance; diff --git a/client/CharacterClient.h b/client/CharacterClient.h index 449578c10..b0209fef2 100644 --- a/client/CharacterClient.h +++ b/client/CharacterClient.h @@ -24,7 +24,7 @@ class ClientConnection; /// \brief Class to implement a character entity in an admin client -class CharacterClient : public BaseMind { +class CharacterClient : public Router { protected: ClientConnection & m_connection; diff --git a/rulesets/BaseMind.cpp b/rulesets/BaseMind.cpp index bce87a2bc..98a76768d 100644 --- a/rulesets/BaseMind.cpp +++ b/rulesets/BaseMind.cpp @@ -1,3 +1,5 @@ +#include + // Cyphesis Online RPG Server and AI Engine // Copyright (C) 2000,2001 Alistair Riddoch // @@ -26,6 +28,7 @@ #include "common/op_switch.h" #include "common/TypeNode.h" #include "common/Inheritance.h" +#include "common/Setup.h" #include #include @@ -41,31 +44,31 @@ using Atlas::Objects::smart_dynamic_cast; static const bool debug_flag = false; -/// \brief BaseMind constructor -/// -/// @param id String identifier -/// @param intId Integer identifier -/// @param body_name The name attribute of the body this mind controls -BaseMind::BaseMind(const std::string& id, long intId) : - MemEntity(id, intId), +BaseMind::BaseMind(const std::string& mindId, const std::string& entityId) : + Router(mindId, std::stol(mindId)), + m_refCount(0), + m_entityId(entityId), + m_flags(0), m_map(), + m_typeResolver(new TypeResolver()), m_serialNoCounter(0) { - setVisible(true); -// m_map.addEntity(this); } -BaseMind::~BaseMind() +BaseMind::~BaseMind() = default; + + +void BaseMind::init(OpVector& res) { - m_map.m_entities.erase(getIntId()); - // FIXME Remove this once MemMap uses parent refcounting - m_location.m_parent = nullptr; - // debug(std::cout << getId() << ":" << getType() << " flushing mind with " - // << m_map.getEntities().size() << " entities in it" - // << std::endl << std::flush;); - m_map.flush(); + Look look; + Root lookArg; + lookArg->setId(m_entityId); + look->setArgs1(lookArg); + look->setFrom(getId()); + res.push_back(look); } + /// \brief Process the Sight of a Create operation. /// /// @param op The Create operation to be processed. @@ -168,7 +171,7 @@ void BaseMind::SoundOperation(const Operation& op, OpVector& res) std::string event_name("sound_"); event_name += op2->getParent(); - if (m_scripts.empty() || m_scripts.front()->operation(event_name, op2, res) != OPERATION_BLOCKED) { + if (m_script || m_script->operation(event_name, op2, res) != OPERATION_BLOCKED) { callSoundOperation(op2, res); } } @@ -198,7 +201,7 @@ void BaseMind::SightOperation(const Operation& op, OpVector& res) log(WARNING, String::compose("Sight op argument ('%1') had no seconds set.", op2->getParent())); } - if (m_scripts.empty() || m_scripts.front()->operation(event_name, op2, res) != OPERATION_BLOCKED) { + if (m_script || m_script->operation(event_name, op2, res) != OPERATION_BLOCKED) { callSightOperation(op2, res); } } else /* if (op2->getObjtype() == "object") */ { @@ -233,25 +236,8 @@ void BaseMind::ThinkOperation(const Operation& op, OpVector& res) OpVector mres; - if (m_scripts.empty() || m_scripts.front()->operation(event_name, op2, mres) != OPERATION_BLOCKED) { - int op2ClassNo = op2->getClassNo(); - switch (op2ClassNo) { - case Atlas::Objects::Operation::SET_NO: - thinkSetOperation(op2, mres); - break; - case Atlas::Objects::Operation::DELETE_NO: - thinkDeleteOperation(op2, mres); - break; - case Atlas::Objects::Operation::GET_NO: - thinkGetOperation(op2, mres); - break; - case Atlas::Objects::Operation::LOOK_NO: - thinkLookOperation(op2, mres); - break; - default: - log(WARNING, "Got invalid Think operation. We only support 'Set', 'Delete', 'Get' and 'Look'."); - break; - } + if (m_script) { + m_script->operation(event_name, op2, mres); } if (!mres.empty() && !op->isDefaultSerialno()) { @@ -264,22 +250,6 @@ void BaseMind::ThinkOperation(const Operation& op, OpVector& res) } } -void BaseMind::thinkSetOperation(const Operation& op, OpVector& res) -{ -} - -void BaseMind::thinkDeleteOperation(const Operation& op, OpVector& res) -{ -} - -void BaseMind::thinkGetOperation(const Operation& op, OpVector& res) -{ -} - -void BaseMind::thinkLookOperation(const Operation& op, OpVector& res) -{ -} - void BaseMind::AppearanceOperation(const Operation& op, OpVector& res) { if (!isAwake()) { return; } @@ -341,6 +311,28 @@ void BaseMind::UnseenOperation(const Operation& op, OpVector& res) m_map.del(arg->getId()); } +void BaseMind::setOwnEntity(OpVector& res, Ref ownEntity) +{ + m_ownEntity = std::move(ownEntity); + //Also send a "Setup" op to the mind, which will trigger any setup hooks. + Atlas::Objects::Operation::Setup s; + Anonymous setup_arg; + setup_arg->setName("mind"); + s->setTo(getId()); + s->setFrom(getId()); + s->setArgs1(setup_arg); + operation(s, res); + + //Start by sending a unspecified "Look". This tells the server to send us a bootstrapped view. + Look l; + l->setFrom(getId()); + res.push_back(l); + + res.insert(std::end(res), std::begin(m_pendingOperations), std::end(m_pendingOperations)); + m_pendingOperations.clear(); +} + + void BaseMind::InfoOperation(const Operation& op, OpVector& res) { if (m_typeResolver) { @@ -351,11 +343,18 @@ void BaseMind::InfoOperation(const Operation& op, OpVector& res) if (I != m_map.m_unresolvedEntities.end()) { for (auto& entity : I->second) { entity->setType(type); + auto J = m_pendingEntitiesOperations.find(entity->getId()); if (J != m_pendingEntitiesOperations.end()) { res.insert(std::end(res), std::begin(J->second), std::end(J->second)); } m_pendingEntitiesOperations.erase(J); + + //If we have resolved our own entity we should do some house keeping + if (entity->getId() == m_entityId) { + + setOwnEntity(res, entity); + } } } m_map.m_unresolvedEntities.erase(I); @@ -364,65 +363,12 @@ void BaseMind::InfoOperation(const Operation& op, OpVector& res) } -void BaseMind::setMindId(const std::string& mindId) -{ - m_mindId = mindId; -} - -const std::string& BaseMind::getMindId() const -{ - return m_mindId; -} void BaseMind::setTypeResolver(std::unique_ptr typeResolver) { m_typeResolver = std::move(typeResolver); } -// -//void BaseMind::setup(OpVector& res) -//{ -// Look look; -// Root lookArg; -// lookArg->setId(getId()); -// look->setArgs1(lookArg); -// look->setFrom(getMindId()); -// look->setSerialno(m_serialNoCounter++); -// -// m_callbacks[look->getSerialno()] = [this](const Operation& op, OpVector& res){ -// if (op->getClassNo() != Atlas::Objects::Operation::SIGHT_NO || op->getArgs().empty()) { -// log(ERROR, "Malformed initial sight when setting up mind."); -// return; -// } -// -// auto ent = Atlas::Objects::smart_dynamic_cast(op->getArgs().front()); -// -// if (!ent || ent->isDefaultParent()) { -// log(ERROR, "Malformed initial sight when setting up mind."); -// return; -// } -// -// auto parent = ent->getParent(); -// -// auto typeNode = Inheritance::instance().getType(parent); -// -// if (!typeNode) { -// m_typeResolver->requestType(parent, res); -// } else { -// -// } -// -// -// -// m_typeResolver-> -// -// -// }; -// -// res.push_back(look); -// -//} - void BaseMind::operation(const Operation& op, OpVector& res) { // This might end up being quite tricky to do @@ -452,6 +398,19 @@ void BaseMind::operation(const Operation& op, OpVector& res) InfoOperation(op, res); } else { + //If we haven't yet resolved our own entity we should delay delivery of the ops + if (!m_ownEntity) { + if (!op->isDefaultFrom()) { + if (op->getFrom() != m_entityId) { + m_pendingOperations.push_back(op); + return; + } + } else { + log(WARNING, String::compose("Got %1 operation without any 'from'.", op->getParent())); + } + } + + m_map.check(op->getSeconds()); //Unless it's an Unseen op, we should add the entity the op was from. @@ -463,9 +422,9 @@ void BaseMind::operation(const Operation& op, OpVector& res) } } m_map.sendLooks(res); - if (!m_scripts.empty()) { - m_scripts.front()->operation("call_triggers", op, res); - if (m_scripts.front()->operation(op->getParent(), op, res) == OPERATION_BLOCKED) { + if (m_script) { + m_script->operation("call_triggers", op, res); + if (m_script->operation(op->getParent(), op, res) == OPERATION_BLOCKED) { return; } } @@ -493,6 +452,13 @@ void BaseMind::operation(const Operation& op, OpVector& res) } } } + + for (auto& resOp : res) { + if (resOp->isDefaultFrom()) { + resOp->setFrom(getId()); + } + } + if (debug_flag) { for (const auto& resOp : res) { std::cout << "BaseMind::operation sent {" << std::endl; @@ -502,6 +468,12 @@ void BaseMind::operation(const Operation& op, OpVector& res) } } +void BaseMind::externalOperation(const Operation& op, Link&) +{ + +} + + void BaseMind::callSightOperation(const Operation& op, OpVector& res) { @@ -544,8 +516,6 @@ void BaseMind::callSoundOperation(const Operation& op, void BaseMind::setScript(Script* scrpt) { - LocatedEntity::setScript(scrpt); - if (m_scripts.size() == 1) { - m_map.setScript(m_scripts.front()); - } + m_script.reset(scrpt); + m_map.setScript(scrpt); } diff --git a/rulesets/BaseMind.h b/rulesets/BaseMind.h index fd93c5528..3cdfede8c 100644 --- a/rulesets/BaseMind.h +++ b/rulesets/BaseMind.h @@ -30,8 +30,15 @@ /// handles most of the AI. The main purpose of this class is to handle /// operations and interface to the MemMap used as the core of /// the entity's memory. -class BaseMind : public MemEntity { +class BaseMind : public Router { protected: + + int m_refCount; + + std::string m_entityId; + + Flags m_flags; + /// \brief Memory map of world entities this mind knows about MemMap m_map; /// \brief World time as far as this mind is aware @@ -42,17 +49,26 @@ class BaseMind : public MemEntity { std::unique_ptr m_typeResolver; - std::string m_mindId; std::map> m_callbacks; long m_serialNoCounter; + std::unique_ptr