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.

GameServer.cs 12 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. using Grpc.Core;
  2. using Protobuf;
  3. using System.Threading;
  4. using Timothy.FrameRateTask;
  5. using System;
  6. using System.Net.Http.Headers;
  7. using Gaming;
  8. using GameClass.GameObj;
  9. namespace Server
  10. {
  11. public class GameServer : AvailableService.AvailableServiceBase
  12. {
  13. private Dictionary<long, (SemaphoreSlim, SemaphoreSlim)> semaDict = new();
  14. private object gameLock = new();
  15. private const int playerNum = 2; // 注意修改
  16. private MessageToClient currentGameInfo = new();
  17. private SemaphoreSlim endGameSem = new(0);
  18. // 以上是测试时用到的定义
  19. protected readonly ArgumentOptions options;
  20. protected readonly Game game;
  21. private uint spectatorMinPlayerID = 2022;
  22. private List<Tuple<PlayerType, uint>> spectatorList = new List<Tuple<PlayerType, uint>>();
  23. public int TeamCount => options.TeamCount;
  24. protected long[,] communicationToGameID; // 通信用的ID映射到游戏内的ID,[i,j]表示team:i,player:j的id。
  25. private readonly object messageToAllClientsLock = new();
  26. public static readonly long SendMessageToClientIntervalInMilliseconds = 50;
  27. private readonly Semaphore endGameInfoSema = new(0, 1);
  28. // private MessageWriter? mwr = null;
  29. public void StartGame()
  30. {
  31. bool gameState = game.StartGame((int)options.GameTimeInSecond * 1000);
  32. var waitHandle = new SemaphoreSlim(gameState == true ? 1 : 0); // 注意修改
  33. new Thread(() =>
  34. {
  35. new FrameRateTaskExecutor<int>
  36. (
  37. () => game.GameMap.Timer.IsGaming,
  38. ReportGame,
  39. 1000,
  40. () =>
  41. {
  42. ReportGame(); // 最后发一次消息,唤醒发消息的线程,防止发消息的线程由于有概率处在 Wait 状态而卡住
  43. return 0;
  44. }
  45. ).Start();
  46. })
  47. { IsBackground = true }.Start();
  48. new Thread(() =>
  49. {
  50. waitHandle.Wait();
  51. this.endGameSem.Release();
  52. })
  53. { IsBackground = true }.Start();
  54. }
  55. public void WaitForEnd()
  56. {
  57. this.endGameSem.Wait();
  58. }
  59. public void ReportGame()
  60. {
  61. // currentGameInfo = game.GetCopiedGameInfo();
  62. currentGameInfo.HumanMessage[0].X = 1;
  63. currentGameInfo.ButcherMessage[0].X = 1;
  64. foreach (var kvp in semaDict)
  65. {
  66. kvp.Value.Item1.Release();
  67. }
  68. foreach (var kvp in semaDict)
  69. {
  70. kvp.Value.Item2.Wait();
  71. }
  72. }
  73. private uint GetBirthPointIdx(long teamID, long playerID) // 获取出生点位置
  74. {
  75. return (uint)((teamID * options.PlayerCountPerTeam) + playerID);
  76. }
  77. public override Task<BoolRes> TryConnection(IDMsg request, ServerCallContext context)
  78. {
  79. var onConnection = new BoolRes();
  80. lock (gameLock)
  81. {
  82. if (0 <= request.PlayerId && request.PlayerId < playerNum) // 注意修改
  83. {
  84. onConnection.ActSuccess = true;
  85. Console.WriteLine(onConnection.ActSuccess);
  86. return Task.FromResult(onConnection);
  87. }
  88. }
  89. onConnection.ActSuccess = false;
  90. return Task.FromResult(onConnection);
  91. }
  92. protected readonly object addPlayerLock = new();
  93. public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter<MessageToClient> responseStream, ServerCallContext context)
  94. {
  95. if (request.PlayerId >= spectatorMinPlayerID && request.PlayerType == PlayerType.NullPlayerType)
  96. {
  97. // 观战模式
  98. Tuple<PlayerType, uint> tp = new Tuple<PlayerType, uint>(request.PlayerType, (uint)request.PlayerId);
  99. if (!spectatorList.Contains(tp))
  100. {
  101. spectatorList.Add(tp);
  102. Console.WriteLine("A new spectator comes to watch this game.");
  103. }
  104. return;
  105. }
  106. if (game.GameMap.Timer.IsGaming)
  107. return;
  108. /*if (!ValidTeamIDAndPlayerID(msg.TeamID, msg.PlayerID)) //玩家id是否正确
  109. return;
  110. if (communicationToGameID[msg.TeamID, msg.PlayerID] != GameObj.invalidID) //是否已经添加了该玩家
  111. return false;
  112. Preparation.Utility.PassiveSkillType passiveSkill;
  113. */
  114. lock (addPlayerLock)
  115. {
  116. /*Game.PlayerInitInfo playerInitInfo = new(GetBirthPointIdx(msg.TeamID, msg.PlayerID), msg.TeamID, msg.PlayerID, passiveSkill, commonSkill);
  117. long newPlayerID = game.AddPlayer(playerInitInfo);
  118. if (newPlayerID == GameObj.invalidID)
  119. return false;*/
  120. // 内容待修改
  121. var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1));
  122. bool start = false;
  123. Console.WriteLine($"Id: {request.PlayerId} joins.");
  124. lock (semaDict)
  125. {
  126. semaDict.Add(request.PlayerId, temp);
  127. start = semaDict.Count == playerNum; // 之后补上CheckStart函数
  128. }
  129. if (start)
  130. {
  131. Console.WriteLine("Game starts!");
  132. StartGame();
  133. }
  134. }
  135. do
  136. {
  137. semaDict[request.PlayerId].Item1.Wait();
  138. if (currentGameInfo != null)
  139. {
  140. await responseStream.WriteAsync(currentGameInfo);
  141. Console.WriteLine("Send!");
  142. }
  143. semaDict[request.PlayerId].Item2.Release();
  144. } while (game.GameMap.Timer.IsGaming);
  145. }
  146. public override Task<BoolRes> Attack(AttackMsg request, ServerCallContext context)
  147. {
  148. return base.Attack(request, context);
  149. }
  150. public override Task<BoolRes> CarryHuman(IDMsg request, ServerCallContext context)
  151. {
  152. return base.CarryHuman(request, context);
  153. }
  154. public override Task<BoolRes> EndFixMachine(IDMsg request, ServerCallContext context)
  155. {
  156. return base.EndFixMachine(request, context);
  157. }
  158. public override Task<BoolRes> EndSaveHuman(IDMsg request, ServerCallContext context)
  159. {
  160. return base.EndSaveHuman(request, context);
  161. }
  162. public override Task<BoolRes> Escape(IDMsg request, ServerCallContext context)
  163. {
  164. return base.Escape(request, context);
  165. }
  166. public override Task GetMessage(IDMsg request, IServerStreamWriter<MsgRes> responseStream, ServerCallContext context)
  167. {
  168. return base.GetMessage(request, responseStream, context);
  169. }
  170. public override Task<BoolRes> HangHuman(IDMsg request, ServerCallContext context)
  171. {
  172. return base.HangHuman(request, context);
  173. }
  174. public override Task<MoveRes> Move(MoveMsg request, ServerCallContext context)
  175. {
  176. return base.Move(request, context);
  177. }
  178. public override Task<BoolRes> PickProp(PickMsg request, ServerCallContext context)
  179. {
  180. return base.PickProp(request, context);
  181. }
  182. public override Task<BoolRes> ReleaseHuman(IDMsg request, ServerCallContext context)
  183. {
  184. return base.ReleaseHuman(request, context);
  185. }
  186. public override Task<BoolRes> SendMessage(SendMsg request, ServerCallContext context)
  187. {
  188. return base.SendMessage(request, context);
  189. }
  190. public override Task<BoolRes> StartFixMachine(IDMsg request, ServerCallContext context)
  191. {
  192. return base.StartFixMachine(request, context);
  193. }
  194. public override Task<BoolRes> StartSaveHuman(IDMsg request, ServerCallContext context)
  195. {
  196. return base.StartSaveHuman(request, context);
  197. }
  198. public override Task<BoolRes> UseProp(IDMsg request, ServerCallContext context)
  199. {
  200. return base.UseProp(request, context);
  201. }
  202. public override Task<BoolRes> UseSkill(IDMsg request, ServerCallContext context)
  203. {
  204. return base.UseSkill(request, context);
  205. }
  206. public GameServer(ArgumentOptions options)
  207. {
  208. /*this.options = options;
  209. if (options.mapResource == DefaultArgumentOptions.MapResource)
  210. this.game = new Game(MapInfo.defaultMap, options.TeamCount);
  211. else
  212. {
  213. uint[,] map = new uint[GameData.rows, GameData.cols];
  214. try
  215. {
  216. string? line;
  217. int i = 0, j = 0;
  218. using (StreamReader sr = new StreamReader(options.mapResource))
  219. {
  220. while (!sr.EndOfStream && i < GameData.rows)
  221. {
  222. if ((line = sr.ReadLine()) != null)
  223. {
  224. string[] nums = line.Split(' ');
  225. foreach (string item in nums)
  226. {
  227. if (item.Length > 1)//以兼容原方案
  228. {
  229. map[i, j] = (uint)int.Parse(item);
  230. }
  231. else
  232. {
  233. //2022-04-22 by LHR 十六进制编码地图方案(防止地图编辑员瞎眼x
  234. map[i, j] = (uint)Preparation.Utility.MapEncoder.Hex2Dec(char.Parse(item));
  235. }
  236. j++;
  237. if (j >= GameData.cols)
  238. {
  239. j = 0;
  240. break;
  241. }
  242. }
  243. i++;
  244. }
  245. }
  246. }
  247. }
  248. catch
  249. {
  250. map = MapInfo.defaultMap;
  251. }
  252. finally { this.game = new Game(map, options.TeamCount); }
  253. }
  254. communicationToGameID = new long[options.TeamCount, options.PlayerCountPerTeam];
  255. //创建server时先设定待加入人物都是invalid
  256. for (int i = 0; i < communicationToGameID.GetLength(0); i++)
  257. {
  258. for (int j = 0; j < communicationToGameID.GetLength(1); j++)
  259. {
  260. communicationToGameID[i, j] = GameObj.invalidID;
  261. }
  262. }
  263. if (options.FileName != DefaultArgumentOptions.FileName)
  264. {
  265. try
  266. {
  267. //mwr = new MessageWriter(options.FileName, options.TeamCount, options.PlayerCountPerTeam);
  268. }
  269. catch
  270. {
  271. Console.WriteLine($"Error: Cannot create the playback file: {options.FileName}!");
  272. }
  273. }
  274. if (options.Url != DefaultArgumentOptions.Url && options.Token != DefaultArgumentOptions.Token)
  275. {
  276. //this.httpSender = new HttpSender(options.Url, options.Token, "PUT");
  277. }*/
  278. }
  279. }
  280. }