Skip to content

Commit

Permalink
Merge pull request #104 from asdawej/dev-playback
Browse files Browse the repository at this point in the history
Dev for playback
  • Loading branch information
DragonAura authored Mar 9, 2024
2 parents f15a87a + a9a5173 commit 2ebad4c
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 154 deletions.
4 changes: 2 additions & 2 deletions dependency/proto/Message2Clients.proto
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ message MessageOfMap

message MessageOfTeam
{
int64 team_id = 1;
int64 team_id = 1;
int64 player_id = 2;
int32 score = 3;
int32 score = 3;
int32 money = 4;
int64 guid=5;
}
Expand Down
2 changes: 1 addition & 1 deletion dependency/proto/Message2Server.proto
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ message ConstructMsg //修建建筑
message RecoverMsg
{
int64 player_id = 1;
int64 recover = 2;
int64 recover = 2;
int64 team_id = 3;
}

Expand Down
2 changes: 1 addition & 1 deletion logic/GameClass/GameObj/Map/MapStruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class MapStreamError(Stream str) : Exception
#endregion

/// <summary>
/// 地图结构体
/// 地图结构体,本结构将会被MapGenerator-Python引用——asdawej
/// </summary>
public readonly struct MapStruct
{
Expand Down
137 changes: 31 additions & 106 deletions playback/Playback/MessageReader.cs
Original file line number Diff line number Diff line change
@@ -1,143 +1,68 @@
using Google.Protobuf;
using Protobuf;
using System;
using System.IO;
using System.IO.Compression;

namespace Playback
{
public class FileFormatNotLegalException(string fileName) : Exception
{
private readonly string fileName = fileName;
public override string Message => $"The file: " + this.fileName + " is not a legal playback file for THUAI6.";
}

public class MessageReader : IDisposable
{
private FileStream? fs;
private CodedInputStream cos;
private GZipStream gzs;
private byte[] buffer;
public bool Finished { get; private set; } = false;
/// <summary>
/// 回放文件绝对路径
/// </summary>
public string FileName { get; }

public readonly uint teamCount;
public readonly uint playerCount;

const int bufferMaxSize = 10 * 1024 * 1024; // 10M
private readonly CodedInputStream cis; // Protobuf类型二进制输入流

public bool Disposed { get; private set; } = false;

public MessageReader(string fileName)
{
if (!fileName.EndsWith(PlayBackConstant.ExtendedName))
{
fileName += PlayBackConstant.ExtendedName;
}

fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);

try
{
var prefixLen = PlayBackConstant.Prefix.Length;
byte[] bt = new byte[prefixLen + sizeof(UInt32) * 2];
fs.Read(bt, 0, bt.Length);
for (int i = 0; i < prefixLen; ++i)
{
if (bt[i] != PlayBackConstant.Prefix[i]) throw new FileFormatNotLegalException(fileName);
}

teamCount = BitConverter.ToUInt32(bt, prefixLen);
playerCount = BitConverter.ToUInt32(bt, prefixLen + sizeof(UInt32));
}
catch
{
throw new FileFormatNotLegalException(fileName);
}

gzs = new GZipStream(fs, CompressionMode.Decompress);
var tmpBuffer = new byte[bufferMaxSize];
var bufferSize = gzs.Read(tmpBuffer);
if (bufferSize == 0)
{
buffer = tmpBuffer;
Finished = true;
}
else if (bufferSize != bufferMaxSize) // 不留空位,防止 CodedInputStream 获取信息错误
{
if (bufferSize == 0)
{
Finished = true;
}
buffer = new byte[bufferSize];
Array.Copy(tmpBuffer, buffer, bufferSize);
}
else
{
buffer = tmpBuffer;
}
cos = new CodedInputStream(buffer);
Utils.FileNameRegular(ref fileName);
FileStream fs = File.OpenRead(fileName);
FileName = fs.Name;
(teamCount, playerCount) = fs.ReadHeader();
GZipStream gzs = new(fs, CompressionMode.Decompress);
cis = new(gzs);
}

public MessageToClient? ReadOne()
{
beginRead:
if (Finished)
return null;
var pos = cos.Position;
if (Disposed) return null;
if (cis.IsAtEnd) return null;
MessageToClient ret = new();
try
{
MessageToClient? msg = new();
cos.ReadMessage(msg);
return msg;
cis.ReadMessage(ret);
return ret;
}
catch (InvalidProtocolBufferException)
catch
{
var leftByte = buffer.Length - pos; // 上次读取剩余的字节
if (buffer.Length < bufferMaxSize / 2)
{
var newBuffer = new byte[bufferMaxSize];
for (int i = 0; i < leftByte; i++)
{
newBuffer[i] = buffer[pos + i];
}
buffer = newBuffer;
}
else
{
for (int i = 0; i < leftByte; ++i)
{
buffer[i] = buffer[pos + i];
}
}
var bufferSize = gzs.Read(buffer, (int)leftByte, (int)(buffer.Length - leftByte)) + leftByte;
if (bufferSize == leftByte)
{
Finished = true;
return null;
}
if (bufferSize != buffer.Length) // 不留空位,防止 CodedInputStream 获取信息错误
{
var tmpBuffer = new byte[bufferSize];
Array.Copy(buffer, tmpBuffer, bufferSize);
buffer = tmpBuffer;
}
cos = new CodedInputStream(buffer);
goto beginRead;
return null;
}
}

public void Dispose()
{
Finished = true;
if (fs == null)
return;
if (fs.CanRead)
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (Disposed) return;
if (disposing)
{
fs.Close();
cis.Dispose();
}
Disposed = true;
}

~MessageReader()
{
Dispose();
Dispose(false);
}
}
}
65 changes: 25 additions & 40 deletions playback/Playback/MessageWriter.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,60 @@
using Google.Protobuf;
using Protobuf;
using System;
using System.IO;
using System.IO.Compression;

namespace Playback
{
public class MessageWriter : IDisposable
{
private FileStream fs;
private CodedOutputStream cos;
private MemoryStream ms;
private GZipStream gzs;
private const int memoryCapacity = 10 * 1024 * 1024; // 10M
/// <summary>
/// 回放文件绝对路径
/// </summary>
public string FileName { get; }

private static void ClearMemoryStream(MemoryStream msToClear)
{
msToClear.Position = 0;
msToClear.SetLength(0);
}
private readonly CodedOutputStream cos; // Protobuf类型二进制输出流

public bool Disposed { get; private set; } = false;

public MessageWriter(string fileName, uint teamCount, uint playerCount)
{
if (!fileName.EndsWith(PlayBackConstant.ExtendedName))
{
fileName += PlayBackConstant.ExtendedName;
}

fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
fs.Write(PlayBackConstant.Prefix); // 写入前缀

fs.Write(BitConverter.GetBytes((UInt32)teamCount)); // 写入队伍数
fs.Write(BitConverter.GetBytes((UInt32)playerCount)); // 写入每队的玩家人数
ms = new MemoryStream(memoryCapacity);
cos = new CodedOutputStream(ms);
gzs = new GZipStream(fs, CompressionMode.Compress);
Utils.FileNameRegular(ref fileName);
FileStream fs = File.Create(fileName);
FileName = fs.Name;
fs.WriteHeader(teamCount, playerCount);
GZipStream gzs = new(fs, CompressionMode.Compress);
cos = new(gzs);
}

public void WriteOne(MessageToClient msg)
{
if (Disposed) return;
cos.WriteMessage(msg);
if (ms.Length > memoryCapacity)
Flush();
}

public void Flush()
{
if (fs.CanWrite)
{
cos.Flush();
gzs.Write(ms.GetBuffer(), 0, (int)ms.Length);
gzs.Flush();
ClearMemoryStream(ms);
fs.Flush();
}
cos.Flush();
}

public void Dispose()
{
if (fs.CanWrite)
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (Disposed) return;
if (disposing)
{
Flush();
cos.Dispose();
gzs.Dispose();
fs.Dispose();
}
Disposed = true;
}

~MessageWriter()
{
Dispose();
Dispose(false);
}
}
}
67 changes: 63 additions & 4 deletions playback/Playback/PlaybackConstant.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,67 @@
namespace Playback
namespace Playback;
public static class Constants
{
public static class PlayBackConstant
/// <summary>
/// 回放版本
/// </summary>
public const int Version = 7;
/// <summary>
/// 回放文件扩展名
/// </summary>
public static readonly string FileExtension = $".thuai{Version}.pb";
/// <summary>
/// 回放文件头
/// </summary>
public static readonly byte[] FileHeader = [(byte)'P', (byte)'B', Version, 0];
}
/// <summary>
/// 回放文件格式错误
/// </summary>
/// <param name="fileName"></param>
public class FileFormatNotLegalException(string fileName) : Exception
{
public string FileName { get; } = fileName;
public override string Message { get; }
= $"The file: {fileName} is not a legal playback file for THUAI{Constants.Version}.";
}
public static class Utils
{
/// <summary>
/// 回放文件名正则化
/// </summary>
/// <param name="fileName">文件名</param>
public static void FileNameRegular(ref string fileName)
{
if (!fileName.EndsWith(Constants.FileExtension))
{
fileName += Constants.FileExtension;
}
}
/// <summary>
/// 文件头数据写入
/// </summary>
/// <param name="fs">文件写入流</param>
/// <param name="teamCount">队伍数</param>
/// <param name="playerCount">玩家数</param>
public static void WriteHeader(this FileStream fs, uint teamCount, uint playerCount)
{
BinaryWriter bw = new(fs);
bw.Write(Constants.FileHeader); // 写入文件头
bw.Write(teamCount); // 写入队伍数
bw.Write(playerCount); // 写入玩家数
}
/// <summary>
/// 文件头数据读取
/// </summary>
/// <returns></returns>
/// <param name="fs">文件读取流</param>
/// <exception cref="FileFormatNotLegalException"></exception>
public static (uint teamCount, uint playerCount) ReadHeader(this FileStream fs)
{
public static string ExtendedName = ".thuaipb";
public static byte[] Prefix = { (byte)'P', (byte)'B', 6, 0 }; // 文件前缀,用于标识文件类型,版本号为6
BinaryReader br = new(fs);
if (!br.ReadBytes(Constants.FileHeader.Length) // 判断文件头
.SequenceEqual(Constants.FileHeader))
throw new FileFormatNotLegalException(fs.Name);
return (br.ReadUInt32(), br.ReadUInt32()); // 读取队伍数和每队玩家人数
}
}

0 comments on commit 2ebad4c

Please sign in to comment.