using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net; using IjwFramework.Types; using System.Net.Sockets; namespace OpenRa.Game { class OrderManager { BinaryWriter savingReplay; List players; int frameNumber = 0; public int FrameNumber { get { return frameNumber; } } public OrderManager( IEnumerable players ) { this.players = players.ToList(); } public OrderManager( IEnumerable players, string replayFilename ) : this( players ) { savingReplay = new BinaryWriter( new FileStream( replayFilename, FileMode.Create ) ); } public bool Tick() { var localOrders = Game.controller.GetRecentOrders(); if( localOrders == null ) return false; foreach( var p in players ) p.SendLocalOrders( frameNumber, localOrders ); if( savingReplay != null ) savingReplay.Write( frameNumber ); var allOrders = players.SelectMany(p => p.OrdersForFrame(frameNumber)).OrderBy(o => o.Player.Index); foreach (var order in allOrders) { UnitOrders.ProcessOrder(order); if (savingReplay != null) savingReplay.Write(order.Serialize()); } ++frameNumber; // sanity check on the framenumber. This is 2^31 frames maximum, or multiple *years* at 40ms/frame. if( ( frameNumber & 0x80000000 ) != 0 ) throw new InvalidOperationException( "(OrderManager) Frame number too large" ); return true; } } interface OrderSource { void SendLocalOrders( int localFrame, List localOrders ); List OrdersForFrame( int currentFrame ); } class LocalOrderSource : OrderSource { Dictionary> orders = new Dictionary>(); public List OrdersForFrame( int currentFrame ) { // TODO: prune `orders` based on currentFrame. return orders[ currentFrame ]; } public void SendLocalOrders( int localFrame, List localOrders ) { orders[ localFrame ] = localOrders; } } class ReplayOrderSource : OrderSource { BinaryReader replayReader; public ReplayOrderSource( string replayFilename ) { replayReader = new BinaryReader( File.Open( replayFilename, FileMode.Open ) ); replayReader.ReadUInt32(); } public void SendLocalOrders( int localFrame, List localOrders ) { } public List OrdersForFrame( int frameNumber ) { var ret = new List(); while( true ) { try { var first = replayReader.ReadUInt32(); var order = Order.Deserialize( replayReader, first ); if( order == null ) { if( (uint)frameNumber + 1 != first ) throw new NotImplementedException(); return ret; } ret.Add( order ); } catch( EndOfStreamException ) { return ret; } } } } class NetworkOrderSource : OrderSource { int nextLocalOrderFrame = 0; TcpClient socket; BinaryReader reader; public NetworkOrderSource( TcpClient socket ) { this.socket = socket; reader = new BinaryReader( socket.GetStream() ); var nextFrameId = System.BitConverter.GetBytes( nextLocalOrderFrame ); socket.GetStream().Write( nextFrameId, 0, nextFrameId.Length ); } public List OrdersForFrame( int currentFrame ) { if( currentFrame == 0 ) { var firstFrameNum = reader.ReadInt32(); if( firstFrameNum != 0 ) throw new InvalidOperationException( "Wrong frame number at start of stream" ); } var ret = new List(); while( true ) { var first = reader.ReadUInt32(); if( first == currentFrame + 1 ) return ret; ret.Add( Order.Deserialize( reader, first ) ); } } public void SendLocalOrders( int localFrame, List localOrders ) { if( nextLocalOrderFrame != localFrame ) throw new InvalidOperationException( "Attempted time-travel in NetworkOrderSource.SendLocalOrders()" ); foreach( var order in localOrders ) { var bytes = order.Serialize(); socket.GetStream().Write( bytes, 0, bytes.Length ); } ++nextLocalOrderFrame; var nextFrameId = System.BitConverter.GetBytes( nextLocalOrderFrame ); socket.GetStream().Write( nextFrameId, 0, nextFrameId.Length ); } } }