diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index ecbfb8efa8..f8a71a38a2 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -68,7 +68,12 @@ namespace OpenRA lastConnectionState = ConnectionState.PreConnecting; ConnectionStateChanged(); - orderManager = new OrderManager(new NetworkConnection(host, port), ChooseReplayFilename()); + var replayFilename = ChooseReplayFilename(); + string path = Path.Combine( Game.SupportDir, "Replays" ); + if( !Directory.Exists( path ) ) Directory.CreateDirectory( path ); + var replayFile = File.Create( Path.Combine( path, replayFilename ) ); + + orderManager = new OrderManager( new ReplayRecorderConnection( new NetworkConnection( host, port ), replayFile ) ); } static string ChooseReplayFilename() @@ -117,7 +122,7 @@ namespace OpenRA lastTime += Settings.Game.Timestep; Widget.DoTick(world); Sound.Tick(); - orderManager.TickImmediate(world); + Sync.CheckSyncUnchanged( world, () => { orderManager.TickImmediate( world ); } ); var isNetTick = LocalTick % NetTickScale == 0; diff --git a/OpenRA.Game/Network/Connection.cs b/OpenRA.Game/Network/Connection.cs index ea39217c2d..988e4bfc12 100755 --- a/OpenRA.Game/Network/Connection.cs +++ b/OpenRA.Game/Network/Connection.cs @@ -26,7 +26,7 @@ namespace OpenRA.Network Connected, } - interface IConnection + interface IConnection : IDisposable { int LocalClientId { get; } ConnectionState ConnectionState { get; } @@ -73,9 +73,11 @@ namespace OpenRA.Network foreach( var p in packets ) packetFn( p.FromClient, p.Data ); } + + public virtual void Dispose() { } } - class NetworkConnection : EchoConnection, IDisposable + class NetworkConnection : EchoConnection { TcpClient socket; int clientId; @@ -145,7 +147,7 @@ namespace OpenRA.Network bool disposed = false; - public void Dispose () + public override void Dispose () { if (disposed) return; disposed = true; @@ -160,48 +162,4 @@ namespace OpenRA.Network ~NetworkConnection() { Dispose(); } } - - class ReplayConnection : IConnection - { - //uint nextFrame = 1; - FileStream replayStream; - - public ReplayConnection( string replayFilename ) - { - replayStream = File.OpenRead( replayFilename ); - } - - public int LocalClientId - { - get { return 0; } - } - - public ConnectionState ConnectionState - { - get { return ConnectionState.Connected; } - } - - public void Send( byte[] packet ) - { - // do nothing; ignore locally generated orders - } - - public void Receive( Action packetFn ) - { - if( replayStream == null ) return; - - var reader = new BinaryReader( replayStream ); - while( replayStream.Position < replayStream.Length ) - { - var client = reader.ReadInt32(); - var packetLen = reader.ReadInt32(); - var packet = reader.ReadBytes( packetLen ); - packetFn( client, packet ); - - if( !Game.orderManager.GameStarted ) - return; - } - replayStream = null; - } - } } diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs index a0908a5357..1adcb7e467 100755 --- a/OpenRA.Game/Network/OrderManager.cs +++ b/OpenRA.Game/Network/OrderManager.cs @@ -35,8 +35,6 @@ namespace OpenRA.Network new Dictionary>(); List localOrders = new List(); - FileStream replaySaveFile; - public void StartGame() { if (GameStarted) return; @@ -51,14 +49,6 @@ namespace OpenRA.Network Connection = conn; } - public OrderManager( IConnection conn, string replayFilename ) - : this( conn ) - { - string path = Game.SupportDir + "Replays" + Path.DirectorySeparatorChar; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - replaySaveFile = File.Create( path + replayFilename ); - } - public void IssueOrders( Order[] orders ) { foreach( var order in orders ) @@ -94,11 +84,8 @@ namespace OpenRA.Network } ); foreach( var p in immediatePackets ) - { foreach( var o in p.Second.ToOrderList( world ) ) UnitOrders.ProcessOrder( world, p.First, o ); - WriteImmediateToReplay( immediatePackets ); - } } Dictionary syncForFrame = new Dictionary(); @@ -189,7 +176,6 @@ namespace OpenRA.Network var ss = sync.SerializeSync( FrameNumber ); Connection.Send( ss ); - WriteToReplay( frameData, ss ); syncReport.UpdateSyncReport(); @@ -198,43 +184,12 @@ namespace OpenRA.Network ++FrameNumber; } - void WriteToReplay( Dictionary frameData, byte[] syncData ) - { - if( replaySaveFile == null ) return; - - foreach( var f in frameData ) - { - replaySaveFile.Write( BitConverter.GetBytes( f.Key ) ); - replaySaveFile.Write( BitConverter.GetBytes( f.Value.Length ) ); - replaySaveFile.Write( f.Value ); - } - replaySaveFile.Write( BitConverter.GetBytes( (int)0 ) ); - replaySaveFile.Write( BitConverter.GetBytes( (int)syncData.Length ) ); - replaySaveFile.Write( syncData ); - } - - void WriteImmediateToReplay( List> immediatePackets ) - { - if( replaySaveFile == null ) return; - - foreach( var i in immediatePackets ) - { - replaySaveFile.Write( BitConverter.GetBytes( i.First ) ); - replaySaveFile.Write( BitConverter.GetBytes( i.Second.Length ) ); - replaySaveFile.Write( i.Second ); - } - } - bool disposed; public void Dispose() { if (disposed) return; - if (replaySaveFile != null) - replaySaveFile.Dispose(); - - var disposableConnection = Connection as IDisposable; - if (disposableConnection != null) disposableConnection.Dispose(); + Connection.Dispose(); disposed = true; GC.SuppressFinalize(this); diff --git a/OpenRA.Game/Network/ReplayConnection.cs b/OpenRA.Game/Network/ReplayConnection.cs new file mode 100755 index 0000000000..1e42f528ad --- /dev/null +++ b/OpenRA.Game/Network/ReplayConnection.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace OpenRA.Network +{ + class ReplayConnection : IConnection + { + //uint nextFrame = 1; + FileStream replayStream; + + public ReplayConnection( string replayFilename ) + { + replayStream = File.OpenRead( replayFilename ); + } + + public int LocalClientId + { + get { return 0; } + } + + public ConnectionState ConnectionState + { + get { return ConnectionState.Connected; } + } + + public void Send( byte[] packet ) + { + // do nothing; ignore locally generated orders + } + + public void Receive( Action packetFn ) + { + if( replayStream == null ) return; + + var reader = new BinaryReader( replayStream ); + while( replayStream.Position < replayStream.Length ) + { + var client = reader.ReadInt32(); + var packetLen = reader.ReadInt32(); + var packet = reader.ReadBytes( packetLen ); + packetFn( client, packet ); + + if( !Game.orderManager.GameStarted ) + return; + } + replayStream = null; + } + + public void Dispose() { } + } + + class ReplayRecorderConnection : IConnection + { + IConnection inner; + FileStream replayFile; + BinaryWriter writer; + + public ReplayRecorderConnection( IConnection inner, FileStream replayFile ) + { + this.inner = inner; + this.replayFile = replayFile; + this.writer = new BinaryWriter( replayFile ); + } + + public int LocalClientId { get { return inner.LocalClientId; } } + public ConnectionState ConnectionState { get { return inner.ConnectionState; } } + + public void Send( byte[] packet ) + { + inner.Send( packet ); + } + + public void Receive( Action packetFn ) + { + inner.Receive( ( client, data ) => + { + writer.Write( client ); + writer.Write( data.Length ); + writer.Write( data ); + packetFn( client, data ); + } ); + } + + bool disposed; + + public void Dispose() + { + if( disposed ) + return; + + writer.Close(); + disposed = true; + } + + ~ReplayRecorderConnection() + { + Dispose(); + } + } +} diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 5de01b3dca..60fd31ea8a 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -3,7 +3,7 @@ Debug x86 - 9.0.21022 + 9.0.30729 2.0 {0DFB103F-2962-400F-8C6D-E2C28CCBA633} WinExe @@ -222,6 +222,7 @@ + diff --git a/OpenRA.Game/Sync.cs b/OpenRA.Game/Sync.cs index efe072cefd..63dc9f0ac4 100755 --- a/OpenRA.Game/Sync.cs +++ b/OpenRA.Game/Sync.cs @@ -140,6 +140,7 @@ namespace OpenRA public static T CheckSyncUnchanged( World world, Func fn ) { int sync = world.SyncHash(); + bool prevInUnsyncedCode = inUnsyncedCode; inUnsyncedCode = true; try { @@ -147,9 +148,9 @@ namespace OpenRA } finally { - inUnsyncedCode = false; + inUnsyncedCode = prevInUnsyncedCode; if( sync != world.SyncHash() ) - throw new InvalidOperationException( "Desync in DispatchMouseInput" ); + throw new InvalidOperationException( "CheckSyncUnchanged: sync-changing code may not run here" ); } }