Merge pull request #8226 from pchote/actor-disposal

Dispose traits when destroying an actor.
This commit is contained in:
Oliver Brakmann
2015-06-02 19:33:22 +02:00
54 changed files with 138 additions and 81 deletions

View File

@@ -22,7 +22,7 @@ using OpenRA.Traits;
namespace OpenRA
{
public class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>
public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>, IDisposable
{
public readonly ActorInfo Info;
@@ -33,7 +33,7 @@ namespace OpenRA
[Sync] public Player Owner { get; set; }
public bool IsInWorld { get; internal set; }
public bool Destroyed { get; private set; }
public bool Disposed { get; private set; }
Activity currentActivity;
@@ -51,7 +51,7 @@ namespace OpenRA
public IEffectiveOwner EffectiveOwner { get { return effectiveOwner.Value; } }
public bool IsIdle { get { return currentActivity == null; } }
public bool IsDead { get { return Destroyed || (health.Value == null ? false : health.Value.IsDead); } }
public bool IsDead { get { return Disposed || (health.Value == null ? false : health.Value.IsDead); } }
public CPos Location { get { return occupySpace.Value.TopLeft; } }
public WPos CenterPosition { get { return occupySpace.Value.CenterPosition; } }
@@ -215,18 +215,21 @@ namespace OpenRA
World.TraitDict.AddTrait(this, trait);
}
public void Destroy()
public void Dispose()
{
World.AddFrameEndTask(w =>
{
if (Destroyed)
if (Disposed)
return;
if (IsInWorld)
World.Remove(this);
foreach (var t in TraitsImplementing<INotifyActorDisposing>())
t.Disposing(this);
World.TraitDict.RemoveActor(this);
Destroyed = true;
Disposed = true;
if (luaInterface != null)
luaInterface.Value.OnActorDestroyed();
@@ -238,7 +241,7 @@ namespace OpenRA
{
World.AddFrameEndTask(w =>
{
if (Destroyed)
if (Disposed)
return;
var oldOwner = Owner;

View File

@@ -123,6 +123,9 @@ namespace OpenRA.Graphics
public void Draw()
{
if (World.WorldActor.Disposed)
return;
RefreshPalette();
if (World.Type == WorldType.Shellmap && !Game.Settings.Game.ShowShellmap)
@@ -140,7 +143,7 @@ namespace OpenRA.Graphics
// added for contrails
foreach (var a in World.ActorsWithTrait<IPostRender>())
if (a.Actor.IsInWorld && !a.Actor.Destroyed)
if (a.Actor.IsInWorld && !a.Actor.Disposed)
a.Trait.RenderAfterWorld(this, a.Actor);
var renderShroud = World.RenderPlayer != null ? World.RenderPlayer.Shroud : null;
@@ -154,7 +157,7 @@ namespace OpenRA.Graphics
Game.Renderer.DisableScissor();
var overlayRenderables = World.Selection.Actors.Where(a => !a.Destroyed)
var overlayRenderables = World.Selection.Actors.Where(a => !a.Disposed)
.SelectMany(a => a.TraitsImplementing<IPostRenderSelection>())
.SelectMany(t => t.RenderAfterWorld(this));
@@ -174,7 +177,7 @@ namespace OpenRA.Graphics
if (World.Type == WorldType.Regular && Game.Settings.Game.AlwaysShowStatusBars)
{
foreach (var g in World.Actors.Where(a => !a.Destroyed
foreach (var g in World.Actors.Where(a => !a.Disposed
&& a.HasTrait<Selectable>()
&& !World.FogObscures(a)
&& !World.Selection.Actors.Contains(a)))
@@ -273,6 +276,12 @@ namespace OpenRA.Graphics
public void Dispose()
{
// HACK: Disposing the world from here violates ownership
// but the WorldRenderer lifetime matches the disposal
// behavior we want for the world, and the root object setup
// is so horrible that doing it properly would be a giant mess.
World.Dispose();
palette.Dispose();
Theater.Dispose();
terrainRenderer.Dispose();

View File

@@ -107,7 +107,7 @@ namespace OpenRA.Orders
if (self.Owner != self.World.LocalPlayer)
return null;
if (self.Destroyed || !target.IsValidFor(self))
if (self.Disposed || !target.IsValidFor(self))
return null;
if (mi.Button == Game.Settings.Game.MouseButtonPreference.Action)

View File

@@ -41,7 +41,7 @@ namespace OpenRA.Scripting
var commandClasses = Context.ActorCommands[actor.Info].AsEnumerable();
// Destroyed actors cannot have their traits queried
if (actor.Destroyed)
if (actor.Disposed)
commandClasses = commandClasses.Where(c => c.HasAttribute<ExposedForDestroyedActors>());
var args = new object[] { Context, actor };

View File

@@ -78,7 +78,7 @@ namespace OpenRA
foreach (var cg in controlGroups.Values)
{
// note: NOT `!a.IsInWorld`, since that would remove things that are in transports.
cg.RemoveAll(a => a.Destroyed || a.Owner != world.LocalPlayer);
cg.RemoveAll(a => a.Disposed || a.Owner != world.LocalPlayer);
}
}

View File

@@ -80,7 +80,7 @@ namespace OpenRA
static void CheckDestroyed(Actor actor)
{
if (actor.Destroyed)
if (actor.Disposed)
throw new InvalidOperationException("Attempted to get trait from destroyed object ({0})".F(actor));
}

View File

@@ -104,7 +104,7 @@ namespace OpenRA.Traits
self.World.AddFrameEndTask(w =>
{
if (self.Destroyed)
if (self.Disposed)
return;
var line = self.TraitOrDefault<DrawLineToTarget>();
@@ -120,7 +120,7 @@ namespace OpenRA.Traits
self.World.AddFrameEndTask(w =>
{
if (self.Destroyed)
if (self.Disposed)
return;
target.Flash();

View File

@@ -155,7 +155,7 @@ namespace OpenRA.Traits
nd.Killed(self, ai);
if (RemoveOnDeath)
self.Destroy();
self.Dispose();
if (attacker == null)
Log.Write("debug", "{0} #{1} was killed.", self.Info.Name, self.ActorID);
@@ -198,7 +198,7 @@ namespace OpenRA.Traits
{
public static DamageState GetDamageState(this Actor self)
{
if (self.Destroyed)
if (self.Disposed)
return DamageState.Dead;
var health = self.TraitOrDefault<Health>();
@@ -207,7 +207,7 @@ namespace OpenRA.Traits
public static void InflictDamage(this Actor self, Actor attacker, int damage, DamageWarhead warhead)
{
if (self.Destroyed) return;
if (self.Disposed) return;
var health = self.TraitOrDefault<Health>();
if (health == null) return;
health.InflictDamage(self, attacker, damage, warhead, false);

View File

@@ -100,7 +100,7 @@ namespace OpenRA.Traits
if (NeedRenderables)
{
NeedRenderables = false;
if (!actor.Destroyed)
if (!actor.Disposed)
{
IsRendering = true;
renderables = actor.Render(wr).ToArray();

View File

@@ -94,6 +94,7 @@ namespace OpenRA.Traits
public interface INotifyDamageStateChanged { void DamageStateChanged(Actor self, AttackInfo e); }
public interface INotifyRepair { void Repairing(Actor self, Actor host); }
public interface INotifyKilled { void Killed(Actor self, AttackInfo e); }
public interface INotifyActorDisposing { void Disposing(Actor self); }
public interface INotifyAppliedDamage { void AppliedDamage(Actor self, Actor damaged, AttackInfo e); }
public interface INotifyBuildComplete { void BuildingComplete(Actor self); }
public interface INotifyBuildingPlaced { void BuildingPlaced(Actor self); }

View File

@@ -198,7 +198,7 @@ namespace OpenRA.Traits
yield break;
for (var i = influence[a]; i != null; i = i.Next)
if (!i.Actor.Destroyed)
if (!i.Actor.Disposed)
yield return i.Actor;
}
@@ -208,7 +208,7 @@ namespace OpenRA.Traits
yield break;
for (var i = influence[a]; i != null; i = i.Next)
if (!i.Actor.Destroyed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
if (!i.Actor.Disposed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
yield return i.Actor;
}
@@ -285,7 +285,7 @@ namespace OpenRA.Traits
var always = sub == SubCell.FullCell || sub == SubCell.Any;
for (var i = influence[a]; i != null; i = i.Next)
if ((always || i.SubCell == sub || i.SubCell == SubCell.FullCell) && !i.Actor.Destroyed && withCondition(i.Actor))
if ((always || i.SubCell == sub || i.SubCell == SubCell.FullCell) && !i.Actor.Disposed && withCondition(i.Actor))
return true;
return false;

View File

@@ -59,7 +59,7 @@ namespace OpenRA
continue;
var orderSubject = o.Subject;
if (orderSubject.Destroyed)
if (orderSubject.Disposed)
continue;
foreach (var voice in orderSubject.TraitsImplementing<IVoiced>())

View File

@@ -24,7 +24,7 @@ namespace OpenRA
{
public enum WorldType { Regular, Shellmap, Editor }
public class World
public sealed class World : IDisposable
{
class ActorIDComparer : IComparer<Actor>
{
@@ -369,6 +369,19 @@ namespace OpenRA
pi.OutcomeTimestampUtc = DateTime.UtcNow;
}
}
public void Dispose()
{
frameEndActions.Clear();
// Dispose newer actors first, and the world actor last
foreach (var a in actors.Reverse())
a.Dispose();
// Actor disposals are done in a FrameEndTask
while (frameEndActions.Count != 0)
frameEndActions.Dequeue()(this);
}
}
public struct TraitPair<T>