cyphesis/aiclient/PossessionClient.cpp
Erik Ogenvik 514d0812a0 Added basic awareness for navigation.
"Awareness" refers to an entity's view of the surrounding world with
regards to how the entity could navigate. We use Recast/Detour to build
a navmesh for the entity.

However, since each world can have many NPCs, each one with their own
mind, we don't want to keep a complete navmesh for each single mind.
This will quickly become expensive. Therefore we want to share
awarenesses between multiple entities of the same type. So, for example,
all fishes handled by the same ai client will share one single awareness
of the world.
This to some degree involves some cheating, since AI minds now might get
knowledge of the world they otherwise wouldn't have. We consider this to
not be such a big issue. If this is unacceptable for some worlds these
should then disable awareness sharing and take the resource hit.
2016-03-19 23:05:13 +01:00

179 lines
5.4 KiB
C++

/*
Copyright (C) 2013 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "PossessionClient.h"
#include "PossessionAccount.h"
#include "rulesets/BaseMind.h"
#include "common/Possess.h"
#include "common/log.h"
#include "common/compose.hpp"
#include "common/sockets.h"
#include "common/id.h"
#include "common/custom.h"
#include "common/SystemTime.h"
#include "common/Inheritance.h"
#include "common/debug.h"
#include <Atlas/Objects/Operation.h>
#include <Atlas/Objects/Entity.h>
static const bool debug_flag = false;
using Atlas::Message::Element;
using Atlas::Objects::Root;
using Atlas::Objects::Entity::Anonymous;
using Atlas::Objects::Operation::RootOperation;
PossessionClient::PossessionClient(MindKit& mindFactory) :
m_mindFactory(mindFactory), m_account(nullptr), m_operationsDispatcher([&](const Operation & op, LocatedEntity & from) {this->operationFromEntity(op, from);},
[&]()->double {return getTime();})
{
}
PossessionClient::~PossessionClient()
{
//Clear all rules when we shut down.
Inheritance::instance().clear();
}
bool PossessionClient::idle()
{
return m_operationsDispatcher.idle();
}
double PossessionClient::secondsUntilNextOp() const
{
return m_operationsDispatcher.secondsUntilNextOp();
}
bool PossessionClient::isQueueDirty() const
{
return m_operationsDispatcher.isQueueDirty();
}
void PossessionClient::markQueueAsClean()
{
m_operationsDispatcher.markQueueAsClean();
}
void PossessionClient::addLocatedEntity(LocatedEntity* entity)
{
m_minds.insert(std::make_pair(entity->getIntId(), entity));
entity->incRef();
}
void PossessionClient::removeLocatedEntity(LocatedEntity* entity)
{
m_minds.erase(entity->getIntId());
entity->decRef();
}
void PossessionClient::createAccount(const std::string& accountId)
{
m_account = new PossessionAccount(accountId, integerId(accountId), *this, m_mindFactory);
OpVector res;
m_account->enablePossession(res);
for (auto& op : res) {
m_connection.send(op);
}
}
void PossessionClient::operationFromEntity(const Operation & op, LocatedEntity& locatedEntity)
{
if (!locatedEntity.isDestroyed()) {
OpVector res;
locatedEntity.operation(op, res);
for (auto& resOp : res) {
//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(resOp->getFrom());
m_operationsDispatcher.addOperationToQueue(resOp, locatedEntity);
} else {
resOp->setFrom(locatedEntity.getId());
send(resOp);
}
}
if (locatedEntity.isDestroyed()) {
removeLocatedEntity(&locatedEntity);
}
}
}
void PossessionClient::operation(const Operation & op, OpVector & res)
{
if (debug_flag) {
std::cout << "PossessionClient::operation {" << std::endl;
debug_dump(op, std::cout);
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(integerId(resOp->getFrom()));
if (I != m_minds.end()) {
resOp->setTo(resOp->getFrom());
m_operationsDispatcher.addOperationToQueue(resOp, *I->second);
}
} else {
res.push_back(resOp);
}
}
} else {
auto mindI = m_minds.find(integerId(op->getTo()));
if (mindI != m_minds.end()) {
OpVector mindRes;
LocatedEntity* 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 {
m_account->operation(op, res);
}
}
}
double PossessionClient::getTime() const
{
SystemTime time;
time.update();
return (double)(time.seconds()) + (double)time.microseconds() / 1000000.;
}