context: #region Copyright & License Information
  /*
   * Copyright 2007-2015 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. For more information,
   * see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.Linq;
  using OpenRA.Primitives;
  
  namespace OpenRA.Network
  {
  	public sealed class OrderManager : IDisposable
  	{
  		static readonly IEnumerable<Session.Client> NoClients = new Session.Client[] { };
  
  		readonly SyncReport syncReport;
  		readonly FrameData frameData = new FrameData();
  
  		public Session LobbyInfo = new Session();
  		public Session.Client LocalClient { get { return LobbyInfo.ClientWithIndex(Connection.LocalClientId); } }
  		public World World;
  
  		public readonly string Host;
  		public readonly int Port;
  		public readonly string Password = "";
  
  		public string ServerError = "Server is not responding";
  		public bool AuthenticationFailed = false;
  
  		public int NetFrameNumber { get; private set; }
  		public int LocalFrameNumber;
  		public int FramesAhead = 0;
  
  		public int LastTickTime = Game.RunTime;
  
  		public bool GameStarted { get { return NetFrameNumber != 0; } }
  		public IConnection Connection { get; private set; }
  
  		public readonly int SyncHeaderSize = 9;
  
  		List<Order> localOrders = new List<Order>();
  
  		List<ChatLine> chatCache = new List<ChatLine>();
  
  		public readonly ReadOnlyList<ChatLine> ChatCache;
  
  		bool disposed;
  
  		static void OutOfSync(int frame)
  		{
  			throw new InvalidOperationException("Out of sync in frame {0}.\n Compare syncreport.log with other players.".F(frame));
  		}
  
  		static void OutOfSync(int frame, string blame)
  		{
  			throw new InvalidOperationException("Out of sync in frame {0}: Blame {1}.\n Compare syncreport.log with other players.".F(frame, blame));
  		}
  
  		public void StartGame()
  		{
  			if (GameStarted) return;
  
  			NetFrameNumber = 1;
  			for (var i = NetFrameNumber; i <= FramesAhead; i++)
  				Connection.Send(i, new List<byte[]>());
  		}
  
  		public OrderManager(string host, int port, string password, IConnection conn)
  		{
  			Host = host;
  			Port = port;
  			Password = password;
  			Connection = conn;
  			syncReport = new SyncReport(this);
  			ChatCache = new ReadOnlyList<ChatLine>(chatCache);
  			AddChatLine += CacheChatLine;
  		}
  
  		public void IssueOrders(Order[] orders)
  		{
  			foreach (var order in orders)
  				IssueOrder(order);
  		}
  
  		public void IssueOrder(Order order)
  		{
  			localOrders.Add(order);
  		}
  
  		public Action<Color, string, string> AddChatLine = (c, n, s) => { };
  		void CacheChatLine(Color color, string name, string text)
  		{
  			chatCache.Add(new ChatLine(color, name, text));
  		}
  
  		public void TickImmediate()
  		{
  			var immediateOrders = localOrders.Where(o => o.IsImmediate).ToList();
  			if (immediateOrders.Count != 0)
  				Connection.SendImmediate(immediateOrders.Select(o => o.Serialize()).ToList());
  			localOrders.RemoveAll(o => o.IsImmediate);
  
  			var immediatePackets = new List<Pair<int, byte[]>>();
  
  			Connection.Receive(
  				(clientId, packet) =>
  				{
  					var frame = BitConverter.ToInt32(packet, 0);
  					if (packet.Length == 5 && packet[4] == 0xBF)
  						frameData.ClientQuit(clientId, frame);
  					else if (packet.Length >= 5 && packet[4] == 0x65)
  						CheckSync(packet);
  					else if (frame == 0)
  						immediatePackets.Add(Pair.New(clientId, packet));
  					else
  						frameData.AddFrameOrders(clientId, frame, packet);
  				});
  
  			foreach (var p in immediatePackets)
  			{
  				foreach (var o in p.Second.ToOrderList(World))
  				{
  					UnitOrders.ProcessOrder(this, World, p.First, o);
  
  					// A mod switch or other event has pulled the ground from beneath us
  					if (disposed)
  						return;
  				}
  			}
  		}
  
  		Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
  
  		void CheckSync(byte[] packet)
  		{
  			var frame = BitConverter.ToInt32(packet, 0);
  			byte[] existingSync;
  			if (syncForFrame.TryGetValue(frame, out existingSync))
  			{
  				if (packet.Length != existingSync.Length)
  				{
  					syncReport.DumpSyncReport(frame);
  					OutOfSync(frame);
  				}
  				else
  				{
  					for (var i = 0; i < packet.Length; i++)
  					{
  						if (packet[i] != existingSync[i])
  						{
  							syncReport.DumpSyncReport(frame);
  
  							if (i < SyncHeaderSize)
  								OutOfSync(frame, "Tick");
  							else
  								OutOfSync(frame, (i - SyncHeaderSize) / 4);
  						}
  					}
  				}
  			}
  			else
  				syncForFrame.Add(frame, packet);
  		}
  
  		void OutOfSync(int frame, int index)
  		{
  			var orders = frameData.OrdersForFrame(World, frame);
  
  			// Invalid index
  			if (index >= orders.Count())
  				OutOfSync(frame);
  
  			throw new InvalidOperationException("Out of sync in frame {0}.\n {1}\n Compare syncreport.log with other players.".F(frame, orders.ElementAt(index).Order.ToString()));
  		}
  
  		public bool IsReadyForNextFrame
  		{
  			get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame(NetFrameNumber); }
  		}
  
  		public IEnumerable<Session.Client> GetClientsNotReadyForNextFrame
  		{
  			get
  			{
  				return NetFrameNumber >= 1
  					? frameData.ClientsNotReadyForFrame(NetFrameNumber)
  						.Select(a => LobbyInfo.ClientWithIndex(a))
  					: NoClients;
  			}
  		}
  
  		public void Tick()
  		{
  			if (!IsReadyForNextFrame)
  				throw new InvalidOperationException();
  
  			Connection.Send(NetFrameNumber + FramesAhead, localOrders.Select(o => o.Serialize()).ToList());
  			localOrders.Clear();
  
  			var sync = new List<int>();
  			sync.Add(World.SyncHash());
  
  			foreach (var order in frameData.OrdersForFrame(World, NetFrameNumber))
  			{
  				UnitOrders.ProcessOrder(this, World, order.Client, order.Order);
  				sync.Add(World.SyncHash());
  			}
  
  
gt: 			var ss = sync.SerializeSync();