Files
OpenRA/OpenRA.Game/Network/ReplayRecorderConnection.cs
Pavlos Touboulidis de0a5ebd43 Improve replay metadata and the replay browser
List of changes:

* Better and more filters with new layout, for both mods.

* Rename/Delete/Detele all functionality.

* Simplified ReplayMetadata class considerably by introducing a new
GameInformation data object. The new GameInformation class contains
more information than previously available so the new solution is not
compatible with old replays, meaning it can't read old replays.

* Better and cleaner game information gathering in order to be written
at the end of the replay file.

* Revert changes to ReplayConnection, no longer necessary.

* Better exception message on missing sprites and fonts.

* New "SpawnOccupant" class that holds all the information needed by the
MapPreviewWidget to visualize a spawn point. It was using Session.Client
before and it was necessary to separate it to be able to show information
not available at lobby time.

* Fix keyboard focus UI bug when closing a window would not remove focus.
2014-05-22 21:54:14 +03:00

124 lines
3.0 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2013 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.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Widgets;
namespace OpenRA.Network
{
class ReplayRecorderConnection : IConnection
{
public ReplayMetadata Metadata;
IConnection inner;
BinaryWriter writer;
Func<string> chooseFilename;
MemoryStream preStartBuffer = new MemoryStream();
public ReplayRecorderConnection(IConnection inner, Func<string> chooseFilename)
{
this.chooseFilename = chooseFilename;
this.inner = inner;
writer = new BinaryWriter(preStartBuffer);
}
void StartSavingReplay(byte[] initialContent)
{
var filename = chooseFilename();
var mod = Game.modData.Manifest.Mod;
var dir = new[] { Platform.SupportDir, "Replays", mod.Id, mod.Version }.Aggregate(Path.Combine);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
FileStream file = null;
var id = -1;
while (file == null)
{
var fullFilename = Path.Combine(dir, id < 0 ? "{0}.rep".F(filename) : "{0}-{1}.rep".F(filename, id));
id++;
try
{
file = File.Create(fullFilename);
}
catch (IOException) { }
}
file.Write(initialContent);
this.writer = new BinaryWriter(file);
}
public int LocalClientId { get { return inner.LocalClientId; } }
public ConnectionState ConnectionState { get { return inner.ConnectionState; } }
public void Send(int frame, List<byte[]> orders) { inner.Send(frame, orders); }
public void SendImmediate(List<byte[]> orders) { inner.SendImmediate(orders); }
public void SendSync(int frame, byte[] syncData) { inner.SendSync(frame, syncData); }
public void Receive(Action<int, byte[]> packetFn)
{
inner.Receive((client, data) =>
{
if (preStartBuffer != null && IsGameStart(data))
{
writer.Flush();
var preStartData = preStartBuffer.ToArray();
preStartBuffer = null;
StartSavingReplay(preStartData);
}
writer.Write(client);
writer.Write(data.Length);
writer.Write(data);
packetFn(client, data);
});
}
bool IsGameStart(byte[] data)
{
if (data.Length == 5 && data[4] == 0xbf)
return false;
if (data.Length >= 5 && data[4] == 0x65)
return false;
var frame = BitConverter.ToInt32(data, 0);
return frame == 0 && data.ToOrderList(null).Any(o => o.OrderString == "StartGame");
}
bool disposed;
public void Dispose()
{
if (disposed)
return;
if (Metadata != null)
{
if (Metadata.GameInfo != null)
Metadata.GameInfo.EndTimeUtc = DateTime.UtcNow;
Metadata.Write(writer);
}
writer.Close();
inner.Dispose();
disposed = true;
}
~ReplayRecorderConnection()
{
Dispose();
}
}
}