Fixed #186 (problems with entity deletion in Atlas).

Maybe fixed errors from rare Atlas double-shutdown, but I don't know how
to reproduce the condition.

This was SVN commit r5162.
This commit is contained in:
Ykkrosh 2007-06-10 19:00:25 +00:00
parent 2045b3c188
commit 69404654bf
5 changed files with 222 additions and 169 deletions

View File

@ -21,11 +21,16 @@
namespace AtlasMessage {
static bool g_IsInitialised = false;
static bool g_DidInitSim;
MESSAGEHANDLER(Init)
{
UNUSED2(msg);
// Don't do anything if we're called multiple times
if (g_IsInitialised)
return;
#if OS_LINUX
// When using GLX (Linux), SDL has to load the GL library to find
@ -61,6 +66,8 @@ MESSAGEHANDLER(Init)
if(ogl_HaveExtension("WGL_EXT_swap_control"))
pwglSwapIntervalEXT(1);
#endif
g_IsInitialised = true;
}
@ -68,6 +75,10 @@ MESSAGEHANDLER(Shutdown)
{
UNUSED2(msg);
// Don't do anything if we're called multiple times
if (! g_IsInitialised)
return;
// Empty the CommandProc, to get rid of its references to entities before
// we kill the EntityManager
GetCommandProc().Destroy();
@ -79,6 +90,8 @@ MESSAGEHANDLER(Shutdown)
if (! g_DidInitSim)
flags |= INIT_NO_SIM;
Shutdown(flags);
g_IsInitialised = false;
}

View File

@ -4,6 +4,7 @@
#include "MessageHandler.h"
#include "../CommandProc.h"
#include "../SimState.h"
#include "../View.h"
#include "graphics/GameView.h"
@ -705,28 +706,13 @@ END_COMMAND(RotateObject)
BEGIN_COMMAND(DeleteObject)
{
CUnit* m_UnitInLimbo;
// These two values are never both non-NULL
std::auto_ptr<SimState::Entity> m_FrozenEntity;
std::auto_ptr<SimState::Nonentity> m_FrozenNonentity;
cDeleteObject()
: m_FrozenEntity(NULL), m_FrozenNonentity(NULL)
{
m_UnitInLimbo = NULL;
}
~cDeleteObject()
{
if (m_UnitInLimbo)
{
if (m_UnitInLimbo->GetEntity())
m_UnitInLimbo->GetEntity()->Kill();
else
delete m_UnitInLimbo;
}
// TODO (IMPORTANT): this can crash when interacting with simulation
// playing/resetting because the entity gets deleted while we still
// think we have a reference to it. This system should probably be
// replaced by something like SimState::Entity to safely serialise
// units while they're in limbo.
}
void Do()
@ -741,30 +727,32 @@ BEGIN_COMMAND(DeleteObject)
if (unit->GetEntity())
{
// HACK: I don't know the proper way of undoably deleting entities...
unit->GetEntity()->entf_set(ENTF_DESTROYED);
// TODO: territories don't ignore DESTROYED entities
if (unit->GetEntity()->m_base->m_isTerritoryCentre)
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
m_FrozenEntity.reset(new SimState::Entity( SimState::Entity::Freeze(unit) ));
unit->GetEntity()->Kill();
}
else
{
m_FrozenNonentity.reset(new SimState::Nonentity( SimState::Nonentity::Freeze(unit) ));
GetUnitManager().RemoveUnit(unit);
}
GetUnitManager().RemoveUnit(unit);
m_UnitInLimbo = unit;
}
void Undo()
{
if (m_UnitInLimbo->GetEntity())
if (m_FrozenEntity.get())
{
m_UnitInLimbo->GetEntity()->entf_clear(ENTF_DESTROYED);
m_FrozenEntity->Thaw();
// XXXif (m_UnitInLimbo->GetEntity()->m_base->m_isTerritoryCentre)
//g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
if (m_UnitInLimbo->GetEntity()->m_base->m_isTerritoryCentre)
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
m_FrozenEntity.reset();
}
else if (m_FrozenNonentity.get())
{
m_FrozenNonentity->Thaw();
m_FrozenNonentity.reset();
}
GetUnitManager().AddUnit(m_UnitInLimbo);
m_UnitInLimbo = NULL;
}
};
END_COMMAND(DeleteObject)

View File

@ -0,0 +1,136 @@
#include "precompiled.h"
#include "SimState.h"
#include "graphics/Model.h"
#include "graphics/ObjectBase.h"
#include "graphics/ObjectEntry.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "maths/MathUtil.h"
#include "ps/Player.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "simulation/Entity.h"
#include "simulation/EntityTemplate.h"
#include "simulation/EntityTemplateCollection.h"
#include "simulation/EntityManager.h"
#include "simulation/Projectile.h"
SimState::Entity SimState::Entity::Freeze(CUnit* unit)
{
CEntity* entity = unit->GetEntity();
debug_assert(entity);
Entity e;
e.templateName = entity->m_base->m_Tag;
e.unitID = unit->GetID();
e.selections = unit->GetActorSelections();
e.playerID = entity->GetPlayer()->GetPlayerID();
e.position = entity->m_position;
e.angle = entity->m_orientation.Y;
return e;
}
void SimState::Entity::Thaw()
{
CEntityTemplate* base = g_EntityTemplateCollection.GetTemplate(templateName, g_Game->GetPlayer(playerID));
if (! base)
return;
HEntity ent = g_EntityManager.Create(base, position, angle, selections);
if (! ent)
return;
ent->m_actor->SetPlayerID(playerID);
ent->m_actor->SetID(unitID);
ent->Initialize();
}
SimState::Nonentity SimState::Nonentity::Freeze(CUnit* unit)
{
Nonentity n;
n.actorName = unit->GetObject()->m_Base->m_Name;
n.unitID = unit->GetID();
n.selections = unit->GetActorSelections();
n.position = unit->GetModel()->GetTransform().GetTranslation();
CVector3D orient = unit->GetModel()->GetTransform().GetIn();
n.angle = atan2(-orient.X, -orient.Z);
return n;
}
void SimState::Nonentity::Thaw()
{
CUnitManager& unitMan = g_Game->GetWorld()->GetUnitManager();
CUnit* unit = unitMan.CreateUnit(actorName, NULL, selections);
if (! unit)
return;
CMatrix3D m;
m.SetYRotation(angle + PI);
m.Translate(position);
unit->GetModel()->SetTransform(m);
unit->SetID(unitID);
}
SimState* SimState::Freeze(bool onlyEntities)
{
SimState* simState = new SimState();
simState->onlyEntities = onlyEntities;
CUnitManager& unitMan = g_Game->GetWorld()->GetUnitManager();
const std::vector<CUnit*>& units = unitMan.GetUnits();
for (std::vector<CUnit*>::const_iterator unit = units.begin(); unit != units.end(); ++unit)
{
// Ignore objects that aren't entities
if (! (*unit)->GetEntity())
continue;
Entity e = Entity::Freeze(*unit);
simState->entities.push_back(e);
}
if (! onlyEntities)
{
for (std::vector<CUnit*>::const_iterator unit = units.begin(); unit != units.end(); ++unit)
{
// Ignore objects that are entities
if ((*unit)->GetEntity())
continue;
Nonentity n = Nonentity::Freeze(*unit);
simState->nonentities.push_back(n);
}
}
return simState;
}
void SimState::Thaw()
{
CUnitManager& unitMan = g_Game->GetWorld()->GetUnitManager();
// delete all existing entities
g_Game;
g_Game->GetWorld();
g_Game->GetWorld()->GetProjectileManager();
g_Game->GetWorld()->GetProjectileManager().DeleteAll();
g_EntityManager.DeleteAll();
if (! onlyEntities)
{
// delete all remaining non-entity units
unitMan.DeleteAll();
// don't reset the unitID counter - there's no need, since it'll work alright anyway
}
for (size_t i = 0; i < entities.size(); ++i)
entities[i].Thaw();
g_EntityManager.InitializeAll();
if (! onlyEntities)
for (size_t i = 0; i < nonentities.size(); ++i)
nonentities[i].Thaw();
}

View File

@ -0,0 +1,46 @@
#ifndef SIMSTATE_INCLUDED
#define SIMSTATE_INCLUDED
class CUnit;
#include "maths/Vector3D.h"
class SimState
{
public:
class Entity
{
public:
static Entity Freeze(CUnit* unit);
void Thaw();
private:
CStrW templateName;
int unitID;
std::set<CStr> selections;
int playerID;
CVector3D position;
float angle;
};
class Nonentity
{
public:
static Nonentity Freeze(CUnit* unit);
void Thaw();
private:
CStrW actorName;
int unitID;
std::set<CStr> selections;
CVector3D position;
float angle;
};
static SimState* Freeze(bool onlyEntities);
void Thaw();
private:
bool onlyEntities;
std::vector<Entity> entities;
std::vector<Nonentity> nonentities;
};
#endif // SIMSTATE_INCLUDED

View File

@ -5,26 +5,17 @@
#include "ActorViewer.h"
#include "GameLoop.h"
#include "Messages.h"
#include "SimState.h"
#include "graphics/CinemaTrack.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/ObjectBase.h"
#include "graphics/ObjectEntry.h"
#include "graphics/SColor.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "lib/timer.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Player.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "simulation/Entity.h"
#include "simulation/EntityManager.h"
#include "simulation/EntityTemplate.h"
#include "simulation/EntityTemplateCollection.h"
#include "simulation/Projectile.h"
#include "simulation/Simulation.h"
extern void (*Atlas_GLSwapBuffers)(void* context);
@ -128,32 +119,6 @@ namespace AtlasMessage
extern void AtlasRenderSelection();
}
struct SimState
{
struct Entity
{
CStrW templateName;
int unitID;
std::set<CStr> selections;
int playerID;
CVector3D position;
float angle;
};
struct Nonentity
{
CStrW actorName;
int unitID;
std::set<CStr> selections;
CVector3D position;
float angle;
};
bool onlyEntities;
std::vector<Entity> entities;
std::vector<Nonentity> nonentities;
};
template<typename T, typename S>
static void delete_pair_2nd(std::pair<T,S> v)
{
@ -254,53 +219,8 @@ void ViewGame::SetSpeedMultiplier(float speed)
void ViewGame::SaveState(const std::wstring& label, bool onlyEntities)
{
SimState* simState = new SimState();
simState->onlyEntities = onlyEntities;
CUnitManager& unitMan = g_Game->GetWorld()->GetUnitManager();
const std::vector<CUnit*>& units = unitMan.GetUnits();
for (std::vector<CUnit*>::const_iterator unit = units.begin(); unit != units.end(); ++unit)
{
CEntity* entity = (*unit)->GetEntity();
// Ignore objects that aren't entities
if (! entity)
continue;
SimState::Entity e;
e.templateName = entity->m_base->m_Tag;
e.unitID = (*unit)->GetID();
e.selections = (*unit)->GetActorSelections();
e.playerID = entity->GetPlayer()->GetPlayerID();
e.position = entity->m_position;
e.angle = entity->m_orientation.Y;
simState->entities.push_back(e);
}
if (! onlyEntities)
{
for (std::vector<CUnit*>::const_iterator unit = units.begin(); unit != units.end(); ++unit)
{
// Ignore objects that are entities
if ((*unit)->GetEntity())
continue;
SimState::Nonentity n;
n.actorName = (*unit)->GetObject()->m_Base->m_Name;
n.unitID = (*unit)->GetID();
n.selections = (*unit)->GetActorSelections();
n.position = (*unit)->GetModel()->GetTransform().GetTranslation();
CVector3D orient = (*unit)->GetModel()->GetTransform().GetIn();
n.angle = atan2(-orient.X, -orient.Z);
simState->nonentities.push_back(n);
}
}
delete m_SavedStates[label]; // in case it already exists
m_SavedStates[label] = simState;
m_SavedStates[label] = SimState::Freeze(onlyEntities);
}
void ViewGame::RestoreState(const std::wstring& label)
@ -309,57 +229,7 @@ void ViewGame::RestoreState(const std::wstring& label)
if (! simState)
return;
CUnitManager& unitMan = g_Game->GetWorld()->GetUnitManager();
// delete all existing entities
g_Game->GetWorld()->GetProjectileManager().DeleteAll();
g_EntityManager.DeleteAll();
if (! simState->onlyEntities)
{
// delete all remaining non-entity units
unitMan.DeleteAll();
// don't reset the unitID counter - there's no need, since it'll work alright anyway
}
for (size_t i = 0; i < simState->entities.size(); ++i)
{
SimState::Entity& e = simState->entities[i];
CEntityTemplate* base = g_EntityTemplateCollection.GetTemplate(e.templateName, g_Game->GetPlayer(e.playerID));
if (base)
{
HEntity ent = g_EntityManager.Create(base, e.position, e.angle, e.selections);
if (ent)
{
ent->m_actor->SetPlayerID(e.playerID);
ent->m_actor->SetID(e.unitID);
}
}
}
g_EntityManager.InitializeAll();
if (! simState->onlyEntities)
{
for (size_t i = 0; i < simState->nonentities.size(); ++i)
{
SimState::Nonentity& n = simState->nonentities[i];
CUnit* unit = unitMan.CreateUnit(n.actorName, NULL, n.selections);
if (unit)
{
CMatrix3D m;
m.SetYRotation(n.angle + PI);
m.Translate(n.position);
unit->GetModel()->SetTransform(m);
unit->SetID(n.unitID);
}
}
}
simState->Thaw();
}
//////////////////////////////////////////////////////////////////////////