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 14 kB

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