diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index f6f03ced2a..4cacd3392d 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -38,9 +38,14 @@ namespace OpenRA public readonly MersenneTwister SharedRandom; - public readonly List Players = new List(); + public Player[] Players = new Player[0]; + + public void SetPlayers(IEnumerable players, Player localPlayer) + { + Players = players.ToArray(); + SetLocalPlayer(localPlayer); + } - public void AddPlayer(Player p) { Players.Add(p); } public Player LocalPlayer { get; private set; } public event Action GameOver = () => { }; @@ -82,12 +87,18 @@ namespace OpenRA get { return LobbyInfo.GlobalSettings.AllowCheats || LobbyInfo.IsSinglePlayer; } } - public void SetLocalPlayer(string pr) + void SetLocalPlayer(Player localPlayer) { + if (localPlayer == null) + return; + + if (!Players.Contains(localPlayer)) + throw new ArgumentException("The local player must be one of the players in the world.", "localPlayer"); + if (IsReplay) return; - LocalPlayer = Players.FirstOrDefault(p => p.InternalName == pr); + LocalPlayer = localPlayer; RenderPlayer = LocalPlayer; } diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index c211285bf9..d1bb72ee2d 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -35,11 +35,11 @@ namespace OpenRA.Mods.Common.Traits readonly bool startsRevealed; readonly PPos[] footprint; - readonly Lazy tooltip; - readonly Lazy health; - readonly Dictionary stateByPlayer = new Dictionary(); + FrozenState[] stateByPlayerIndex; + ITooltip tooltip; + Health health; bool initialized; class FrozenState @@ -62,8 +62,6 @@ namespace OpenRA.Mods.Common.Traits startsRevealed = info.StartsRevealed && !init.Contains(); var footprintCells = FootprintUtils.Tiles(init.Self).ToList(); footprint = footprintCells.SelectMany(c => map.ProjectedCellsCovering(c.ToMPos(map))).ToArray(); - tooltip = Exts.Lazy(() => init.Self.TraitsImplementing().FirstOrDefault()); - health = Exts.Lazy(() => init.Self.TraitOrDefault()); } bool IsVisibleInner(Actor self, Player byPlayer) @@ -90,42 +88,55 @@ namespace OpenRA.Mods.Common.Traits return; VisibilityHash = 0; - foreach (var player in self.World.Players) + var players = self.World.Players; + + if (!initialized) + { + // The world players never change, so we can safely index this collection. + stateByPlayerIndex = new FrozenState[players.Length]; + tooltip = self.TraitsImplementing().FirstOrDefault(); + health = self.TraitOrDefault(); + } + + for (var i = 0; i < players.Length; i++) { FrozenActor frozenActor; bool isVisible; if (!initialized) { + var player = players[i]; frozenActor = new FrozenActor(self, footprint, player.Shroud, startsRevealed); isVisible = startsRevealed; - stateByPlayer.Add(player, new FrozenState(frozenActor) { IsVisible = isVisible }); + var state = new FrozenState(frozenActor) { IsVisible = isVisible }; + stateByPlayer.Add(player, state); + stateByPlayerIndex[i] = state; player.PlayerActor.Trait().Add(frozenActor); } else { - var state = stateByPlayer[player]; + var state = stateByPlayerIndex[i]; frozenActor = state.FrozenActor; isVisible = !frozenActor.Visible; state.IsVisible = isVisible; } if (isVisible) - VisibilityHash += player.ClientIndex; + VisibilityHash |= 1 << (i % 32); else continue; frozenActor.Owner = self.Owner; - if (health.Value != null) + if (health != null) { - frozenActor.HP = health.Value.HP; - frozenActor.DamageState = health.Value.DamageState; + frozenActor.HP = health.HP; + frozenActor.DamageState = health.DamageState; } - if (tooltip.Value != null) + if (tooltip != null) { - frozenActor.TooltipInfo = tooltip.Value.TooltipInfo; - frozenActor.TooltipOwner = tooltip.Value.Owner; + frozenActor.TooltipInfo = tooltip.TooltipInfo; + frozenActor.TooltipOwner = tooltip.Owner; } } diff --git a/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs b/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs index 1f24e02b94..53d950cddd 100644 --- a/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs +++ b/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs @@ -8,6 +8,7 @@ */ #endregion +using System.Collections.Generic; using System.Linq; using OpenRA.Network; using OpenRA.Traits; @@ -22,16 +23,19 @@ namespace OpenRA.Mods.Common.Traits public void CreatePlayers(World w) { var players = new MapPlayers(w.Map.PlayerDefinitions).Players; + var worldPlayers = new List(); // Create the unplayable map players -- neutral, shellmap, scripted, etc. foreach (var kv in players.Where(p => !p.Value.Playable)) { var player = new Player(w, null, kv.Value); - w.AddPlayer(player); + worldPlayers.Add(player); if (kv.Value.OwnsWorld) w.WorldActor.Owner = player; } + Player localPlayer = null; + // Create the regular playable players. foreach (var kv in w.LobbyInfo.Slots) { @@ -40,22 +44,24 @@ namespace OpenRA.Mods.Common.Traits continue; var player = new Player(w, client, players[kv.Value.PlayerReference]); - w.AddPlayer(player); + worldPlayers.Add(player); if (client.Index == Game.LocalClientId) - w.SetLocalPlayer(player.InternalName); + localPlayer = player; } // Create a player that is allied with everyone for shared observer shroud. - w.AddPlayer(new Player(w, null, new PlayerReference + worldPlayers.Add(new Player(w, null, new PlayerReference { Name = "Everyone", NonCombatant = true, Spectating = true, Faction = "Random", - Allies = w.Players.Where(p => !p.NonCombatant && p.Playable).Select(p => p.InternalName).ToArray() + Allies = worldPlayers.Where(p => !p.NonCombatant && p.Playable).Select(p => p.InternalName).ToArray() })); + w.SetPlayers(worldPlayers, localPlayer); + foreach (var p in w.Players) foreach (var q in w.Players) if (!p.Stances.ContainsKey(q)) diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs index fe6143e911..e6cb7d33be 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs @@ -164,10 +164,7 @@ namespace OpenRA.Mods.Common.Traits var index = int.Parse(name.Substring(5)); if (index >= newCount) - { Players.Players.Remove(name); - worldRenderer.World.Players.RemoveAll(pp => pp.InternalName == name); - } } for (var index = 0; index < newCount; index++)