diff --git a/OpenRA.Game/Map/MapPlayers.cs b/OpenRA.Game/Map/MapPlayers.cs index 476bdd6..af3a857 100644 --- a/OpenRA.Game/Map/MapPlayers.cs +++ b/OpenRA.Game/Map/MapPlayers.cs @@ -22,15 +22,16 @@ namespace OpenRA // The "Everyone" player for spectators is created at runtime, // reducing the available player count for maps by 1. public const int MaximumPlayerCount = 63; - public readonly Dictionary Players; + public readonly OrderedDictionary Players; public MapPlayers() : this([]) { } public MapPlayers(IEnumerable playerDefinitions) { - Players = playerDefinitions.Select(pr => new PlayerReference(new MiniYaml(pr.Key, pr.Value.Nodes))) - .ToDictionary(player => player.Name); + Players = new OrderedDictionary(playerDefinitions + .Select(pr => new PlayerReference(new MiniYaml(pr.Key, pr.Value.Nodes))) + .Select(player => KeyValuePair.Create(player.Name, player))); } public MapPlayers(Ruleset rules, int playerCount) @@ -38,7 +39,7 @@ namespace OpenRA var firstFaction = rules.Actors[SystemActors.World].TraitInfos() .First(f => f.Selectable).InternalName; - Players = new Dictionary + Players = new OrderedDictionary { { "Neutral", new PlayerReference diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs index faefc33..55f79b5 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs @@ -307,7 +307,7 @@ namespace OpenRA.Mods.Common.Traits { var newCount = previews.Count(p => p.Info.Name == PlayerSpawnName); var playersChanged = false; - foreach (var kv in Players.Players) + foreach (var kv in Players.Players.ToList()) { if (!kv.Key.StartsWith("Multi", StringComparison.Ordinal)) continue; diff --git a/OpenRA.Test/OpenRA.Game/MapPlayersTest.cs b/OpenRA.Test/OpenRA.Game/MapPlayersTest.cs new file mode 100644 index 0000000..c2076af --- /dev/null +++ b/OpenRA.Test/OpenRA.Game/MapPlayersTest.cs @@ -0,0 +1,37 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using NUnit.Framework; + +namespace OpenRA.Test +{ + [TestFixture] + sealed class MapPlayersTest + { + [TestCase(TestName = "Players keep their definition order when removed and re-added")] + public void PlayersKeepDefinitionOrder() + { + var players = new MapPlayers(); + string[] names = ["Neutral", "Creeps", "Multi0", "Multi1", "Multi2"]; + foreach (var name in names) + players.Players.Add(name, null); + + // The map editor removes and re-adds all spawn players when updating multiple spawns at once. + for (var i = 2; i < names.Length; i++) + players.Players.Remove(names[i]); + + for (var i = 2; i < names.Length; i++) + players.Players.Add(names[i], null); + + Assert.That(players.Players.Keys, Is.EqualTo(names)); + } + } +}