ActiveObjectMgr fixes (#13560)

This commit is contained in:
DS 2023-10-09 17:13:04 +02:00 committed by GitHub
parent 929a13a9a0
commit 11ec75c2ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 204 additions and 153 deletions

@ -20,7 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once #pragma once
#include <map> #include <map>
#include <memory>
#include <vector>
#include "debug.h"
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include "util/basic_macros.h"
class TestClientActiveObjectMgr; class TestClientActiveObjectMgr;
class TestServerActiveObjectMgr; class TestServerActiveObjectMgr;
@ -32,14 +36,40 @@ class ActiveObjectMgr
friend class ::TestServerActiveObjectMgr; friend class ::TestServerActiveObjectMgr;
public: public:
ActiveObjectMgr() = default;
DISABLE_CLASS_COPY(ActiveObjectMgr);
virtual ~ActiveObjectMgr()
{
SANITY_CHECK(m_active_objects.empty());
// Note: Do not call clear() here. The derived class is already half
// destructed.
}
virtual void step(float dtime, const std::function<void(T *)> &f) = 0; virtual void step(float dtime, const std::function<void(T *)> &f) = 0;
virtual bool registerObject(T *obj) = 0; virtual bool registerObject(std::unique_ptr<T> obj) = 0;
virtual void removeObject(u16 id) = 0; virtual void removeObject(u16 id) = 0;
void clear()
{
while (!m_active_objects.empty())
removeObject(m_active_objects.begin()->first);
}
T *getActiveObject(u16 id) T *getActiveObject(u16 id)
{ {
auto n = m_active_objects.find(id); auto it = m_active_objects.find(id);
return (n != m_active_objects.end() ? n->second : nullptr); return it != m_active_objects.end() ? it->second.get() : nullptr;
}
std::vector<u16> getAllIds() const
{
std::vector<u16> ids;
ids.reserve(m_active_objects.size());
for (auto &it : m_active_objects) {
ids.push_back(it.first);
}
return ids;
} }
protected: protected:
@ -61,5 +91,8 @@ protected:
return id != 0 && m_active_objects.find(id) == m_active_objects.end(); return id != 0 && m_active_objects.find(id) == m_active_objects.end();
} }
std::map<u16, T *> m_active_objects; // ordered to fix #10985 // ordered to fix #10985
// Note: ActiveObjects can access the ActiveObjectMgr. Only erase objects using
// removeObject()!
std::map<u16, std::unique_ptr<T>> m_active_objects;
}; };

@ -25,28 +25,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
namespace client namespace client
{ {
void ActiveObjectMgr::clear() ActiveObjectMgr::~ActiveObjectMgr()
{ {
// delete active objects if (!m_active_objects.empty()) {
for (auto &active_object : m_active_objects) { warningstream << "client::ActiveObjectMgr::~ActiveObjectMgr(): not cleared."
delete active_object.second; << std::endl;
// Object must be marked as gone when children try to detach clear();
active_object.second = nullptr;
} }
m_active_objects.clear();
} }
void ActiveObjectMgr::step( void ActiveObjectMgr::step(
float dtime, const std::function<void(ClientActiveObject *)> &f) float dtime, const std::function<void(ClientActiveObject *)> &f)
{ {
g_profiler->avg("ActiveObjectMgr: CAO count [#]", m_active_objects.size()); g_profiler->avg("ActiveObjectMgr: CAO count [#]", m_active_objects.size());
for (auto &ao_it : m_active_objects) {
f(ao_it.second); // Same as in server activeobjectmgr.
std::vector<u16> ids = getAllIds();
for (u16 id : ids) {
auto it = m_active_objects.find(id);
if (it == m_active_objects.end())
continue; // obj was removed
f(it->second.get());
} }
} }
// clang-format off // clang-format off
bool ActiveObjectMgr::registerObject(ClientActiveObject *obj) bool ActiveObjectMgr::registerObject(std::unique_ptr<ClientActiveObject> obj)
{ {
assert(obj); // Pre-condition assert(obj); // Pre-condition
if (obj->getId() == 0) { if (obj->getId() == 0) {
@ -55,7 +60,6 @@ bool ActiveObjectMgr::registerObject(ClientActiveObject *obj)
infostream << "Client::ActiveObjectMgr::registerObject(): " infostream << "Client::ActiveObjectMgr::registerObject(): "
<< "no free id available" << std::endl; << "no free id available" << std::endl;
delete obj;
return false; return false;
} }
obj->setId(new_id); obj->setId(new_id);
@ -64,12 +68,11 @@ bool ActiveObjectMgr::registerObject(ClientActiveObject *obj)
if (!isFreeId(obj->getId())) { if (!isFreeId(obj->getId())) {
infostream << "Client::ActiveObjectMgr::registerObject(): " infostream << "Client::ActiveObjectMgr::registerObject(): "
<< "id is not free (" << obj->getId() << ")" << std::endl; << "id is not free (" << obj->getId() << ")" << std::endl;
delete obj;
return false; return false;
} }
infostream << "Client::ActiveObjectMgr::registerObject(): " infostream << "Client::ActiveObjectMgr::registerObject(): "
<< "added (id=" << obj->getId() << ")" << std::endl; << "added (id=" << obj->getId() << ")" << std::endl;
m_active_objects[obj->getId()] = obj; m_active_objects[obj->getId()] = std::move(obj);
return true; return true;
} }
@ -77,17 +80,17 @@ void ActiveObjectMgr::removeObject(u16 id)
{ {
verbosestream << "Client::ActiveObjectMgr::removeObject(): " verbosestream << "Client::ActiveObjectMgr::removeObject(): "
<< "id=" << id << std::endl; << "id=" << id << std::endl;
ClientActiveObject *obj = getActiveObject(id); auto it = m_active_objects.find(id);
if (!obj) { if (it == m_active_objects.end()) {
infostream << "Client::ActiveObjectMgr::removeObject(): " infostream << "Client::ActiveObjectMgr::removeObject(): "
<< "id=" << id << " not found" << std::endl; << "id=" << id << " not found" << std::endl;
return; return;
} }
m_active_objects.erase(id); std::unique_ptr<ClientActiveObject> obj = std::move(it->second);
m_active_objects.erase(it);
obj->removeFromScene(true); obj->removeFromScene(true);
delete obj;
} }
// clang-format on // clang-format on
@ -96,7 +99,7 @@ void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d,
{ {
f32 max_d2 = max_d * max_d; f32 max_d2 = max_d * max_d;
for (auto &ao_it : m_active_objects) { for (auto &ao_it : m_active_objects) {
ClientActiveObject *obj = ao_it.second; ClientActiveObject *obj = ao_it.second.get();
f32 d2 = (obj->getPosition() - origin).getLengthSQ(); f32 d2 = (obj->getPosition() - origin).getLengthSQ();
@ -114,7 +117,7 @@ std::vector<DistanceSortedActiveObject> ActiveObjectMgr::getActiveSelectableObje
v3f dir = shootline.getVector().normalize(); v3f dir = shootline.getVector().normalize();
for (auto &ao_it : m_active_objects) { for (auto &ao_it : m_active_objects) {
ClientActiveObject *obj = ao_it.second; ClientActiveObject *obj = ao_it.second.get();
aabb3f selection_box; aabb3f selection_box;
if (!obj->getSelectionBox(&selection_box)) if (!obj->getSelectionBox(&selection_box))

@ -26,13 +26,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
namespace client namespace client
{ {
class ActiveObjectMgr : public ::ActiveObjectMgr<ClientActiveObject> class ActiveObjectMgr final : public ::ActiveObjectMgr<ClientActiveObject>
{ {
public: public:
void clear(); ~ActiveObjectMgr() override;
void step(float dtime, void step(float dtime,
const std::function<void(ClientActiveObject *)> &f) override; const std::function<void(ClientActiveObject *)> &f) override;
bool registerObject(ClientActiveObject *obj) override; bool registerObject(std::unique_ptr<ClientActiveObject> obj) override;
void removeObject(u16 id) override; void removeObject(u16 id) override;
void getActiveObjects(const v3f &origin, f32 max_d, void getActiveObjects(const v3f &origin, f32 max_d,

@ -364,26 +364,26 @@ GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
return NULL; return NULL;
} }
u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) u16 ClientEnvironment::addActiveObject(std::unique_ptr<ClientActiveObject> object)
{ {
auto obj = object.get();
// Register object. If failed return zero id // Register object. If failed return zero id
if (!m_ao_manager.registerObject(object)) if (!m_ao_manager.registerObject(std::move(object)))
return 0; return 0;
object->addToScene(m_texturesource, m_client->getSceneManager()); obj->addToScene(m_texturesource, m_client->getSceneManager());
// Update lighting immediately // Update lighting immediately
object->updateLight(getDayNightRatio()); obj->updateLight(getDayNightRatio());
return object->getId(); return obj->getId();
} }
void ClientEnvironment::addActiveObject(u16 id, u8 type, void ClientEnvironment::addActiveObject(u16 id, u8 type,
const std::string &init_data) const std::string &init_data)
{ {
ClientActiveObject* obj = std::unique_ptr<ClientActiveObject> obj =
ClientActiveObject::create((ActiveObjectType) type, m_client, this); ClientActiveObject::create((ActiveObjectType) type, m_client, this);
if(obj == NULL) if (!obj) {
{
infostream<<"ClientEnvironment::addActiveObject(): " infostream<<"ClientEnvironment::addActiveObject(): "
<<"id="<<id<<" type="<<type<<": Couldn't create object" <<"id="<<id<<" type="<<type<<": Couldn't create object"
<<std::endl; <<std::endl;
@ -392,12 +392,9 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
obj->setId(id); obj->setId(id);
try try {
{
obj->initialize(init_data); obj->initialize(init_data);
} } catch(SerializationError &e) {
catch(SerializationError &e)
{
errorstream<<"ClientEnvironment::addActiveObject():" errorstream<<"ClientEnvironment::addActiveObject():"
<<" id="<<id<<" type="<<type <<" id="<<id<<" type="<<type
<<": SerializationError in initialize(): " <<": SerializationError in initialize(): "
@ -406,12 +403,12 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
<<std::endl; <<std::endl;
} }
u16 new_id = addActiveObject(obj); u16 new_id = addActiveObject(std::move(obj));
// Object initialized: // Object initialized:
if ((obj = getActiveObject(new_id))) { if (ClientActiveObject *obj2 = getActiveObject(new_id)) {
// Final step is to update all children which are already known // Final step is to update all children which are already known
// Data provided by AO_CMD_SPAWN_INFANT // Data provided by AO_CMD_SPAWN_INFANT
const auto &children = obj->getAttachmentChildIds(); const auto &children = obj2->getAttachmentChildIds();
for (auto c_id : children) { for (auto c_id : children) {
if (auto *o = getActiveObject(c_id)) if (auto *o = getActiveObject(c_id))
o->updateAttachments(); o->updateAttachments();

@ -101,7 +101,7 @@ public:
Returns the id of the object. Returns the id of the object.
Returns 0 if not added and thus deleted. Returns 0 if not added and thus deleted.
*/ */
u16 addActiveObject(ClientActiveObject *object); u16 addActiveObject(std::unique_ptr<ClientActiveObject> object);
void addActiveObject(u16 id, u8 type, const std::string &init_data); void addActiveObject(u16 id, u8 type, const std::string &init_data);
void removeActiveObject(u16 id); void removeActiveObject(u16 id);

@ -38,7 +38,7 @@ ClientActiveObject::~ClientActiveObject()
removeFromScene(true); removeFromScene(true);
} }
ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, std::unique_ptr<ClientActiveObject> ClientActiveObject::create(ActiveObjectType type,
Client *client, ClientEnvironment *env) Client *client, ClientEnvironment *env)
{ {
// Find factory function // Find factory function
@ -47,11 +47,11 @@ ClientActiveObject* ClientActiveObject::create(ActiveObjectType type,
// If factory is not found, just return. // If factory is not found, just return.
warningstream << "ClientActiveObject: No factory for type=" warningstream << "ClientActiveObject: No factory for type="
<< (int)type << std::endl; << (int)type << std::endl;
return NULL; return nullptr;
} }
Factory f = n->second; Factory f = n->second;
ClientActiveObject *object = (*f)(client, env); std::unique_ptr<ClientActiveObject> object = (*f)(client, env);
return object; return object;
} }

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "activeobject.h" #include "activeobject.h"
#include <memory>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -78,8 +79,8 @@ public:
virtual void initialize(const std::string &data) {} virtual void initialize(const std::string &data) {}
// Create a certain type of ClientActiveObject // Create a certain type of ClientActiveObject
static ClientActiveObject *create(ActiveObjectType type, Client *client, static std::unique_ptr<ClientActiveObject> create(ActiveObjectType type,
ClientEnvironment *env); Client *client, ClientEnvironment *env);
// If returns true, punch will not be sent to the server // If returns true, punch will not be sent to the server
virtual bool directReportPunch(v3f dir, const ItemStack *punchitem = nullptr, virtual bool directReportPunch(v3f dir, const ItemStack *punchitem = nullptr,
@ -87,7 +88,7 @@ public:
protected: protected:
// Used for creating objects based on type // Used for creating objects based on type
typedef ClientActiveObject *(*Factory)(Client *client, ClientEnvironment *env); typedef std::unique_ptr<ClientActiveObject> (*Factory)(Client *client, ClientEnvironment *env);
static void registerType(u16 type, Factory f); static void registerType(u16 type, Factory f);
Client *m_client; Client *m_client;
ClientEnvironment *m_env; ClientEnvironment *m_env;

@ -199,7 +199,7 @@ public:
return ACTIVEOBJECT_TYPE_TEST; return ACTIVEOBJECT_TYPE_TEST;
} }
static ClientActiveObject* create(Client *client, ClientEnvironment *env); static std::unique_ptr<ClientActiveObject> create(Client *client, ClientEnvironment *env);
void addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr); void addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr);
void removeFromScene(bool permanent); void removeFromScene(bool permanent);
@ -227,9 +227,9 @@ TestCAO::TestCAO(Client *client, ClientEnvironment *env):
ClientActiveObject::registerType(getType(), create); ClientActiveObject::registerType(getType(), create);
} }
ClientActiveObject* TestCAO::create(Client *client, ClientEnvironment *env) std::unique_ptr<ClientActiveObject> TestCAO::create(Client *client, ClientEnvironment *env)
{ {
return new TestCAO(client, env); return std::make_unique<TestCAO>(client, env);
} }
void TestCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) void TestCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
@ -326,7 +326,7 @@ void TestCAO::processMessage(const std::string &data)
GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): GenericCAO::GenericCAO(Client *client, ClientEnvironment *env):
ClientActiveObject(0, client, env) ClientActiveObject(0, client, env)
{ {
if (client == NULL) { if (!client) {
ClientActiveObject::registerType(getType(), create); ClientActiveObject::registerType(getType(), create);
} else { } else {
m_client = client; m_client = client;

@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "itemgroup.h" #include "itemgroup.h"
#include "constants.h" #include "constants.h"
#include <cassert> #include <cassert>
#include <memory>
class Camera; class Camera;
class Client; class Client;
@ -139,9 +140,9 @@ public:
~GenericCAO(); ~GenericCAO();
static ClientActiveObject* create(Client *client, ClientEnvironment *env) static std::unique_ptr<ClientActiveObject> create(Client *client, ClientEnvironment *env)
{ {
return new GenericCAO(client, env); return std::make_unique<GenericCAO>(client, env);
} }
inline ActiveObjectType getType() const override inline ActiveObjectType getType() const override

@ -611,8 +611,10 @@ int ModApiEnv::l_add_entity(lua_State *L)
const char *name = luaL_checkstring(L, 2); const char *name = luaL_checkstring(L, 2);
std::string staticdata = readParam<std::string>(L, 3, ""); std::string staticdata = readParam<std::string>(L, 3, "");
ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata); std::unique_ptr<ServerActiveObject> obj_u =
int objectid = env->addActiveObject(obj); std::make_unique<LuaEntitySAO>(env, pos, name, staticdata);
auto obj = obj_u.get();
int objectid = env->addActiveObject(std::move(obj_u));
// If failed to add, return nothing (reads as nil) // If failed to add, return nothing (reads as nil)
if (objectid == 0) if (objectid == 0)
return 0; return 0;

@ -25,16 +25,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
namespace server namespace server
{ {
void ActiveObjectMgr::clear(const std::function<bool(ServerActiveObject *, u16)> &cb) ActiveObjectMgr::~ActiveObjectMgr()
{ {
// make a defensive copy in case the if (!m_active_objects.empty()) {
// passed callback changes the set of active objects warningstream << "server::ActiveObjectMgr::~ActiveObjectMgr(): not cleared."
auto cloned_map(m_active_objects); << std::endl;
clear();
}
}
for (auto &it : cloned_map) { void ActiveObjectMgr::clearIf(const std::function<bool(ServerActiveObject *, u16)> &cb)
if (cb(it.second, it.first)) { {
// Remove reference from m_active_objects // Make a defensive copy of the ids in case the passed callback changes the
m_active_objects.erase(it.first); // set of active objects.
// The callback is called for newly added objects iff they happen to reuse
// an old id.
std::vector<u16> ids = getAllIds();
for (u16 id : ids) {
auto it = m_active_objects.find(id);
if (it == m_active_objects.end())
continue; // obj was already removed
if (cb(it->second.get(), id)) {
// erase by id, `it` can be invalid now
removeObject(id);
} }
} }
} }
@ -43,13 +57,20 @@ void ActiveObjectMgr::step(
float dtime, const std::function<void(ServerActiveObject *)> &f) float dtime, const std::function<void(ServerActiveObject *)> &f)
{ {
g_profiler->avg("ActiveObjectMgr: SAO count [#]", m_active_objects.size()); g_profiler->avg("ActiveObjectMgr: SAO count [#]", m_active_objects.size());
for (auto &ao_it : m_active_objects) {
f(ao_it.second); // See above.
std::vector<u16> ids = getAllIds();
for (u16 id : ids) {
auto it = m_active_objects.find(id);
if (it == m_active_objects.end())
continue; // obj was removed
f(it->second.get());
} }
} }
// clang-format off // clang-format off
bool ActiveObjectMgr::registerObject(ServerActiveObject *obj) bool ActiveObjectMgr::registerObject(std::unique_ptr<ServerActiveObject> obj)
{ {
assert(obj); // Pre-condition assert(obj); // Pre-condition
if (obj->getId() == 0) { if (obj->getId() == 0) {
@ -57,8 +78,6 @@ bool ActiveObjectMgr::registerObject(ServerActiveObject *obj)
if (new_id == 0) { if (new_id == 0) {
errorstream << "Server::ActiveObjectMgr::addActiveObjectRaw(): " errorstream << "Server::ActiveObjectMgr::addActiveObjectRaw(): "
<< "no free id available" << std::endl; << "no free id available" << std::endl;
if (obj->environmentDeletes())
delete obj;
return false; return false;
} }
obj->setId(new_id); obj->setId(new_id);
@ -70,8 +89,6 @@ bool ActiveObjectMgr::registerObject(ServerActiveObject *obj)
if (!isFreeId(obj->getId())) { if (!isFreeId(obj->getId())) {
errorstream << "Server::ActiveObjectMgr::addActiveObjectRaw(): " errorstream << "Server::ActiveObjectMgr::addActiveObjectRaw(): "
<< "id is not free (" << obj->getId() << ")" << std::endl; << "id is not free (" << obj->getId() << ")" << std::endl;
if (obj->environmentDeletes())
delete obj;
return false; return false;
} }
@ -80,15 +97,14 @@ bool ActiveObjectMgr::registerObject(ServerActiveObject *obj)
warningstream << "Server::ActiveObjectMgr::addActiveObjectRaw(): " warningstream << "Server::ActiveObjectMgr::addActiveObjectRaw(): "
<< "object position (" << p.X << "," << p.Y << "," << p.Z << "object position (" << p.X << "," << p.Y << "," << p.Z
<< ") outside maximum range" << std::endl; << ") outside maximum range" << std::endl;
if (obj->environmentDeletes())
delete obj;
return false; return false;
} }
m_active_objects[obj->getId()] = obj; auto obj_p = obj.get();
m_active_objects[obj->getId()] = std::move(obj);
verbosestream << "Server::ActiveObjectMgr::addActiveObjectRaw(): " verbosestream << "Server::ActiveObjectMgr::addActiveObjectRaw(): "
<< "Added id=" << obj->getId() << "; there are now " << "Added id=" << obj_p->getId() << "; there are now "
<< m_active_objects.size() << " active objects." << std::endl; << m_active_objects.size() << " active objects." << std::endl;
return true; return true;
} }
@ -97,15 +113,17 @@ void ActiveObjectMgr::removeObject(u16 id)
{ {
verbosestream << "Server::ActiveObjectMgr::removeObject(): " verbosestream << "Server::ActiveObjectMgr::removeObject(): "
<< "id=" << id << std::endl; << "id=" << id << std::endl;
ServerActiveObject *obj = getActiveObject(id); auto it = m_active_objects.find(id);
if (!obj) { if (it == m_active_objects.end()) {
infostream << "Server::ActiveObjectMgr::removeObject(): " infostream << "Server::ActiveObjectMgr::removeObject(): "
<< "id=" << id << " not found" << std::endl; << "id=" << id << " not found" << std::endl;
return; return;
} }
m_active_objects.erase(id); // Delete the obj before erasing, as the destructor may indirectly access
delete obj; // m_active_objects.
it->second.reset();
m_active_objects.erase(id); // `it` can be invalid now
} }
// clang-format on // clang-format on
@ -115,7 +133,7 @@ void ActiveObjectMgr::getObjectsInsideRadius(const v3f &pos, float radius,
{ {
float r2 = radius * radius; float r2 = radius * radius;
for (auto &activeObject : m_active_objects) { for (auto &activeObject : m_active_objects) {
ServerActiveObject *obj = activeObject.second; ServerActiveObject *obj = activeObject.second.get();
const v3f &objectpos = obj->getBasePosition(); const v3f &objectpos = obj->getBasePosition();
if (objectpos.getDistanceFromSQ(pos) > r2) if (objectpos.getDistanceFromSQ(pos) > r2)
continue; continue;
@ -130,7 +148,7 @@ void ActiveObjectMgr::getObjectsInArea(const aabb3f &box,
std::function<bool(ServerActiveObject *obj)> include_obj_cb) std::function<bool(ServerActiveObject *obj)> include_obj_cb)
{ {
for (auto &activeObject : m_active_objects) { for (auto &activeObject : m_active_objects) {
ServerActiveObject *obj = activeObject.second; ServerActiveObject *obj = activeObject.second.get();
const v3f &objectpos = obj->getBasePosition(); const v3f &objectpos = obj->getBasePosition();
if (!box.isPointInside(objectpos)) if (!box.isPointInside(objectpos))
continue; continue;
@ -155,7 +173,7 @@ void ActiveObjectMgr::getAddedActiveObjectsAroundPos(const v3f &player_pos, f32
u16 id = ao_it.first; u16 id = ao_it.first;
// Get object // Get object
ServerActiveObject *object = ao_it.second; ServerActiveObject *object = ao_it.second.get();
if (!object) if (!object)
continue; continue;

@ -26,13 +26,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
namespace server namespace server
{ {
class ActiveObjectMgr : public ::ActiveObjectMgr<ServerActiveObject> class ActiveObjectMgr final : public ::ActiveObjectMgr<ServerActiveObject>
{ {
public: public:
void clear(const std::function<bool(ServerActiveObject *, u16)> &cb); ~ActiveObjectMgr() override;
// If cb returns true, the obj will be deleted
void clearIf(const std::function<bool(ServerActiveObject *, u16)> &cb);
void step(float dtime, void step(float dtime,
const std::function<void(ServerActiveObject *)> &f) override; const std::function<void(ServerActiveObject *)> &f) override;
bool registerObject(ServerActiveObject *obj) override; bool registerObject(std::unique_ptr<ServerActiveObject> obj) override;
void removeObject(u16 id) override; void removeObject(u16 id) override;
void getObjectsInsideRadius(const v3f &pos, float radius, void getObjectsInsideRadius(const v3f &pos, float radius,

@ -67,10 +67,6 @@ public:
virtual void addedToEnvironment(u32 dtime_s){}; virtual void addedToEnvironment(u32 dtime_s){};
// Called before removing from environment // Called before removing from environment
virtual void removingFromEnvironment(){}; virtual void removingFromEnvironment(){};
// Returns true if object's deletion is the job of the
// environment
virtual bool environmentDeletes() const
{ return true; }
// Safely mark the object for removal or deactivation // Safely mark the object for removal or deactivation
void markForRemoval(); void markForRemoval();

@ -633,9 +633,9 @@ void ServerEnvironment::savePlayer(RemotePlayer *player)
PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player, PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
session_t peer_id, bool is_singleplayer) session_t peer_id, bool is_singleplayer)
{ {
PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer); auto playersao = std::make_unique<PlayerSAO>(this, player, peer_id, is_singleplayer);
// Create player if it doesn't exist // Create player if it doesn't exist
if (!m_player_database->loadPlayer(player, playersao)) { if (!m_player_database->loadPlayer(player, playersao.get())) {
*new_player = true; *new_player = true;
// Set player position // Set player position
infostream << "Server: Finding spawn place for player \"" infostream << "Server: Finding spawn place for player \""
@ -662,12 +662,13 @@ PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
player->clearHud(); player->clearHud();
/* Add object to environment */ /* Add object to environment */
addActiveObject(playersao); PlayerSAO *ret = playersao.get();
addActiveObject(std::move(playersao));
// Update active blocks quickly for a bit so objects in those blocks appear on the client // Update active blocks quickly for a bit so objects in those blocks appear on the client
m_fast_active_block_divider = 10; m_fast_active_block_divider = 10;
return playersao; return ret;
} }
void ServerEnvironment::saveMeta() void ServerEnvironment::saveMeta()
@ -1230,13 +1231,10 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
m_script->removeObjectReference(obj); m_script->removeObjectReference(obj);
// Delete active object // Delete active object
if (obj->environmentDeletes())
delete obj;
return true; return true;
}; };
m_ao_manager.clear(cb_removal); m_ao_manager.clearIf(cb_removal);
// Get list of loaded blocks // Get list of loaded blocks
std::vector<v3s16> loaded_blocks; std::vector<v3s16> loaded_blocks;
@ -1675,11 +1673,11 @@ void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
} }
} }
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) u16 ServerEnvironment::addActiveObject(std::unique_ptr<ServerActiveObject> object)
{ {
assert(object); // Pre-condition assert(object); // Pre-condition
m_added_objects++; m_added_objects++;
u16 id = addActiveObjectRaw(object, true, 0); u16 id = addActiveObjectRaw(std::move(object), true, 0);
return id; return id;
} }
@ -1831,10 +1829,11 @@ void ServerEnvironment::getSelectedActiveObjects(
************ Private methods ************* ************ Private methods *************
*/ */
u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, u16 ServerEnvironment::addActiveObjectRaw(std::unique_ptr<ServerActiveObject> object_u,
bool set_changed, u32 dtime_s) bool set_changed, u32 dtime_s)
{ {
if (!m_ao_manager.registerObject(object)) { auto object = object_u.get();
if (!m_ao_manager.registerObject(std::move(object_u))) {
return 0; return 0;
} }
@ -1925,13 +1924,10 @@ void ServerEnvironment::removeRemovedObjects()
m_script->removeObjectReference(obj); m_script->removeObjectReference(obj);
// Delete // Delete
if (obj->environmentDeletes())
delete obj;
return true; return true;
}; };
m_ao_manager.clear(clear_cb); m_ao_manager.clearIf(clear_cb);
} }
static void print_hexdump(std::ostream &o, const std::string &data) static void print_hexdump(std::ostream &o, const std::string &data)
@ -1968,12 +1964,12 @@ static void print_hexdump(std::ostream &o, const std::string &data)
} }
} }
ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos, std::unique_ptr<ServerActiveObject> ServerEnvironment::createSAO(ActiveObjectType type,
const std::string &data) v3f pos, const std::string &data)
{ {
switch (type) { switch (type) {
case ACTIVEOBJECT_TYPE_LUAENTITY: case ACTIVEOBJECT_TYPE_LUAENTITY:
return new LuaEntitySAO(this, pos, data); return std::make_unique<LuaEntitySAO>(this, pos, data);
default: default:
warningstream << "ServerActiveObject: No factory for type=" << type << std::endl; warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
} }
@ -1995,7 +1991,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
std::vector<StaticObject> new_stored; std::vector<StaticObject> new_stored;
for (const StaticObject &s_obj : block->m_static_objects.getAllStored()) { for (const StaticObject &s_obj : block->m_static_objects.getAllStored()) {
// Create an active object from the data // Create an active object from the data
ServerActiveObject *obj = std::unique_ptr<ServerActiveObject> obj =
createSAO((ActiveObjectType)s_obj.type, s_obj.pos, s_obj.data); createSAO((ActiveObjectType)s_obj.type, s_obj.pos, s_obj.data);
// If couldn't create object, store static data back. // If couldn't create object, store static data back.
if (!obj) { if (!obj) {
@ -2012,7 +2008,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
<< "activated static object pos=" << (s_obj.pos / BS) << "activated static object pos=" << (s_obj.pos / BS)
<< " type=" << (int)s_obj.type << std::endl; << " type=" << (int)s_obj.type << std::endl;
// This will also add the object to the active static list // This will also add the object to the active static list
addActiveObjectRaw(obj, false, dtime_s); addActiveObjectRaw(std::move(obj), false, dtime_s);
if (block->isOrphan()) if (block->isOrphan())
return; return;
} }
@ -2168,13 +2164,10 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
m_script->removeObjectReference(obj); m_script->removeObjectReference(obj);
// Delete active object // Delete active object
if (obj->environmentDeletes())
delete obj;
return true; return true;
}; };
m_ao_manager.clear(cb_deactivate); m_ao_manager.clearIf(cb_deactivate);
} }
void ServerEnvironment::deleteStaticFromBlock( void ServerEnvironment::deleteStaticFromBlock(

@ -277,7 +277,7 @@ public:
Returns the id of the object. Returns the id of the object.
Returns 0 if not added and thus deleted. Returns 0 if not added and thus deleted.
*/ */
u16 addActiveObject(ServerActiveObject *object); u16 addActiveObject(std::unique_ptr<ServerActiveObject> object);
/* /*
Add an active object as a static object to the corresponding Add an active object as a static object to the corresponding
@ -422,7 +422,8 @@ private:
Returns the id of the object. Returns the id of the object.
Returns 0 if not added and thus deleted. Returns 0 if not added and thus deleted.
*/ */
u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s); u16 addActiveObjectRaw(std::unique_ptr<ServerActiveObject> object,
bool set_changed, u32 dtime_s);
/* /*
Remove all objects that satisfy (isGone() && m_known_by_count==0) Remove all objects that satisfy (isGone() && m_known_by_count==0)
@ -518,5 +519,6 @@ private:
MetricGaugePtr m_active_block_gauge; MetricGaugePtr m_active_block_gauge;
MetricGaugePtr m_active_object_gauge; MetricGaugePtr m_active_object_gauge;
ServerActiveObject* createSAO(ActiveObjectType type, v3f pos, const std::string &data); std::unique_ptr<ServerActiveObject> createSAO(ActiveObjectType type, v3f pos,
const std::string &data);
}; };

@ -90,8 +90,9 @@ void TestClientActiveObjectMgr::testFreeID()
// Register basic objects, ensure we never found // Register basic objects, ensure we never found
for (u8 i = 0; i < UINT8_MAX; i++) { for (u8 i = 0; i < UINT8_MAX; i++) {
// Register an object // Register an object
auto tcao = new TestClientActiveObject(); auto tcao_u = std::make_unique<TestClientActiveObject>();
caomgr.registerObject(tcao); auto tcao = tcao_u.get();
caomgr.registerObject(std::move(tcao_u));
aoids.push_back(tcao->getId()); aoids.push_back(tcao->getId());
// Ensure next id is not in registered list // Ensure next id is not in registered list
@ -105,8 +106,9 @@ void TestClientActiveObjectMgr::testFreeID()
void TestClientActiveObjectMgr::testRegisterObject() void TestClientActiveObjectMgr::testRegisterObject()
{ {
client::ActiveObjectMgr caomgr; client::ActiveObjectMgr caomgr;
auto tcao = new TestClientActiveObject(); auto tcao_u = std::make_unique<TestClientActiveObject>();
UASSERT(caomgr.registerObject(tcao)); auto tcao = tcao_u.get();
UASSERT(caomgr.registerObject(std::move(tcao_u)));
u16 id = tcao->getId(); u16 id = tcao->getId();
@ -114,8 +116,9 @@ void TestClientActiveObjectMgr::testRegisterObject()
UASSERT(tcaoToCompare->getId() == id); UASSERT(tcaoToCompare->getId() == id);
UASSERT(tcaoToCompare == tcao); UASSERT(tcaoToCompare == tcao);
tcao = new TestClientActiveObject(); tcao_u = std::make_unique<TestClientActiveObject>();
UASSERT(caomgr.registerObject(tcao)); tcao = tcao_u.get();
UASSERT(caomgr.registerObject(std::move(tcao_u)));
UASSERT(caomgr.getActiveObject(tcao->getId()) == tcao); UASSERT(caomgr.getActiveObject(tcao->getId()) == tcao);
UASSERT(caomgr.getActiveObject(tcao->getId()) != tcaoToCompare); UASSERT(caomgr.getActiveObject(tcao->getId()) != tcaoToCompare);
@ -125,8 +128,9 @@ void TestClientActiveObjectMgr::testRegisterObject()
void TestClientActiveObjectMgr::testRemoveObject() void TestClientActiveObjectMgr::testRemoveObject()
{ {
client::ActiveObjectMgr caomgr; client::ActiveObjectMgr caomgr;
auto tcao = new TestClientActiveObject(); auto tcao_u = std::make_unique<TestClientActiveObject>();
UASSERT(caomgr.registerObject(tcao)); auto tcao = tcao_u.get();
UASSERT(caomgr.registerObject(std::move(tcao_u)));
u16 id = tcao->getId(); u16 id = tcao->getId();
UASSERT(caomgr.getActiveObject(id) != nullptr) UASSERT(caomgr.getActiveObject(id) != nullptr)
@ -140,8 +144,10 @@ void TestClientActiveObjectMgr::testRemoveObject()
void TestClientActiveObjectMgr::testGetActiveSelectableObjects() void TestClientActiveObjectMgr::testGetActiveSelectableObjects()
{ {
client::ActiveObjectMgr caomgr; client::ActiveObjectMgr caomgr;
auto obj = new TestSelectableClientActiveObject({v3f{-1, -1, -1}, v3f{1, 1, 1}}); auto obj_u = std::make_unique<TestSelectableClientActiveObject>(
UASSERT(caomgr.registerObject(obj)); aabb3f{v3f{-1, -1, -1}, v3f{1, 1, 1}});
auto obj = obj_u.get();
UASSERT(caomgr.registerObject(std::move(obj_u)));
auto assert_obj_selected = [&] (v3f a, v3f b) { auto assert_obj_selected = [&] (v3f a, v3f b) {
auto actual = caomgr.getActiveSelectableObjects({a, b}); auto actual = caomgr.getActiveSelectableObjects({a, b});

@ -53,15 +53,6 @@ void TestServerActiveObjectMgr::runTests(IGameDef *gamedef)
TEST(testGetAddedActiveObjectsAroundPos); TEST(testGetAddedActiveObjectsAroundPos);
} }
void clearSAOMgr(server::ActiveObjectMgr *saomgr)
{
auto clear_cb = [](ServerActiveObject *obj, u16 id) {
delete obj;
return true;
};
saomgr->clear(clear_cb);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TestServerActiveObjectMgr::testFreeID() void TestServerActiveObjectMgr::testFreeID()
@ -78,8 +69,9 @@ void TestServerActiveObjectMgr::testFreeID()
// Register basic objects, ensure we never found // Register basic objects, ensure we never found
for (u8 i = 0; i < UINT8_MAX; i++) { for (u8 i = 0; i < UINT8_MAX; i++) {
// Register an object // Register an object
auto sao = new MockServerActiveObject(); auto sao_u = std::make_unique<MockServerActiveObject>();
saomgr.registerObject(sao); auto sao = sao_u.get();
saomgr.registerObject(std::move(sao_u));
aoids.push_back(sao->getId()); aoids.push_back(sao->getId());
// Ensure next id is not in registered list // Ensure next id is not in registered list
@ -87,14 +79,15 @@ void TestServerActiveObjectMgr::testFreeID()
aoids.end()); aoids.end());
} }
clearSAOMgr(&saomgr); saomgr.clear();
} }
void TestServerActiveObjectMgr::testRegisterObject() void TestServerActiveObjectMgr::testRegisterObject()
{ {
server::ActiveObjectMgr saomgr; server::ActiveObjectMgr saomgr;
auto sao = new MockServerActiveObject(); auto sao_u = std::make_unique<MockServerActiveObject>();
UASSERT(saomgr.registerObject(sao)); auto sao = sao_u.get();
UASSERT(saomgr.registerObject(std::move(sao_u)));
u16 id = sao->getId(); u16 id = sao->getId();
@ -102,19 +95,21 @@ void TestServerActiveObjectMgr::testRegisterObject()
UASSERT(saoToCompare->getId() == id); UASSERT(saoToCompare->getId() == id);
UASSERT(saoToCompare == sao); UASSERT(saoToCompare == sao);
sao = new MockServerActiveObject(); sao_u = std::make_unique<MockServerActiveObject>();
UASSERT(saomgr.registerObject(sao)); sao = sao_u.get();
UASSERT(saomgr.registerObject(std::move(sao_u)));
UASSERT(saomgr.getActiveObject(sao->getId()) == sao); UASSERT(saomgr.getActiveObject(sao->getId()) == sao);
UASSERT(saomgr.getActiveObject(sao->getId()) != saoToCompare); UASSERT(saomgr.getActiveObject(sao->getId()) != saoToCompare);
clearSAOMgr(&saomgr); saomgr.clear();
} }
void TestServerActiveObjectMgr::testRemoveObject() void TestServerActiveObjectMgr::testRemoveObject()
{ {
server::ActiveObjectMgr saomgr; server::ActiveObjectMgr saomgr;
auto sao = new MockServerActiveObject(); auto sao_u = std::make_unique<MockServerActiveObject>();
UASSERT(saomgr.registerObject(sao)); auto sao = sao_u.get();
UASSERT(saomgr.registerObject(std::move(sao_u)));
u16 id = sao->getId(); u16 id = sao->getId();
UASSERT(saomgr.getActiveObject(id) != nullptr) UASSERT(saomgr.getActiveObject(id) != nullptr)
@ -122,7 +117,7 @@ void TestServerActiveObjectMgr::testRemoveObject()
saomgr.removeObject(sao->getId()); saomgr.removeObject(sao->getId());
UASSERT(saomgr.getActiveObject(id) == nullptr); UASSERT(saomgr.getActiveObject(id) == nullptr);
clearSAOMgr(&saomgr); saomgr.clear();
} }
void TestServerActiveObjectMgr::testGetObjectsInsideRadius() void TestServerActiveObjectMgr::testGetObjectsInsideRadius()
@ -137,7 +132,7 @@ void TestServerActiveObjectMgr::testGetObjectsInsideRadius()
}; };
for (const auto &p : sao_pos) { for (const auto &p : sao_pos) {
saomgr.registerObject(new MockServerActiveObject(nullptr, p)); saomgr.registerObject(std::make_unique<MockServerActiveObject>(nullptr, p));
} }
std::vector<ServerActiveObject *> result; std::vector<ServerActiveObject *> result;
@ -160,7 +155,7 @@ void TestServerActiveObjectMgr::testGetObjectsInsideRadius()
saomgr.getObjectsInsideRadius(v3f(), 750000, result, include_obj_cb); saomgr.getObjectsInsideRadius(v3f(), 750000, result, include_obj_cb);
UASSERTCMP(int, ==, result.size(), 4); UASSERTCMP(int, ==, result.size(), 4);
clearSAOMgr(&saomgr); saomgr.clear();
} }
void TestServerActiveObjectMgr::testGetAddedActiveObjectsAroundPos() void TestServerActiveObjectMgr::testGetAddedActiveObjectsAroundPos()
@ -175,7 +170,7 @@ void TestServerActiveObjectMgr::testGetAddedActiveObjectsAroundPos()
}; };
for (const auto &p : sao_pos) { for (const auto &p : sao_pos) {
saomgr.registerObject(new MockServerActiveObject(nullptr, p)); saomgr.registerObject(std::make_unique<MockServerActiveObject>(nullptr, p));
} }
std::queue<u16> result; std::queue<u16> result;
@ -188,5 +183,5 @@ void TestServerActiveObjectMgr::testGetAddedActiveObjectsAroundPos()
saomgr.getAddedActiveObjectsAroundPos(v3f(), 740, 50, cur_objects, result); saomgr.getAddedActiveObjectsAroundPos(v3f(), 740, 50, cur_objects, result);
UASSERTCMP(int, ==, result.size(), 2); UASSERTCMP(int, ==, result.size(), 2);
clearSAOMgr(&saomgr); saomgr.clear();
} }