#include "cellapp.h"
#include "spacememory.h"
#include "entity.h"
#include "witness.h"
#include "navigation/navigation.h"
#include "loadnavmesh_threadtasks.h"
#include "entitydef/entities.h"
#include "client_lib/client_interface.h"
#include "network/network_stats.h"
#include "../../server/baseappmgr/baseappmgr_interface.h"
#include "../../server/cellappmgr/cellappmgr_interface.h"
#include "../../server/baseapp/baseapp_interface.h"
#include "../../server/cellapp/cellapp_interface.h"
#include "../../server/dbmgr/dbmgr_interface.h"
namespace KBEngine{
SpaceMemory::SpaceMemory(SPACE_ID spaceID, const std::string& scriptModuleName) :
id_(spaceID),
scriptModuleName_(scriptModuleName),
entities_(),
hasGeometry_(false),
pCell_(NULL),
coordinateSystem_(),
pNavHandle_(),
state_(STATE_NORMAL),
destroyTime_(0)
{
Network::Channel* pChannel = Components::getSingleton().getCellappmgrChannel();
if (pChannel != NULL)
{
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(CellappmgrInterface::updateSpaceData);
(*pBundle) << g_componentID;
(*pBundle) << id_;
(*pBundle) << scriptModuleName_;
(*pBundle) << false;
(*pBundle) << "";
pChannel->send(pBundle);
}
}
SpaceMemory::~SpaceMemory()
{
_clearGhosts();
entities_.clear();
this->coordinateSystem_.releaseNodes();
pNavHandle_.clear();
SAFE_RELEASE(pCell_);
Network::Channel* pChannel = Components::getSingleton().getCellappmgrChannel();
if (pChannel != NULL)
{
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(CellappmgrInterface::updateSpaceData);
(*pBundle) << g_componentID;
(*pBundle) << id_;
(*pBundle) << scriptModuleName_;
(*pBundle) << true;
(*pBundle) << "";
pChannel->send(pBundle);
}
}
void SpaceMemory::_clearGhosts()
{
if(entities_.size() == 0)
return;
std::vector<ENTITY_ID> entitieslog;
SPACE_ENTITIES::const_iterator log_iter = this->entities().begin();
for(; log_iter != this->entities().end(); ++log_iter)
entitieslog.push_back((*log_iter).get()->id());
std::vector<ENTITY_ID>::iterator iter = entitieslog.begin();
for(; iter != entitieslog.end(); ++iter)
{
Entity* entity = Cellapp::getSingleton().findEntity((*iter));
if(entity != NULL && !entity->isDestroyed() && entity->spaceID() == this->id())
{
entity->destroyEntity();
}
else
{
entity = findEntity((*iter));
if(entity != NULL && !entity->isDestroyed() && entity->spaceID() == this->id())
{
removeEntity(entity);
}
}
}
entities_.clear();
}
PyObject* SpaceMemory::__py_GetSpaceGeometryMapping(PyObject* self, PyObject* args)
{
SPACE_ID spaceID = 0;
if(PyTuple_Size(args) != 1)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceGeometryMapping: (argssize != 1) error!");
PyErr_PrintEx(0);
return 0;
}
if(!PyArg_ParseTuple(args, "I", &spaceID))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceGeometryMapping: args error!");
PyErr_PrintEx(0);
return 0;
}
SpaceMemory* space = SpaceMemorys::findSpace(spaceID);
if(space == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceGeometryMapping: (spaceID=%u) not found!",
spaceID);
PyErr_PrintEx(0);
return 0;
}
return PyUnicode_FromString(space->getGeometryPath().c_str());
}
PyObject* SpaceMemory::__py_AddSpaceGeometryMapping(PyObject* self, PyObject* args)
{
SPACE_ID spaceID = 0;
char* path = NULL;
bool shouldLoadOnServer = true;
PyObject* mapper = NULL;
PyObject* py_params = NULL;
std::map< int, std::string > params;
Py_ssize_t argCount = PyTuple_Size(args);
if(argCount < 3 || argCount > 5)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: (argssize[spaceID, mapper, path, shouldLoadOnServer] < 3 || > 5) error!");
PyErr_PrintEx(0);
return 0;
}
if(argCount == 4)
{
if(!PyArg_ParseTuple(args, "I|O|s|b", &spaceID, &mapper, &path, &shouldLoadOnServer))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: args error!");
PyErr_PrintEx(0);
return 0;
}
}
else if(argCount == 5)
{
if(!PyArg_ParseTuple(args, "I|O|s|b|O", &spaceID, &mapper, &path, &shouldLoadOnServer, &py_params))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: args error!");
PyErr_PrintEx(0);
return 0;
}
if(py_params)
{
PyObject *key, *value;
Py_ssize_t pos = 0;
if(!PyDict_Check(py_params))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: args(params) not is PyDict!");
PyErr_PrintEx(0);
return 0;
}
while(PyDict_Next(py_params, &pos, &key, &value))
{
if(!PyLong_Check(key) || !PyUnicode_Check(value))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: args(params) error!");
PyErr_PrintEx(0);
return 0;
}
params[PyLong_AsLong(key)] = PyUnicode_AsUTF8AndSize(value, NULL);
}
SCRIPT_ERROR_CHECK();
}
}
else
{
if(!PyArg_ParseTuple(args, "I|O|s", &spaceID, &mapper, &path))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: args wrong!");
PyErr_PrintEx(0);
return 0;
}
}
SpaceMemory* space = SpaceMemorys::findSpace(spaceID);
if(space == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: spaceID error! spaceID=%u respath=%s",
spaceID, path);
PyErr_PrintEx(0);
return 0;
}
SCRIPT_ERROR_CHECK();
if (Resmgr::getSingleton().matchPath(path).size() == 0)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: path error! spaceID=%u respath=%s",
spaceID, path);
PyErr_PrintEx(0);
return 0;
}
if(!space->addSpaceGeometryMapping(path, shouldLoadOnServer, params))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::addSpaceGeometryMapping: (spaceID=%u respath=%s) error!",
spaceID, path);
PyErr_PrintEx(0);
return 0;
}
S_Return;
}
bool SpaceMemory::addSpaceGeometryMapping(std::string respath, bool shouldLoadOnServer, const std::map< int, std::string >& params)
{
INFO_MSG(fmt::format("KBEngine::addSpaceGeometryMapping: spaceID={}, respath={}, shouldLoadOnServer={}!\n",
id(), respath, shouldLoadOnServer));
hasGeometry_ = true;
if(getGeometryPath() == respath)
{
WARNING_MSG(fmt::format("KBEngine::addSpaceGeometryMapping: spaceID={}, respath={} exist!\n",
id(), respath));
return true;
}
setGeometryPath(respath);
if(shouldLoadOnServer)
loadSpaceGeometry(params);
return true;
}
void SpaceMemory::loadSpaceGeometry(const std::map< int, std::string >& params)
{
KBE_ASSERT(pNavHandle_ == NULL);
Cellapp::getSingleton().threadPool().addTask(new LoadNavmeshTask(getGeometryPath(), this->id(), params));
}
void SpaceMemory::unLoadSpaceGeometry()
{
Network::Channel* pChannel = Components::getSingleton().getCellappmgrChannel();
if (pChannel != NULL)
{
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(CellappmgrInterface::updateSpaceData);
(*pBundle) << g_componentID;
(*pBundle) << id_;
(*pBundle) << scriptModuleName_;
(*pBundle) << false;
(*pBundle) << "";
pChannel->send(pBundle);
}
}
void SpaceMemory::onLoadedSpaceGeometryMapping(NavigationHandlePtr pNavHandle)
{
pNavHandle_ = pNavHandle;
INFO_MSG(fmt::format("KBEngine::onLoadedSpaceGeometryMapping: spaceID={}, respath={}!\n",
id(), getGeometryPath()));
{
SCOPED_PROFILE(SCRIPTCALL_PROFILE);
SCRIPT_OBJECT_CALL_ARGS2(Cellapp::getSingleton().getEntryScript().get(), const_cast<char*>("onSpaceGeometryLoaded"),
const_cast<char*>("Is"), this->id(), getGeometryPath().c_str(), false);
}
onAllSpaceGeometryLoaded();
Network::Channel* pChannel = Components::getSingleton().getCellappmgrChannel();
if (pChannel != NULL)
{
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(CellappmgrInterface::updateSpaceData);
(*pBundle) << g_componentID;
(*pBundle) << id_;
(*pBundle) << scriptModuleName_;
(*pBundle) << false;
(*pBundle) << getGeometryPath();
pChannel->send(pBundle);
}
}
void SpaceMemory::onAllSpaceGeometryLoaded()
{
SCOPED_PROFILE(SCRIPTCALL_PROFILE);
SCRIPT_OBJECT_CALL_ARGS3(Cellapp::getSingleton().getEntryScript().get(), const_cast<char*>("onAllSpaceGeometryLoaded"),
const_cast<char*>("Iis"), this->id(), true, getGeometryPath().c_str(), false);
}
bool SpaceMemory::update()
{
if(destroyTime_ > 0 && timestamp() - destroyTime_ >= uint64( 3.f * stampsPerSecond() ))
{
if(entities_.size() == 0)
return false;
}
this->coordinateSystem_.releaseNodes();
if(destroyTime_ > 0 && timestamp() - destroyTime_ >= uint64( 30.f * stampsPerSecond() ))
{
_clearGhosts();
KBE_ASSERT(entities_.size() == 0);
this->coordinateSystem_.releaseNodes();
}
return true;
}
void SpaceMemory::addEntityAndEnterWorld(Entity* pEntity, bool isRestore)
{
addEntity(pEntity);
addEntityToNode(pEntity);
onEnterWorld(pEntity);
}
void SpaceMemory::addEntity(Entity* pEntity)
{
pEntity->spaceID(this->id_);
pEntity->spaceEntityIdx(entities_.size());
entities_.push_back(pEntity);
pEntity->onEnterSpace(this);
}
void SpaceMemory::addEntityToNode(Entity* pEntity)
{
pEntity->installCoordinateNodes(&coordinateSystem_);
}
void SpaceMemory::removeEntity(Entity* pEntity)
{
KBE_ASSERT(pEntity->spaceID() == id());
pEntity->spaceID(0);
SPACE_ENTITIES::size_type idx = pEntity->spaceEntityIdx();
KBE_ASSERT(idx < entities_.size());
KBE_ASSERT(entities_[ idx ] == pEntity);
Entity* pBack = entities_.back().get();
pBack->spaceEntityIdx(idx);
entities_[idx] = pBack;
pEntity->spaceEntityIdx(SPACE_ENTITIES::size_type(-1));
entities_.pop_back();
onLeaveWorld(pEntity);
pEntity->uninstallCoordinateNodes(&coordinateSystem_);
pEntity->onLeaveSpace(this);
if(entities_.empty() && state_ == STATE_NORMAL)
{
SpaceMemorys::destroySpace(this->id(), 0);
}
}
void SpaceMemory::_onEnterWorld(Entity* pEntity)
{
if(!pEntity->isReal() || !pEntity->pScriptModule()->hasClient())
return;
if(pEntity->hasWitness())
{
_addSpaceDatasToEntityClient(pEntity);
pEntity->pWitness()->onEnterSpace(this);
}
}
void SpaceMemory::onEnterWorld(Entity* pEntity)
{
KBE_ASSERT(pEntity != NULL);
if(pEntity->hasWitness())
{
_onEnterWorld(pEntity);
}
}
void SpaceMemory::onEntityAttachWitness(Entity* pEntity)
{
KBE_ASSERT(pEntity != NULL && pEntity->hasWitness());
_onEnterWorld(pEntity);
}
void SpaceMemory::onLeaveWorld(Entity* pEntity)
{
if(!pEntity->isReal() || !pEntity->pScriptModule()->hasClient())
return;
if(pEntity->hasWitness())
{
pEntity->pWitness()->onLeaveSpace(this);
}
}
Entity* SpaceMemory::findEntity(ENTITY_ID entityID)
{
SPACE_ENTITIES::const_iterator iter = this->entities().begin();
for(; iter != this->entities().end(); ++iter)
{
const Entity* entity = (*iter).get();
if(entity->id() == entityID)
return const_cast<Entity*>(entity);
}
return NULL;
}
bool SpaceMemory::destroy(ENTITY_ID entityID, bool ignoreGhost)
{
if(state_ != STATE_NORMAL)
return false;
state_ = STATE_DESTROYING;
destroyTime_ = timestamp();
std::vector<ENTITY_ID> entitieslog;
{
SPACE_ENTITIES::const_iterator iter = this->entities().begin();
for(; iter != this->entities().end(); ++iter)
{
const Entity* entity = (*iter).get();
entitieslog.push_back(entity->id());
}
}
{
std::vector<ENTITY_ID>::iterator iter = entitieslog.begin();
for(; iter != entitieslog.end(); ++iter)
{
Entity* entity = Cellapp::getSingleton().findEntity((*iter));
if(entity != NULL && !entity->isDestroyed() && entity->spaceID() == this->id() && entity->isReal())
{
entity->onSpaceGone();
}
}
}
state_ = STATE_DESTROYED;
if(this->entities().size() == 0)
return true;
std::vector<ENTITY_ID>::iterator iter = entitieslog.begin();
for(; iter != entitieslog.end(); ++iter)
{
Entity* entity = Cellapp::getSingleton().findEntity((*iter));
if(entity != NULL && entity->isReal() && !entity->isDestroyed() && entity->spaceID() == this->id())
{
entity->destroyEntity();
}
else
{
entity = findEntity((*iter));
if(entity != NULL && entity->isReal() && !entity->isDestroyed() && entity->spaceID() == this->id())
{
removeEntity(entity);
}
}
}
if(!ignoreGhost)
_clearGhosts();
return true;
}
void SpaceMemory::setGeometryPath(const std::string& path)
{
return setSpaceData("_mapping", path);
}
const std::string& SpaceMemory::getGeometryPath()
{
return getSpaceData("_mapping");
}
void SpaceMemory::setSpaceData(const std::string& key, const std::string& value)
{
SPACE_DATA::iterator iter = datas_.find(key);
if(iter == datas_.end())
datas_.insert(SPACE_DATA::value_type(key, value));
else
if(iter->second == value)
return;
else
datas_[key] = value;
onSpaceDataChanged(key, value, false);
}
bool SpaceMemory::hasSpaceData(const std::string& key)
{
SPACE_DATA::iterator iter = datas_.find(key);
if(iter == datas_.end())
return false;
return true;
}
const std::string& SpaceMemory::getSpaceData(const std::string& key)
{
SPACE_DATA::iterator iter = datas_.find(key);
if(iter == datas_.end())
{
static const std::string null = "";
return null;
}
return iter->second;
}
void SpaceMemory::delSpaceData(const std::string& key)
{
SPACE_DATA::iterator iter = datas_.find(key);
if(iter == datas_.end())
return;
datas_.erase(iter);
onSpaceDataChanged(key, "", true);
}
void SpaceMemory::onSpaceDataChanged(const std::string& key, const std::string& value, bool isdel)
{
if(!isdel)
{
SCOPED_PROFILE(SCRIPTCALL_PROFILE);
SCRIPT_OBJECT_CALL_ARGS3(Cellapp::getSingleton().getEntryScript().get(), const_cast<char*>("onSpaceData"),
const_cast<char*>("Iss"), this->id(), key.c_str(), value.c_str(), false);
}
else
{
SCOPED_PROFILE(SCRIPTCALL_PROFILE);
SCRIPT_OBJECT_CALL_ARGS3(Cellapp::getSingleton().getEntryScript().get(), const_cast<char*>("onSpaceData"),
const_cast<char*>("IsO"), this->id(), key.c_str(), Py_None, false);
}
SPACE_ENTITIES::const_iterator iter = this->entities().begin();
for(; iter != this->entities().end(); ++iter)
{
const Entity* pEntity = (*iter).get();
if(pEntity == NULL || pEntity->isDestroyed() || !pEntity->hasWitness())
continue;
Network::Bundle* pSendBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
NETWORK_ENTITY_MESSAGE_FORWARD_CLIENT_BEGIN(pEntity->id(), (*pSendBundle));
if(!isdel)
{
ENTITY_MESSAGE_FORWARD_CLIENT_BEGIN(pSendBundle, ClientInterface::setSpaceData, set);
(*pSendBundle) << this->id();
(*pSendBundle) << key;
(*pSendBundle) << value;
ENTITY_MESSAGE_FORWARD_CLIENT_END(pSendBundle, ClientInterface::setSpaceData, set);
}
else
{
ENTITY_MESSAGE_FORWARD_CLIENT_BEGIN(pSendBundle, ClientInterface::delSpaceData, del);
(*pSendBundle) << this->id();
(*pSendBundle) << key;
ENTITY_MESSAGE_FORWARD_CLIENT_END(pSendBundle, ClientInterface::delSpaceData, del);
}
if(!isdel)
pEntity->pWitness()->sendToClient(ClientInterface::setSpaceData, pSendBundle);
else
pEntity->pWitness()->sendToClient(ClientInterface::delSpaceData, pSendBundle);
}
}
void SpaceMemory::_addSpaceDatasToEntityClient(const Entity* pEntity)
{
if(!pEntity)
{
return;
}
if(pEntity->isDestroyed())
{
return;
}
if(!pEntity->hasWitness())
{
WARNING_MSG(fmt::format("Space::_addSpaceDatasToEntityClient: entity {} no client!\n",
pEntity->id()));
return;
}
Network::Bundle* pSendBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
NETWORK_ENTITY_MESSAGE_FORWARD_CLIENT_BEGIN(pEntity->id(), (*pSendBundle));
ENTITY_MESSAGE_FORWARD_CLIENT_BEGIN(pSendBundle, ClientInterface::initSpaceData, init);
(*pSendBundle) << this->id();
SPACE_DATA::iterator iter = datas_.begin();
for(; iter != datas_.end(); ++iter)
{
(*pSendBundle) << iter->first;
(*pSendBundle) << iter->second;
}
ENTITY_MESSAGE_FORWARD_CLIENT_END(pSendBundle, ClientInterface::initSpaceData, init);
pEntity->pWitness()->sendToClient(ClientInterface::initSpaceData, pSendBundle);
}
PyObject* SpaceMemory::__py_SetSpaceData(PyObject* self, PyObject* args)
{
SPACE_ID spaceID = 0;
if(PyTuple_Size(args) != 3)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::setSpaceData: (argssize != (spaceID, key, value)) error!");
PyErr_PrintEx(0);
return 0;
}
char* key = NULL, *value = NULL;
if(!PyArg_ParseTuple(args, "Iss", &spaceID, &key, &value))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::setSpaceData: args error!");
PyErr_PrintEx(0);
return 0;
}
if(key == NULL || value == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::setSpaceData: key or value error, not is string!");
PyErr_PrintEx(0);
return 0;
}
if(strlen(key) == 0)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::setSpaceData: key is empty!");
PyErr_PrintEx(0);
return 0;
}
SpaceMemory* space = SpaceMemorys::findSpace(spaceID);
if(space == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::setSpaceData: (spaceID=%u) not found!",
spaceID);
PyErr_PrintEx(0);
return 0;
}
if(kbe_stricmp(key, "_mapping") == 0)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::setSpaceData: key{_mapping} is protected!",
spaceID);
PyErr_PrintEx(0);
return 0;
}
space->setSpaceData(key, value);
S_Return;
}
PyObject* SpaceMemory::__py_GetSpaceData(PyObject* self, PyObject* args)
{
SPACE_ID spaceID = 0;
if(PyTuple_Size(args) != 2)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceData: (argssize != (spaceID, key)) error!");
PyErr_PrintEx(0);
return 0;
}
char* key = NULL;
if(!PyArg_ParseTuple(args, "Is", &spaceID, &key))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceData: args error!");
PyErr_PrintEx(0);
return 0;
}
if(key == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceData: key not is string!");
PyErr_PrintEx(0);
return 0;
}
if(strlen(key) == 0)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceData: key is empty!");
PyErr_PrintEx(0);
return 0;
}
SpaceMemory* space = SpaceMemorys::findSpace(spaceID);
if(space == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceData: (spaceID=%u) not found!",
spaceID);
PyErr_PrintEx(0);
return 0;
}
if(!space->hasSpaceData(key))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::getSpaceData: (spaceID=%u, key=%s) not found!",
spaceID, key);
PyErr_PrintEx(0);
return 0;
}
return PyUnicode_FromString(space->getSpaceData(key).c_str());
}
PyObject* SpaceMemory::__py_DelSpaceData(PyObject* self, PyObject* args)
{
SPACE_ID spaceID = 0;
if(PyTuple_Size(args) != 2)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: (argssize != (spaceID, key)) error!");
PyErr_PrintEx(0);
return 0;
}
char* key = NULL;
if(!PyArg_ParseTuple(args, "Is", &spaceID, &key))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: args error!");
PyErr_PrintEx(0);
return 0;
}
if(key == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: key not is string!");
PyErr_PrintEx(0);
return 0;
}
if(strlen(key) == 0)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: key is empty!");
PyErr_PrintEx(0);
return 0;
}
SpaceMemory* space = SpaceMemorys::findSpace(spaceID);
if(space == NULL)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: (spaceID=%u) not found!",
spaceID);
PyErr_PrintEx(0);
return 0;
}
if(!space->hasSpaceData(key))
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: (spaceID=%u, key=%s) not found!",
spaceID, key);
PyErr_PrintEx(0);
return 0;
}
if(kbe_stricmp(key, "_mapping") == 0)
{
PyErr_Format(PyExc_AssertionError, "KBEngine::delSpaceData: key{_mapping} is protected!",
spaceID);
PyErr_PrintEx(0);
return 0;
}
space->delSpaceData(key);
S_Return;
}
}