Fix TOCTOU issues when calling Game.RunAfterDelay.

Since the action runs after a delay, the state of the game may no longer be the same and it may no longer be valid to run the action. Anything that references the world now calls IsCurrentWorld to ensure the world hasn't changed or been disposed.
This commit is contained in:
RoosterDragon
2015-10-24 19:27:05 +01:00
parent d10dd5c9d9
commit 7c889c5ef0
7 changed files with 36 additions and 22 deletions

View File

@@ -767,7 +767,7 @@ namespace OpenRA
public static bool IsCurrentWorld(World world)
{
return OrderManager != null && OrderManager.World == world;
return OrderManager != null && OrderManager.World == world && !world.Disposing;
}
}
}

View File

@@ -62,22 +62,22 @@ namespace OpenRA.Mods.Common.Traits
foreach (var a in player.World.Actors.Where(a => a.Owner == player))
a.Kill(a);
if (player == player.World.LocalPlayer)
{
Game.RunAfterDelay(info.NotificationDelay, () =>
{
if (Game.IsCurrentWorld(player.World))
if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer)
Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Lose", player.Faction.InternalName);
});
}
}
public void OnPlayerWon(Player player)
{
Game.Debug("{0} is victorious.", player.PlayerName);
if (player == player.World.LocalPlayer)
Game.RunAfterDelay(info.NotificationDelay, () => Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Faction.InternalName));
Game.RunAfterDelay(info.NotificationDelay, () =>
{
if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer)
Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Faction.InternalName);
});
}
public void OnObjectiveAdded(Player player, int id) { }

View File

@@ -150,6 +150,9 @@ namespace OpenRA.Mods.Common.Traits
if (gameOver)
Game.RunAfterDelay(Info.GameOverDelay, () =>
{
if (!Game.IsCurrentWorld(player.World))
return;
player.World.EndGame();
player.World.SetPauseState(true);
player.World.PauseStateLocked = true;

View File

@@ -107,22 +107,22 @@ namespace OpenRA.Mods.Common.Traits
foreach (var a in player.World.Actors.Where(a => a.Owner == player))
a.Kill(a);
if (player == player.World.LocalPlayer)
{
Game.RunAfterDelay(info.NotificationDelay, () =>
{
if (Game.IsCurrentWorld(player.World))
if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer)
Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Lose", player.Faction.InternalName);
});
}
}
public void OnPlayerWon(Player player)
{
Game.Debug("{0} is victorious.", player.PlayerName);
if (player == player.World.LocalPlayer)
Game.RunAfterDelay(info.NotificationDelay, () => Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Faction.InternalName));
Game.RunAfterDelay(info.NotificationDelay, () =>
{
if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer)
Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Faction.InternalName);
});
}
public void OnObjectiveAdded(Player player, int id) { }

View File

@@ -51,12 +51,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var exitDelay = iop != null ? iop.ExitDelay : 0;
if (mpe != null)
{
Game.RunAfterDelay(exitDelay, () => mpe.Fade(MenuPaletteEffect.EffectType.Black));
Game.RunAfterDelay(exitDelay, () =>
{
if (Game.IsCurrentWorld(world))
mpe.Fade(MenuPaletteEffect.EffectType.Black);
});
exitDelay += 40 * mpe.Info.FadeLength;
}
Game.RunAfterDelay(exitDelay, () =>
{
if (!Game.IsCurrentWorld(world))
return;
Game.Disconnect();
Ui.ResetAll();
Game.LoadShellMap();

View File

@@ -42,6 +42,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
loadingObserverWidgets = true;
Game.RunAfterDelay(objectives != null ? objectives.GameOverDelay : 0, () =>
{
if (!Game.IsCurrentWorld(world))
return;
playerRoot.RemoveChildren();
Game.LoadWidget(world, "OBSERVER_WIDGETS", playerRoot, new WidgetArgs());
});

View File

@@ -109,6 +109,7 @@ namespace OpenRA.Mods.D2k.Activities
Game.RunAfterDelay(1000, () =>
{
if (Game.IsCurrentWorld(self.World))
foreach (var affectedPlayer in affectedPlayers)
NotifyPlayer(affectedPlayer, attackPosition);
});