You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

PlaybackServer.cs 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using Protobuf;
  2. using Playback;
  3. using System;
  4. using System.Threading;
  5. using Timothy.FrameRateTask;
  6. using Gaming;
  7. using Grpc.Core;
  8. namespace Server
  9. {
  10. class PlaybackServer : AvailableService.AvailableServiceBase
  11. {
  12. protected readonly ArgumentOptions options;
  13. private int[,] teamScore;
  14. private Dictionary<long, (SemaphoreSlim, SemaphoreSlim)> semaDict = new();
  15. private object semaDictLock = new();
  16. private MessageToClient? currentGameInfo = new();
  17. private uint spectatorMinPlayerID = 2023;
  18. private List<uint> spectatorList = new List<uint>();
  19. public int TeamCount => options.TeamCount;
  20. private MessageWriter? mwr = null;
  21. private bool IsGaming { get; set; }
  22. public PlaybackServer(ArgumentOptions options)
  23. {
  24. this.options = options;
  25. IsGaming = true;
  26. teamScore = new int[0, 0];
  27. }
  28. public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter<MessageToClient> responseStream, ServerCallContext context)
  29. {
  30. if (request.PlayerId >= spectatorMinPlayerID)
  31. {
  32. // 观战模式
  33. uint tp = (uint)request.PlayerId;
  34. if (!spectatorList.Contains(tp))
  35. {
  36. spectatorList.Add(tp);
  37. Console.WriteLine("A new spectator comes to watch this game.");
  38. var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1));
  39. lock (semaDictLock)
  40. {
  41. semaDict.Add(request.PlayerId, temp);
  42. }
  43. }
  44. do
  45. {
  46. semaDict[request.PlayerId].Item1.Wait();
  47. try
  48. {
  49. if (currentGameInfo != null)
  50. {
  51. await responseStream.WriteAsync(currentGameInfo);
  52. //Console.WriteLine("Send!");
  53. }
  54. }
  55. catch (Exception)
  56. {
  57. //Console.WriteLine(ex);
  58. }
  59. finally
  60. {
  61. semaDict[request.PlayerId].Item2.Release();
  62. }
  63. } while (IsGaming == true);
  64. return;
  65. }
  66. }
  67. public void ReportGame(MessageToClient? msg)
  68. {
  69. currentGameInfo = msg;
  70. foreach (var kvp in semaDict)
  71. {
  72. kvp.Value.Item1.Release();
  73. }
  74. foreach (var kvp in semaDict)
  75. {
  76. kvp.Value.Item2.Wait();
  77. }
  78. }
  79. public void WaitForGame()
  80. {
  81. try
  82. {
  83. if (options.ResultOnly)
  84. {
  85. using (MessageReader mr = new MessageReader(options.FileName))
  86. {
  87. Console.WriteLine("Parsing playback file...");
  88. teamScore = new int[mr.teamCount, mr.playerCount];
  89. int infoNo = 0;
  90. object cursorLock = new object();
  91. var initialTop = Console.CursorTop;
  92. var initialLeft = Console.CursorLeft;
  93. while (true)
  94. {
  95. MessageToClient? msg = null;
  96. for (int i = 0; i < mr.teamCount; ++i)
  97. {
  98. for (int j = 0; j < mr.playerCount; ++j)
  99. {
  100. msg = mr.ReadOne();
  101. if (msg == null)
  102. {
  103. Console.WriteLine("The game doesn't come to an end because of timing up!");
  104. IsGaming = false;
  105. goto endParse;
  106. }
  107. lock (cursorLock)
  108. {
  109. var curTop = Console.CursorTop;
  110. var curLeft = Console.CursorLeft;
  111. Console.SetCursorPosition(initialLeft, initialTop);
  112. Console.WriteLine($"Parsing messages... Current message number: {infoNo}");
  113. Console.SetCursorPosition(curLeft, curTop);
  114. }
  115. if (msg != null)
  116. {
  117. //teamScore[i] = msg.TeamScore;
  118. }
  119. }
  120. }
  121. ++infoNo;
  122. if (msg == null)
  123. {
  124. Console.WriteLine("No game information in this file!");
  125. goto endParse;
  126. }
  127. if (msg.GameState == GameState.GameEnd)
  128. {
  129. Console.WriteLine("Game over normally!");
  130. goto endParse;
  131. }
  132. }
  133. endParse:
  134. Console.WriteLine($"Successfully parsed {infoNo} informations!");
  135. }
  136. }
  137. else
  138. {
  139. long timeInterval = GameServer.SendMessageToClientIntervalInMilliseconds;
  140. if (options.PlaybackSpeed != 1.0)
  141. {
  142. options.PlaybackSpeed = Math.Max(0.25, Math.Min(4.0, options.PlaybackSpeed));
  143. timeInterval = (int)Math.Round(timeInterval / options.PlaybackSpeed);
  144. }
  145. using (MessageReader mr = new MessageReader(options.FileName))
  146. {
  147. teamScore = new int[mr.teamCount, mr.playerCount];
  148. int infoNo = 0;
  149. object cursorLock = new object();
  150. var msgCurTop = Console.CursorTop;
  151. var msgCurLeft = Console.CursorLeft;
  152. var frt = new FrameRateTaskExecutor<int>
  153. (
  154. loopCondition: () => true,
  155. loopToDo: () =>
  156. {
  157. MessageToClient? msg = null;
  158. msg = mr.ReadOne();
  159. if (msg == null)
  160. {
  161. Console.WriteLine("The game doesn't come to an end because of timing up!");
  162. IsGaming = false;
  163. ReportGame(msg);
  164. return false;
  165. }
  166. ReportGame(msg);
  167. lock (cursorLock)
  168. {
  169. var curTop = Console.CursorTop;
  170. var curLeft = Console.CursorLeft;
  171. Console.SetCursorPosition(msgCurLeft, msgCurTop);
  172. Console.WriteLine($"Sending messages... Current message number: {infoNo}.");
  173. Console.SetCursorPosition(curLeft, curTop);
  174. }
  175. if (msg != null)
  176. {
  177. foreach (var item in msg.ObjMessage)
  178. {
  179. if (item.StudentMessage != null)
  180. teamScore[0, item.StudentMessage.PlayerId] = item.StudentMessage.Score;
  181. if (item.TrickerMessage != null)
  182. teamScore[1, item.TrickerMessage.PlayerId - options.MaxStudentCount] = item.TrickerMessage.Score; // 这里默认 Tricker 的 PlayerId 从 MaxStudentCount = 4 开始
  183. }
  184. }
  185. ++infoNo;
  186. if (msg == null)
  187. {
  188. Console.WriteLine("No game information in this file!");
  189. IsGaming = false;
  190. ReportGame(msg);
  191. return false;
  192. }
  193. if (msg.GameState == GameState.GameEnd)
  194. {
  195. Console.WriteLine("Game over normally!");
  196. IsGaming = false;
  197. ReportGame(msg);
  198. return false;
  199. }
  200. return true;
  201. },
  202. timeInterval: timeInterval,
  203. finallyReturn: () => 0
  204. )
  205. { AllowTimeExceed = true, MaxTolerantTimeExceedCount = 5 };
  206. Console.WriteLine("The server is well prepared! Please MAKE SURE that you have opened all the clients to watch the game!");
  207. Console.WriteLine("If ALL clients have opened, press any key to start.");
  208. Console.ReadKey();
  209. new Thread
  210. (
  211. () =>
  212. {
  213. var rateCurTop = Console.CursorTop;
  214. var rateCurLeft = Console.CursorLeft;
  215. lock (cursorLock)
  216. {
  217. rateCurTop = Console.CursorTop;
  218. rateCurLeft = Console.CursorLeft;
  219. Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}");
  220. }
  221. while (!frt.Finished)
  222. {
  223. lock (cursorLock)
  224. {
  225. var curTop = Console.CursorTop;
  226. var curLeft = Console.CursorLeft;
  227. Console.SetCursorPosition(rateCurLeft, rateCurTop);
  228. Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}");
  229. Console.SetCursorPosition(curLeft, curTop);
  230. }
  231. Thread.Sleep(1000);
  232. }
  233. }
  234. )
  235. { IsBackground = true }.Start();
  236. lock (cursorLock)
  237. {
  238. msgCurLeft = Console.CursorLeft;
  239. msgCurTop = Console.CursorTop;
  240. Console.WriteLine("Sending messages...");
  241. }
  242. frt.Start();
  243. }
  244. }
  245. }
  246. finally
  247. {
  248. teamScore ??= new int[0, 0];
  249. }
  250. }
  251. }
  252. }