From acca3a717611b20cb860310f2eb7f1867f162e6c Mon Sep 17 00:00:00 2001 From: Cyh28 <2606851972@qq.com> Date: Sat, 11 May 2024 22:51:17 +0800 Subject: [PATCH] feat: :rocket: Add unity playback --- .../interface_playback/Assets/Plugins.meta | 8 + .../Assets/Scripts/Playback.meta | 8 + .../Assets/Scripts/Playback/MessageReader.cs | 134 +++++++++++ .../Scripts/Playback/MessageReader.cs.meta | 11 + .../Scripts/Playback/PlaybackConstant.cs | 8 + .../Scripts/Playback/PlaybackConstant.cs.meta | 11 + .../Scripts/Playback/PlaybackController.cs | 223 ++++++++++++++++++ .../Playback/PlaybackController.cs.meta | 11 + 8 files changed, 414 insertions(+) create mode 100644 interface/interface_playback/Assets/Plugins.meta create mode 100644 interface/interface_playback/Assets/Scripts/Playback.meta create mode 100644 interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs create mode 100644 interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs.meta create mode 100644 interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs create mode 100644 interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs.meta create mode 100644 interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs create mode 100644 interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs.meta diff --git a/interface/interface_playback/Assets/Plugins.meta b/interface/interface_playback/Assets/Plugins.meta new file mode 100644 index 00000000..4032dbbc --- /dev/null +++ b/interface/interface_playback/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 550f42af462710b459b66720b19cca16 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/interface/interface_playback/Assets/Scripts/Playback.meta b/interface/interface_playback/Assets/Scripts/Playback.meta new file mode 100644 index 00000000..97f295d7 --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0a847955f4659d24e9a62b4dbb44b481 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs b/interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs new file mode 100644 index 00000000..062d0c8d --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs @@ -0,0 +1,134 @@ + +using Google.Protobuf; +using Protobuf; +using System; +using System.IO; +using System.IO.Compression; + +namespace Playback +{ + public class FileFormatNotLegalException : Exception + { + public FileFormatNotLegalException() + { + + } + public override string Message => $"The file is not a legal playback file for THUAI6."; + } + + public class MessageReader : IDisposable + { + private MemoryStream stream; + private CodedInputStream cos; + private GZipStream gzs; + private byte[] buffer; + public bool Finished { get; private set; } = false; + + public readonly uint teamCount; + public readonly uint playerCount; + + const int bufferMaxSize = 10 * 1024 * 1024; // 10M + + public MessageReader(byte[] bytes) + { + stream = new MemoryStream(bytes); + var prefixLen = PlayBackConstant.Prefix.Length; + byte[] bt = new byte[prefixLen + sizeof(UInt32) * 2]; + stream.Read(bt, 0, bt.Length); + for (int i = 0; i < prefixLen; ++i) + { + if (bt[i] != PlayBackConstant.Prefix[i]) throw new FileFormatNotLegalException(); + } + + teamCount = BitConverter.ToUInt32(bt, prefixLen); + playerCount = BitConverter.ToUInt32(bt, prefixLen + sizeof(UInt32)); + + gzs = new GZipStream(stream, 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); + } + + public MessageToClient? ReadOne() + { + beginRead: + if (Finished) + return null; + var pos = cos.Position; + try + { + MessageToClient? msg = new MessageToClient(); + cos.ReadMessage(msg); + return msg; + } + catch (InvalidProtocolBufferException) + { + 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; + } + } + + public void Dispose() + { + Finished = true; + if (stream == null) + return; + if (stream.CanRead) + { + stream.Close(); + } + } + + ~MessageReader() + { + Dispose(); + } + } +} \ No newline at end of file diff --git a/interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs.meta b/interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs.meta new file mode 100644 index 00000000..a62ff438 --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback/MessageReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45bde46b4862fb24e90f28e57c3472ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs b/interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs new file mode 100644 index 00000000..9db1c411 --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs @@ -0,0 +1,8 @@ +namespace Playback +{ + public static class PlayBackConstant + { + public static string ExtendedName = ".thuaipb"; + public static byte[] Prefix = { (byte)'P', (byte)'B', 7, 0 }; // 文件前缀,用于标识文件类型,版本号为6 + } +} diff --git a/interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs.meta b/interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs.meta new file mode 100644 index 00000000..e3903695 --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback/PlaybackConstant.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3766c4c9813f19748a5a7d8a22a2f080 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs b/interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs new file mode 100644 index 00000000..c0188123 --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs @@ -0,0 +1,223 @@ +using Playback; +using Protobuf; +using System; +using System.Collections; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.SceneManagement; + +public class PlaybackController : MonoBehaviour +{ + // Start is called before the first frame update + + public static int studentNum = 4; + private static int classroomNum = 10; + public static int propNum = 10; + public static int hiddenGateNum = 4; + + byte[] bytes = null; + + IEnumerator WebReader(string uri) + { + //if (!fileName.EndsWith(PlayBackConstant.ExtendedName)) + //{ + // fileName += PlayBackConstant.ExtendedName; + //} + //var uri = new Uri(Path.Combine(Application.streamingAssetsPath, fileName)); + //Debug.Log(uri.AbsoluteUri); + + //UnityWebRequest request = UnityWebRequest.Get(uri.AbsoluteUri); + UnityWebRequest request = UnityWebRequest.Get(uri); + request.timeout = 5; + yield return request.SendWebRequest(); + + if (request.isHttpError || request.isNetworkError) + { + Debug.Log(request.error); + filename = null; + playSpeed = 1; + isMap = true; + isInitial = false; + studentScore = trickerScore = 0; + SceneManager.LoadScene("Playback"); + yield break; + } + bytes = request.downloadHandler.data; + } + + void Start() + { + filename = "C:\\Users\\陈宇瀚\\Downloads\\playback.thuaipb"; + StartCoroutine(WebReader(filename)); + timer = frequency; + isMap = true; + isInitial = false; + //StudentController_Playback.studentCount = 0; + //ClassroomProgress_Playback.ClassroomCnt = 0; + } + + // Update is called once per frame + void Update() + { + timer -= Time.deltaTime * playSpeed; + + try + { + if (timer < 0 && bytes != null) + { + if (reader == null) + { + try + { + StopAllCoroutines(); + reader = new MessageReader(bytes); + } + catch (FileFormatNotLegalException) + { + filename = null; + playSpeed = -1; + isMap = true; + isInitial = false; + SceneManager.LoadScene("Playback"); + } + Debug.Log("reader created"); + } + timer = frequency; + var responseVal = reader.ReadOne(); + Debug.Log($"{responseVal}"); + if (responseVal == null) + { + filename = null; + playSpeed = -1; + isMap = true; + isInitial = false; + SceneManager.LoadScene("GameEnd"); + } + else if (isMap) + { + map = responseVal.ObjMessage[0].MapMessage; + isMap = false; + } + else if (!isInitial) + { + Receive(responseVal); + // for (int i = 0; i < studentNum; i++) + // { + // switch (Student[i].StudentType) + // { + // case StudentType.Athlete: + // Instantiate(student1, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(student1_icon, new Vector3(60f, -10f * i + 41f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case StudentType.StraightAStudent: + // Instantiate(student2, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(student2_icon, new Vector3(60f, -10f * i + 41f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case StudentType.Teacher: + // Instantiate(student3, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(student3_icon, new Vector3(60f, -10f * i + 41f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case StudentType.Sunshine: + // Instantiate(student4, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(student4_icon, new Vector3(60f, -10f * i + 41f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case StudentType.TechOtaku: + // Instantiate(student5, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(student5_icon, new Vector3(60f, -10f * i + 41f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case StudentType.Robot: + // Instantiate(student6, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(student6_icon, new Vector3(60f, -10f * i + 41f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // default: Instantiate(student1, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); break; + // } + // } + // switch (tricker.TrickerType) + // { + // case TrickerType.Assassin: + // Instantiate(tricker1, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(tricker1_icon, new Vector3(60f, 2.7f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case TrickerType.ANoisyPerson: + // Instantiate(tricker2, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(tricker2_icon, new Vector3(60f, 2.7f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case TrickerType.Klee: + // Instantiate(tricker3, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(tricker3_icon, new Vector3(60f, 2.7f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // case TrickerType.Idol: + // Instantiate(tricker1, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); + // Instantiate(tricker1_icon, new Vector3(60f, 2.7f, 0), new Quaternion(0, 0, 0, 0)); + // break; + // default: Instantiate(tricker1, new Vector3(0f, 0f, 10.0f), new Quaternion(0, 0, 0, 0)); break; + // } + isInitial = true; + } + else + { + Receive(responseVal); + } + } + + } + catch (NullReferenceException) + { + filename = null; + playSpeed = 1; + isMap = true; + isInitial = false; + SceneManager.LoadScene("Playback"); + } + + } + + private void Receive(MessageToClient message) + { + int studentCnt = 0; + int classroomCnt = 0; + //Debug.Log(message.ToString()); + time = message.AllMessage.GameTime; + foreach (var messageOfObj in message.ObjMessage) + { + // switch (messageOfObj.MessageOfObjCase) + // { + // case MessageOfObj.MessageOfObjOneofCase.StudentMessage: + // Student[studentCnt] = messageOfObj.StudentMessage; studentCnt++; break; + // case MessageOfObj.MessageOfObjOneofCase.TrickerMessage: + // tricker = messageOfObj.TrickerMessage; break; + // case MessageOfObj.MessageOfObjOneofCase.ClassroomMessage: + // classroom[classroomCnt] = messageOfObj.ClassroomMessage; classroomCnt++; break; + // case MessageOfObj.MessageOfObjOneofCase.PropMessage: + // break; + // case MessageOfObj.MessageOfObjOneofCase.HiddenGateMessage: + // break; + // default: break; + // } + } + } + + public static string filename; + MessageReader reader; + float frequency = 0.05f; + float timer; + public static int playSpeed = 1; + + public static MessageOfMap map; + // public static MessageOfStudent[] Student = new MessageOfStudent[studentNum]; + // public static MessageOfTricker tricker = new MessageOfTricker(); + // public static MessageOfClassroom[] classroom = new MessageOfClassroom[classroomNum]; + // public static MessageOfProp[] prop = new MessageOfProp[propNum]; + // public static MessageOfHiddenGate[] hiddenGate = new MessageOfHiddenGate[hiddenGateNum]; + public GameObject student1, student2, student3, student4, student5, student6; + public GameObject tricker1; + public GameObject tricker2; + public GameObject tricker3; + public GameObject student1_icon, student2_icon, student3_icon, student4_icon, student5_icon, student6_icon; + public GameObject tricker1_icon, tricker2_icon, tricker3_icon; + public static bool isMap; + public static bool isInitial; + public static int time; + public static int studentScore; + public static int trickerScore; +} \ No newline at end of file diff --git a/interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs.meta b/interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs.meta new file mode 100644 index 00000000..369f1223 --- /dev/null +++ b/interface/interface_playback/Assets/Scripts/Playback/PlaybackController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ddfe76a77bc0155448788971463b7f57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: