using CommonLang.IO; using System; using System.Collections.Generic; using System.IO; namespace CommonAI.Zone.Replay { public class BattleRecordData { public const string VERSION_1_0 = "1.0.0"; public readonly static uint HEAD = BitConverter.ToUInt32(new byte[] { (byte)'B', (byte)'R', (byte)'E', (byte)'C' }, 0); public string Name; public string Desc; public int SceneID; public DateTime Time = DateTime.Now; public string Version = VERSION_1_0; public string ResVersion; public LinkedList Events = new LinkedList(); public class EventLog { public long PassTimeMS; public Event EventData; } public static void EncodeHead(OutputStream output, BattleRecordData data) { output.PutU32(BattleRecordData.HEAD); output.PutUTF(data.Name); output.PutUTF(data.Desc); output.PutS64(data.Time.ToBinary()); output.PutUTF(data.Version); output.PutS32(data.SceneID); output.PutUTF(data.ResVersion); } public static bool DecodeHead(InputStream input, BattleRecordData data) { uint head = input.GetU32(); if (head == HEAD) { data.Name = input.GetUTF(); data.Desc = input.GetUTF(); data.Time = DateTime.FromBinary(input.GetS64()); data.Version = input.GetUTF(); data.SceneID = input.GetS32(); data.ResVersion = input.GetUTF(); return true; } return false; } public static void EncodeLog(TemplateManager templates, OutputStream output, EventLog log) { if (log.EventData is BattleMessage) { (log.EventData as BattleMessage).BeforeWrite(templates); } output.PutS64(log.PassTimeMS); output.PutExt(log.EventData); } public static void DecodeLog(TemplateManager templates, InputStream input, EventLog log) { log.PassTimeMS = input.GetS64(); log.EventData = (Event)input.GetExtAny(); if (log.EventData is BattleMessage) { (log.EventData as BattleMessage).EndRead(templates); } } public static void EncodeToStream(TemplateManager templates, Stream stream, BattleRecordData data) { OutputStream output = new OutputStream(stream, TemplateManager.MessageCodec); EncodeHead(output, data); foreach (BattleRecordData.EventLog log in data.Events) { EncodeLog(templates, output, log); } } public static bool DecodeFromStream(TemplateManager templates, Stream stream, out BattleRecordData ret) { InputStream input = new InputStream(stream, TemplateManager.MessageCodec); ret = new BattleRecordData(); if (DecodeHead(input, ret)) { while (stream.CanRead) { try { BattleRecordData.EventLog log = new BattleRecordData.EventLog(); DecodeLog(templates, input, log); ret.Events.AddLast(log); } catch (Exception err) { Console.WriteLine("UnitBooleanAttribute : " + templates.ToString() + ", catch: " + err); break; } } return true; } ret = null; return false; } public static byte[] ToBinary(TemplateManager templates, BattleRecordData data) { byte[] ret; using (MemoryStream ms = new MemoryStream()) { EncodeToStream(templates, ms, data); ret = new byte[ms.Length]; Array.Copy(ms.GetBuffer(), ret, ret.Length); } return ret; } public static BattleRecordData FromBinary(TemplateManager templates, byte[] data) { BattleRecordData ret = null; using (MemoryStream ms = new MemoryStream(data)) { DecodeFromStream(templates, ms, out ret); } return ret; } } public class BattleRecorder { public int SceneID { get; private set; } public string Name { get; set; } public string Desc { get; set; } public DateTime Time { get; set; } public string ResVersion { get; private set; } private bool mIsRunning = false; private OutputStream mStream; private MemoryStream mDefaultStream; private Stream mCurrentStream; private TemplateManager mTemplates; public BattleRecorder(TemplateManager templates, int sceneID, string name) { this.mTemplates = templates; this.SceneID = sceneID; this.Name = name; this.Desc = ""; this.Time = DateTime.Now; this.ResVersion = templates.ResourceVersion; this.mDefaultStream = new MemoryStream(); this.mStream = new OutputStream(mDefaultStream, TemplateManager.MessageCodec); SetOut(mDefaultStream); } public void SetOut(Stream stream) { mCurrentStream = stream; mStream.SetStream(stream); } public void Record(Event evt, long passTimeMS) { if (mIsRunning == false) { mIsRunning = true; BattleRecordData data = new BattleRecordData(); data.Name = this.Name; data.Desc = this.Desc; data.Time = this.Time; data.SceneID = this.SceneID; data.ResVersion = this.ResVersion; BattleRecordData.EncodeHead(mStream, data); } BattleRecordData.EventLog log = new BattleRecordData.EventLog(); log.PassTimeMS = passTimeMS; log.EventData = evt; BattleRecordData.EncodeLog(mTemplates, mStream, log); } public byte[] ToBinary() { if (mDefaultStream == mCurrentStream) { byte[] data = new byte[mDefaultStream.Position]; Array.Copy(mDefaultStream.GetBuffer(), data, data.Length); return data; } else if (mCurrentStream is MemoryStream) { byte[] data = new byte[mCurrentStream.Position]; Array.Copy((mCurrentStream as MemoryStream).GetBuffer(), data, data.Length); return data; } return null; } } public interface IBattleRecordListener { void onEventHandler(Event evt); } public class BattleRecordPlayer : IDisposable { public int SceneID { get; private set; } public string Name { get; private set; } public string Desc { get; private set; } public DateTime Time { get; private set; } public string Version { get; private set; } public string ResVersion { get; private set; } public TemplateManager Templates { get; private set; } public BattleRecordPlayer(TemplateManager templates, Stream data) { this.Templates = templates; this.mStream = data; this.mInput = new InputStream(data, TemplateManager.MessageCodec); BattleRecordData hd = new BattleRecordData(); if (BattleRecordData.DecodeHead(mInput, hd)) { this.Name = hd.Name; this.Desc = hd.Desc; this.Time = hd.Time; this.Version = hd.Version; this.SceneID = hd.SceneID; this.ResVersion = hd.ResVersion; } else { throw new Exception("Error - 数据不包含战报文件头 BattleRecordData.HEAD !"); } } public BattleRecordPlayer(TemplateManager templates, BattleRecordData data) { this.Templates = templates; this.mData = data; this.mLogs = new Queue(data.Events); { this.Name = data.Name; this.Desc = data.Desc; this.Time = data.Time; this.Version = data.Version; this.SceneID = data.SceneID; this.ResVersion = data.ResVersion; } } public void Dispose() { mData = null; mListener = null; Templates = null; mWaittingEvent = null; mLogs.Clear(); } public bool IsDone { get { return mIsDone; } } private IBattleRecordListener mListener; private BattleRecordData mData; private Queue mLogs; private Stream mStream; private InputStream mInput; private BattleRecordData.EventLog mWaittingEvent; private long mPassTime; private bool mIsDone = false; private bool mIsRunning = false; public void Start(IBattleRecordListener listener) { if (mIsDone) return; if (!mIsRunning) { this.mListener = listener; this.mIsDone = false; this.mPassTime = 0; this.mIsRunning = true; } } public void Update(int intervalMS) { if (mIsRunning) { mPassTime += intervalMS; while (true) { if (mWaittingEvent == null) { mWaittingEvent = PickNextLog(); } if (mWaittingEvent != null) { if (mWaittingEvent.PassTimeMS <= mPassTime) { mListener.onEventHandler(mWaittingEvent.EventData); mWaittingEvent = PickNextLog(); continue; } else { break; } } else { mIsDone = true; mIsRunning = false; break; } } } } private BattleRecordData.EventLog PickNextLog() { if (mData != null) { if (mLogs.Count > 0) { return mLogs.Dequeue(); } } else if (mStream != null) { try { BattleRecordData.EventLog log = new BattleRecordData.EventLog(); if (mStream.CanRead) { BattleRecordData.DecodeLog(Templates, mInput, log); return log; } } catch (Exception err) { Console.WriteLine("PickNextLog catch: " + err); } } return null; } } }