context: #region Copyright & License Information
  /*
   * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
   * 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 System;
  using System.Collections.Generic;
  using System.Linq;
  using OpenRA.Effects;
  using OpenRA.FileFormats;
  using OpenRA.Graphics;
  using OpenRA.Network;
  using OpenRA.Orders;
  using OpenRA.Primitives;
  using OpenRA.Support;
  using OpenRA.Traits;
  
  namespace OpenRA
  {
  	public enum WorldType { Regular, Shellmap, Editor }
  
  	public sealed class World : IDisposable
  	{
  		internal readonly TraitDictionary TraitDict = new TraitDictionary();
  		readonly SortedDictionary<uint, Actor> actors = new SortedDictionary<uint, Actor>();
  		readonly List<IEffect> effects = new List<IEffect>();
  		readonly List<IEffect> unpartitionedEffects = new List<IEffect>();
  		readonly List<ISync> syncedEffects = new List<ISync>();
  
  		readonly Queue<Action<World>> frameEndActions = new Queue<Action<World>>();
  
  		public int Timestep;
  
  		internal readonly OrderManager OrderManager;
  		public Session LobbyInfo { get { return OrderManager.LobbyInfo; } }
  
  		public readonly MersenneTwister SharedRandom;
  		public readonly MersenneTwister LocalRandom;
  		public readonly IModelCache ModelCache;
  		public LongBitSet<PlayerBitMask> AllPlayersMask = default(LongBitSet<PlayerBitMask>);
  		public readonly LongBitSet<PlayerBitMask> NoPlayersMask = default(LongBitSet<PlayerBitMask>);
  
  		public Player[] Players = new Player[0];
  
  		public event Action<Player> RenderPlayerChanged;
  
  		public void SetPlayers(IEnumerable<Player> players, Player localPlayer)
  		{
  			if (Players.Length > 0)
  				throw new InvalidOperationException("Players are fixed once they have been set.");
  			Players = players.ToArray();
  			SetLocalPlayer(localPlayer);
  		}
  
  		public Player LocalPlayer { get; private set; }
  
  		public event Action GameOver = () => { };
  		public bool IsGameOver { get; private set; }
  		public void EndGame()
  		{
  			if (!IsGameOver)
  			{
  				IsGameOver = true;
  
  				foreach (var t in WorldActor.TraitsImplementing<IGameOver>())
  					t.GameOver(this);
  				gameInfo.FinalGameTick = WorldTick;
  				GameOver();
  			}
  		}
  
  		Player renderPlayer;
  		public Player RenderPlayer
  		{
  			get
  			{
  				return renderPlayer;
  			}
  
  			set
  			{
  				if (LocalPlayer == null || LocalPlayer.UnlockedRenderPlayer)
  				{
  					renderPlayer = value;
  
  					RenderPlayerChanged?.Invoke(value);
  				}
  			}
  		}
  
  		public bool FogObscures(Actor a) { return RenderPlayer != null && !a.CanBeViewedByPlayer(RenderPlayer); }
  		public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
  		public bool FogObscures(WPos pos) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(pos); }
  		public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
  		public bool ShroudObscures(MPos uv) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(uv); }
  		public bool ShroudObscures(WPos pos) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(pos); }
  		public bool ShroudObscures(PPos uv) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(uv); }
  
  		public bool IsReplay
  		{
  			get { return OrderManager.Connection is ReplayConnection; }
  		}
  
  		public bool IsLoadingGameSave
  		{
  			get { return OrderManager.NetFrameNumber <= OrderManager.GameSaveLastFrame; }
  		}
  
  		public int GameSaveLoadingPercentage
  		{
  			get { return OrderManager.NetFrameNumber * 100 / OrderManager.GameSaveLastFrame; }
  		}
  
  		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 = localPlayer;
  
  			// Set the property backing field directly
  			renderPlayer = LocalPlayer;
  		}
  
  		public readonly Actor WorldActor;
  
  		public readonly Map Map;
  
  		public readonly IActorMap ActorMap;
  		public readonly ScreenMap ScreenMap;
  		public readonly WorldType Type;
  
  		public readonly IValidateOrder[] OrderValidators;
  
  		readonly GameInformation gameInfo;
  
  		// Hide the OrderManager from mod code
  		public void IssueOrder(Order o) { OrderManager.IssueOrder(o); }
  
  		IOrderGenerator orderGenerator;
  		public IOrderGenerator OrderGenerator
  		{
  			get
  			{
  				return orderGenerator;
  			}
  
  			set
  			{
  				Sync.AssertUnsynced("The current order generator may not be changed from synced code");
  				orderGenerator?.Deactivate();
  
  				orderGenerator = value;
  			}
  		}
  
  		public readonly ISelection Selection;
  
  		public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); }
  
  		public bool ToggleInputMode<T>() where T : IOrderGenerator, new()
  		{
  			if (OrderGenerator is T)
  			{
  				CancelInputMode();
  				return false;
  			}
  			else
  			{
  				OrderGenerator = new T();
  				return true;
  			}
  		}
  
  		public bool RulesContainTemporaryBlocker { get; private set; }
  
  		bool wasLoadingGameSave;
  
  		internal World(ModData modData, Map map, OrderManager orderManager, WorldType type)
  		{
  			Type = type;
  			OrderManager = orderManager;
  			orderGenerator = new UnitOrderGenerator();
  			Map = map;
  			Timestep = orderManager.LobbyInfo.GlobalSettings.Timestep;
  			SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
  			LocalRandom = new MersenneTwister();
  
  			ModelCache = modData.ModelSequenceLoader.CacheModels(map, modData, map.Rules.ModelSequences);
  
  			var worldActorType = type == WorldType.Editor ? "EditorWorld" : "World";
  			WorldActor = CreateActor(worldActorType, new TypeDictionary());
  			ActorMap = WorldActor.Trait<IActorMap>();
  			ScreenMap = WorldActor.Trait<ScreenMap>();
  			Selection = WorldActor.Trait<ISelection>();
  			OrderValidators = WorldActor.TraitsImplementing<IValidateOrder>().ToArray();
  
  			// Reset mask
  			LongBitSet<PlayerBitMask>.Reset();
  
  			// Add players
  			foreach (var cmp in WorldActor.TraitsImplementing<ICreatePlayers>())
  				cmp.CreatePlayers(this);
  
  			// Set defaults for any unset stances
  			foreach (var p in Players)
  			{
  				if (!p.Spectating)
  					AllPlayersMask = AllPlayersMask.Union(p.PlayerMask);
  
  				foreach (var q in Players)
  				{
  					SetUpPlayerMask(p, q);
  
  					if (!p.Stances.ContainsKey(q))
  						p.Stances[q] = Stance.Neutral;
  				}
  			}
  
  			Game.Sound.SoundVolumeModifier = 1.0f;
  
  			gameInfo = new GameInformation
  			{
  				Mod = Game.ModData.Manifest.Id,
  				Version = Game.ModData.Manifest.Metadata.Version,
  
  				MapUid = Map.Uid,
  				MapTitle = Map.Title
  			};
  
  			RulesContainTemporaryBlocker = map.Rules.Actors.Any(a => a.Value.HasTraitInfo<ITemporaryBlockerInfo>());
  		}
  
  		void SetUpPlayerMask(Player p, Player q)
  		{
  			if (q.Spectating)
  				return;
  
  			var bitSet = q.PlayerMask;
  
  			switch (p.Stances[q])
  			{
  				case Stance.Enemy:
  				case Stance.Neutral:
  					p.EnemyPlayersMask = p.EnemyPlayersMask.Union(bitSet);
  					break;
  				case Stance.Ally:
  					p.AlliedPlayersMask = p.AlliedPlayersMask.Union(bitSet);
  					break;
  			}
  		}
  
  		public void AddToMaps(Actor self, IOccupySpace ios)
  		{
  			ActorMap.AddInfluence(self, ios);
  			ActorMap.AddPosition(self, ios);
  			ScreenMap.AddOrUpdate(self);
  		}
  
  		public void UpdateMaps(Actor self, IOccupySpace ios)
  		{
  			if (!self.IsInWorld)
  				return;
  
  			ScreenMap.AddOrUpdate(self);
  			ActorMap.UpdatePosition(self, ios);
  		}
  
  		public void RemoveFromMaps(Actor self, IOccupySpace ios)
  		{
  			ActorMap.RemoveInfluence(self, ios);
  			ActorMap.RemovePosition(self, ios);
  			ScreenMap.Remove(self);
  		}
  
  		public void LoadComplete(WorldRenderer wr)
  		{
  			if (IsLoadingGameSave)
  			{
  				wasLoadingGameSave = true;
  				Game.Sound.DisableAllSounds = true;
  				foreach (var nsr in WorldActor.TraitsImplementing<INotifyGameLoading>())
  					nsr.GameLoading(this);
  			}
  
  			// ScreenMap must be initialized before anything else
  			using (new PerfTimer("ScreenMap.WorldLoaded"))
  				ScreenMap.WorldLoaded(this, wr);
  
  			foreach (var iwl in WorldActor.TraitsImplementing<IWorldLoaded>())
  			{
  				// These have already been initialized
  				if (iwl == ScreenMap)
  					continue;
  
  				using (new PerfTimer(iwl.GetType().Name + ".WorldLoaded"))
  					iwl.WorldLoaded(this, wr);
  			}
  
  			foreach (var p in Players)
  				foreach (var iwl in p.PlayerActor.TraitsImplementing<IWorldLoaded>())
  					using (new PerfTimer(iwl.GetType().Name + ".WorldLoaded"))
  						iwl.WorldLoaded(this, wr);
  
  			var assignSpawnLocations = WorldActor.TraitOrDefault<IAssignSpawnPoints>();
  			gameInfo.StartTimeUtc = DateTime.UtcNow;
  			foreach (var player in Players)
  				gameInfo.AddPlayer(player, OrderManager.LobbyInfo);
  
  			var echo = OrderManager.Connection as EchoConnection;
  			var rc = echo != null ? echo.Recorder : null;
  
  
gt: 			if (rc != null)