From 63867b1a372a4d1a4a4ffdec9d0862b094211a89 Mon Sep 17 00:00:00 2001 From: sapier Date: Mon, 5 Jan 2015 18:34:59 +0100 Subject: [PATCH] Fix memory leaks due to messed up memory handling for particles as well as their spawners --- src/client.cpp | 6 + src/client.h | 3 + src/game.cpp | 58 ++------ src/particles.cpp | 340 ++++++++++++++++++++++++++++++---------------- src/particles.h | 62 ++++++--- 5 files changed, 285 insertions(+), 184 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 04c59d077..536e9afa7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -248,6 +248,7 @@ Client::Client( device->getSceneManager(), tsrc, this, device ), + m_particle_manager(&m_env), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this), m_device(device), m_server_ser_ver(SER_FMT_VER_INVALID), @@ -2854,6 +2855,11 @@ MtEventManager* Client::getEventManager() return m_event; } +ParticleManager* Client::getParticleManager() +{ + return &m_particle_manager; +} + scene::IAnimatedMesh* Client::getMesh(const std::string &filename) { std::map::const_iterator i = diff --git a/src/client.h b/src/client.h index 9f36a257f..898fc4daa 100644 --- a/src/client.h +++ b/src/client.h @@ -454,6 +454,7 @@ public: virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); + virtual ParticleManager* getParticleManager(); virtual bool checkLocalPrivilege(const std::string &priv) { return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename); @@ -497,8 +498,10 @@ private: ISoundManager *m_sound; MtEventManager *m_event; + MeshUpdateThread m_mesh_update_thread; ClientEnvironment m_env; + ParticleManager m_particle_manager; con::Connection m_con; IrrlichtDevice *m_device; // Server serialization version diff --git a/src/game.cpp b/src/game.cpp index 6eb6aaec4..b292ad1bf 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1800,8 +1800,6 @@ void Game::shutdown() if (sky) sky->drop(); - clear_particles(); - /* cleanup menus */ while (g_menumgr.menuCount() > 0) { g_menumgr.m_stack.front()->setVisible(false); @@ -3016,44 +3014,11 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) delete(event.show_formspec.formspec); delete(event.show_formspec.formname); - } else if (event.type == CE_SPAWN_PARTICLE) { - video::ITexture *texture = - gamedef->tsrc()->getTexture(*(event.spawn_particle.texture)); - - new Particle(gamedef, smgr, player, client->getEnv(), - *event.spawn_particle.pos, - *event.spawn_particle.vel, - *event.spawn_particle.acc, - event.spawn_particle.expirationtime, - event.spawn_particle.size, - event.spawn_particle.collisiondetection, - event.spawn_particle.vertical, - texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0)); - } else if (event.type == CE_ADD_PARTICLESPAWNER) { - video::ITexture *texture = - gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture)); - - new ParticleSpawner(gamedef, smgr, player, - event.add_particlespawner.amount, - event.add_particlespawner.spawntime, - *event.add_particlespawner.minpos, - *event.add_particlespawner.maxpos, - *event.add_particlespawner.minvel, - *event.add_particlespawner.maxvel, - *event.add_particlespawner.minacc, - *event.add_particlespawner.maxacc, - event.add_particlespawner.minexptime, - event.add_particlespawner.maxexptime, - event.add_particlespawner.minsize, - event.add_particlespawner.maxsize, - event.add_particlespawner.collisiondetection, - event.add_particlespawner.vertical, - texture, - event.add_particlespawner.id); - } else if (event.type == CE_DELETE_PARTICLESPAWNER) { - delete_particlespawner(event.delete_particlespawner.id); + } else if ((event.type == CE_SPAWN_PARTICLE) || + (event.type == CE_ADD_PARTICLESPAWNER) || + (event.type == CE_DELETE_PARTICLESPAWNER)) { + client->getParticleManager()->handleParticleEvent(&event, gamedef, + smgr, player); } else if (event.type == CE_HUDADD) { u32 id = event.hudadd.id; @@ -3615,8 +3580,8 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(n); - addPunchingParticles(gamedef, smgr, player, - client->getEnv(), nodepos, features.tiles); + client->getParticleManager()->addPunchingParticles(gamedef, smgr, + player, nodepos, features.tiles); } } @@ -3662,9 +3627,8 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(wasnode); - addDiggingParticles - (gamedef, smgr, player, client->getEnv(), - nodepos, features.tiles); + client->getParticleManager()->addDiggingParticles(gamedef, smgr, + player, nodepos, features.tiles); } runData->dig_time = 0; @@ -3787,9 +3751,7 @@ void Game::updateFrame(std::vector &highlight_boxes, /* Update particles */ - - allparticles_step(dtime); - allparticlespawners_step(dtime, client->getEnv()); + client->getParticleManager()->step(dtime); /* Fog diff --git a/src/particles.cpp b/src/particles.cpp index b1662e10b..b32ec1542 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "clientmap.h" #include "mapnode.h" +#include "client.h" /* Utility @@ -43,14 +44,11 @@ v3f random_v3f(v3f min, v3f max) rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z); } -std::vector all_particles; -std::map all_particlespawners; - Particle::Particle( IGameDef *gamedef, scene::ISceneManager* smgr, LocalPlayer *player, - ClientEnvironment &env, + ClientEnvironment *env, v3f pos, v3f velocity, v3f acceleration, @@ -66,7 +64,7 @@ Particle::Particle( { // Misc m_gamedef = gamedef; - m_env = &env; + m_env = env; // Texture m_material.setFlag(video::EMF_LIGHTING, false); @@ -100,8 +98,6 @@ Particle::Particle( // Init model updateVertices(); - - all_particles.push_back(this); } Particle::~Particle() @@ -216,98 +212,6 @@ void Particle::updateVertices() } } -/* - Helpers -*/ - - -void allparticles_step (float dtime) -{ - for(std::vector::iterator i = all_particles.begin(); - i != all_particles.end();) - { - if ((*i)->get_expired()) - { - (*i)->remove(); - delete *i; - i = all_particles.erase(i); - } - else - { - (*i)->step(dtime); - i++; - } - } -} - -void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, ClientEnvironment &env, v3s16 pos, - const TileSpec tiles[]) -{ - for (u16 j = 0; j < 32; j++) // set the amount of particles here - { - addNodeParticle(gamedef, smgr, player, env, pos, tiles); - } -} - -void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, ClientEnvironment &env, - v3s16 pos, const TileSpec tiles[]) -{ - addNodeParticle(gamedef, smgr, player, env, pos, tiles); -} - -// add a particle of a node -// used by digging and punching particles -void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, ClientEnvironment &env, v3s16 pos, - const TileSpec tiles[]) -{ - // Texture - u8 texid = myrand_range(0,5); - video::ITexture *texture = tiles[texid].texture; - - // Only use first frame of animated texture - f32 ymax = 1; - if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) - ymax /= tiles[texid].animation_frame_count; - - float size = rand()%64/512.; - float visual_size = BS*size; - v2f texsize(size*2, ymax*size*2); - v2f texpos; - texpos.X = ((rand()%64)/64.-texsize.X); - texpos.Y = ymax*((rand()%64)/64.-texsize.Y); - - // Physics - v3f velocity( (rand()%100/50.-1)/1.5, - rand()%100/35., - (rand()%100/50.-1)/1.5); - - v3f acceleration(0,-9,0); - v3f particlepos = v3f( - (f32)pos.X+rand()%100/200.-0.25, - (f32)pos.Y+rand()%100/200.-0.25, - (f32)pos.Z+rand()%100/200.-0.25 - ); - - new Particle( - gamedef, - smgr, - player, - env, - particlepos, - velocity, - acceleration, - rand()%100/100., // expiration time - visual_size, - true, - false, - texture, - texpos, - texsize); -} - /* ParticleSpawner */ @@ -316,7 +220,9 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, u16 amount, float time, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool vertical, video::ITexture *texture, u32 id) + bool collisiondetection, bool vertical, video::ITexture *texture, u32 id, + ParticleManager *p_manager) : + m_particlemanager(p_manager) { m_gamedef = gamedef; m_smgr = smgr; @@ -343,13 +249,11 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime; m_spawntimes.push_back(spawntime); } - - all_particlespawners.insert(std::pair(id, this)); } ParticleSpawner::~ParticleSpawner() {} -void ParticleSpawner::step(float dtime, ClientEnvironment &env) +void ParticleSpawner::step(float dtime, ClientEnvironment* env) { m_time += dtime; @@ -372,7 +276,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env) *(m_maxsize-m_minsize) +m_minsize; - new Particle( + Particle* toadd = new Particle( m_gamedef, m_smgr, m_player, @@ -387,6 +291,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env) m_texture, v2f(0.0, 0.0), v2f(1.0, 1.0)); + m_particlemanager->addParticle(toadd); i = m_spawntimes.erase(i); } else @@ -431,50 +336,245 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env) } } -void allparticlespawners_step (float dtime, ClientEnvironment &env) + +ParticleManager::ParticleManager(ClientEnvironment* env) : + m_env(env) +{} + +ParticleManager::~ParticleManager() { + clearAll(); +} + +void ParticleManager::step(float dtime) +{ + stepParticles (dtime); + stepSpawners (dtime); +} + +void ParticleManager::stepSpawners (float dtime) +{ + JMutexAutoLock lock(m_spawner_list_lock); for(std::map::iterator i = - all_particlespawners.begin(); - i != all_particlespawners.end();) + m_particle_spawners.begin(); + i != m_particle_spawners.end();) { if (i->second->get_expired()) { delete i->second; - all_particlespawners.erase(i++); + m_particle_spawners.erase(i++); } else { - i->second->step(dtime, env); + i->second->step(dtime, m_env); i++; } } } -void delete_particlespawner (u32 id) +void ParticleManager::stepParticles (float dtime) { - if (all_particlespawners.find(id) != all_particlespawners.end()) + JMutexAutoLock lock(m_particle_list_lock); + for(std::vector::iterator i = m_particles.begin(); + i != m_particles.end();) { - delete all_particlespawners.find(id)->second; - all_particlespawners.erase(id); + if ((*i)->get_expired()) + { + (*i)->remove(); + delete *i; + i = m_particles.erase(i); + } + else + { + (*i)->step(dtime); + i++; + } } } -void clear_particles () +void ParticleManager::clearAll () { + JMutexAutoLock lock(m_spawner_list_lock); + JMutexAutoLock lock2(m_particle_list_lock); for(std::map::iterator i = - all_particlespawners.begin(); - i != all_particlespawners.end();) + m_particle_spawners.begin(); + i != m_particle_spawners.end();) { delete i->second; - all_particlespawners.erase(i++); + m_particle_spawners.erase(i++); } for(std::vector::iterator i = - all_particles.begin(); - i != all_particles.end();) + m_particles.begin(); + i != m_particles.end();) { (*i)->remove(); delete *i; - i = all_particles.erase(i); + i = m_particles.erase(i); } } + +void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, + scene::ISceneManager* smgr, LocalPlayer *player) +{ + if (event->type == CE_DELETE_PARTICLESPAWNER) { + JMutexAutoLock lock(m_spawner_list_lock); + if (m_particle_spawners.find(event->delete_particlespawner.id) != + m_particle_spawners.end()) + { + delete m_particle_spawners.find(event->delete_particlespawner.id)->second; + m_particle_spawners.erase(event->delete_particlespawner.id); + } + // no allocated memory in delete event + return; + } + + if (event->type == CE_ADD_PARTICLESPAWNER) { + + { + JMutexAutoLock lock(m_spawner_list_lock); + if (m_particle_spawners.find(event->delete_particlespawner.id) != + m_particle_spawners.end()) + { + delete m_particle_spawners.find(event->delete_particlespawner.id)->second; + m_particle_spawners.erase(event->delete_particlespawner.id); + } + } + video::ITexture *texture = + gamedef->tsrc()->getTexture(*(event->add_particlespawner.texture)); + + ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player, + event->add_particlespawner.amount, + event->add_particlespawner.spawntime, + *event->add_particlespawner.minpos, + *event->add_particlespawner.maxpos, + *event->add_particlespawner.minvel, + *event->add_particlespawner.maxvel, + *event->add_particlespawner.minacc, + *event->add_particlespawner.maxacc, + event->add_particlespawner.minexptime, + event->add_particlespawner.maxexptime, + event->add_particlespawner.minsize, + event->add_particlespawner.maxsize, + event->add_particlespawner.collisiondetection, + event->add_particlespawner.vertical, + texture, + event->add_particlespawner.id, + this); + + /* delete allocated content of event */ + delete event->add_particlespawner.minpos; + delete event->add_particlespawner.maxpos; + delete event->add_particlespawner.minvel; + delete event->add_particlespawner.maxvel; + delete event->add_particlespawner.minacc; + delete event->add_particlespawner.texture; + delete event->add_particlespawner.maxacc; + + { + JMutexAutoLock lock(m_spawner_list_lock); + m_particle_spawners.insert( + std::pair( + event->delete_particlespawner.id, + toadd)); + } + + return; + } + + if (event->type == CE_SPAWN_PARTICLE) { + video::ITexture *texture = + gamedef->tsrc()->getTexture(*(event->spawn_particle.texture)); + + Particle* toadd = new Particle(gamedef, smgr, player, m_env, + *event->spawn_particle.pos, + *event->spawn_particle.vel, + *event->spawn_particle.acc, + event->spawn_particle.expirationtime, + event->spawn_particle.size, + event->spawn_particle.collisiondetection, + event->spawn_particle.vertical, + texture, + v2f(0.0, 0.0), + v2f(1.0, 1.0)); + + addParticle(toadd); + + delete event->spawn_particle.pos; + delete event->spawn_particle.vel; + delete event->spawn_particle.acc; + + return; + } +} + +void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +{ + for (u16 j = 0; j < 32; j++) // set the amount of particles here + { + addNodeParticle(gamedef, smgr, player, pos, tiles); + } +} + +void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +{ + addNodeParticle(gamedef, smgr, player, pos, tiles); +} + +void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +{ + // Texture + u8 texid = myrand_range(0, 5); + video::ITexture *texture = tiles[texid].texture; + + // Only use first frame of animated texture + f32 ymax = 1; + if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) + ymax /= tiles[texid].animation_frame_count; + + float size = rand() % 64 / 512.; + float visual_size = BS * size; + v2f texsize(size * 2, ymax * size * 2); + v2f texpos; + texpos.X = ((rand() % 64) / 64. - texsize.X); + texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y); + + // Physics + v3f velocity((rand() % 100 / 50. - 1) / 1.5, + rand() % 100 / 35., + (rand() % 100 / 50. - 1) / 1.5); + + v3f acceleration(0,-9,0); + v3f particlepos = v3f( + (f32) pos.X + rand() %100 /200. - 0.25, + (f32) pos.Y + rand() %100 /200. - 0.25, + (f32) pos.Z + rand() %100 /200. - 0.25 + ); + + Particle* toadd = new Particle( + gamedef, + smgr, + player, + m_env, + particlepos, + velocity, + acceleration, + rand() % 100 / 100., // expiration time + visual_size, + true, + false, + texture, + texpos, + texsize); + + addParticle(toadd); +} + +void ParticleManager::addParticle(Particle* toadd) +{ + JMutexAutoLock lock(m_particle_list_lock); + m_particles.push_back(toadd); +} diff --git a/src/particles.h b/src/particles.h index 101fc49ce..d7f1147f1 100644 --- a/src/particles.h +++ b/src/particles.h @@ -28,6 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include "environment.h" +struct ClientEvent; +class ParticleManager; + class Particle : public scene::ISceneNode { public: @@ -35,7 +38,7 @@ class Particle : public scene::ISceneNode IGameDef* gamedef, scene::ISceneManager* mgr, LocalPlayer *player, - ClientEnvironment &env, + ClientEnvironment *env, v3f pos, v3f velocity, v3f acceleration, @@ -114,16 +117,18 @@ class ParticleSpawner bool collisiondetection, bool vertical, video::ITexture *texture, - u32 id); + u32 id, + ParticleManager* p_manager); ~ParticleSpawner(); - void step(float dtime, ClientEnvironment &env); + void step(float dtime, ClientEnvironment *env); bool get_expired () { return (m_amount <= 0) && m_spawntime != 0; } private: + ParticleManager* m_particlemanager; float m_time; IGameDef *m_gamedef; scene::ISceneManager *m_smgr; @@ -144,24 +149,49 @@ class ParticleSpawner std::vector m_spawntimes; bool m_collisiondetection; bool m_vertical; + }; -void allparticles_step (float dtime); -void allparticlespawners_step (float dtime, ClientEnvironment &env); +/** + * Class doing particle as well as their spawners handling + */ +class ParticleManager +{ +friend class ParticleSpawner; +public: + ParticleManager(ClientEnvironment* env); + ~ParticleManager(); -void delete_particlespawner (u32 id); -void clear_particles (); + void step (float dtime); -void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, ClientEnvironment &env, v3s16 pos, - const TileSpec tiles[]); + void handleParticleEvent(ClientEvent *event,IGameDef *gamedef, + scene::ISceneManager* smgr, LocalPlayer *player); -void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, ClientEnvironment &env, v3s16 pos, - const TileSpec tiles[]); + void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); -void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, ClientEnvironment &env, v3s16 pos, - const TileSpec tiles[]); + void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + + void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + +protected: + void addParticle(Particle* toadd); + +private: + + void stepParticles (float dtime); + void stepSpawners (float dtime); + + void clearAll (); + + std::vector m_particles; + std::map m_particle_spawners; + + ClientEnvironment* m_env; + JMutex m_particle_list_lock; + JMutex m_spawner_list_lock; +}; #endif