From 9dbe08c67d72f6ac5d210f0134ffa404bc179cbf Mon Sep 17 00:00:00 2001 From: TCL <1620508360@qq.com> Date: Thu, 27 Apr 2023 02:33:49 +0800 Subject: [PATCH] fix!: :bug: fix deadlock problem of spectator --- logic/Client/Properties/launchSettings.json | 4 +- logic/Server/GameServer.cs | 24 +++++++---- logic/Server/Properties/launchSettings.json | 4 +- logic/Server/RpcServices.cs | 47 +++++++++++++++------ 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/logic/Client/Properties/launchSettings.json b/logic/Client/Properties/launchSettings.json index 5cdc367..59aa8f1 100644 --- a/logic/Client/Properties/launchSettings.json +++ b/logic/Client/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Client": { "commandName": "Project", - "commandLineArgs": "--port 8888 --characterID 3 --type 1 --occupation 5" + "commandLineArgs": "--port 8892 --characterID 8880 --type 1 --occupation 1 --ip thuai6.eesast.com --cl" } } -} +} \ No newline at end of file diff --git a/logic/Server/GameServer.cs b/logic/Server/GameServer.cs index 5a6c14a..49e8614 100644 --- a/logic/Server/GameServer.cs +++ b/logic/Server/GameServer.cs @@ -11,14 +11,15 @@ using Playback; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Preparation.Interface; +using System.Collections.Concurrent; namespace Server { public partial class GameServer : AvailableService.AvailableServiceBase { - private Dictionary semaDict = new(); - private object semaDictLock = new(); + private ConcurrentDictionary semaDict = new(); + // private object semaDictLock = new(); protected readonly ArgumentOptions options; private HttpSender? httpSender; private object gameLock = new(); @@ -29,13 +30,13 @@ namespace Server private SemaphoreSlim endGameSem = new(0); protected readonly Game game; private uint spectatorMinPlayerID = 2023; - private List spectatorList = new List(); public int playerNum; public int TeamCount => options.TeamCount; protected long[] communicationToGameID; // 通信用的ID映射到游戏内的ID,通信中0-3为Student,4为Tricker private readonly object messageToAllClientsLock = new(); public static readonly long SendMessageToClientIntervalInMilliseconds = 50; private MessageWriter? mwr = null; + private object spetatorJoinLock = new(); public void StartGame() { @@ -169,14 +170,19 @@ namespace Server break; } } - foreach (var kvp in semaDict) + lock (spetatorJoinLock) { - kvp.Value.Item1.Release(); - } + foreach (var kvp in semaDict) + { + kvp.Value.Item1.Release(); + } - foreach (var kvp in semaDict) - { - kvp.Value.Item2.Wait(); + // 若此时观战者加入,则死锁,所以需要 spetatorJoinLock + + foreach (var kvp in semaDict) + { + kvp.Value.Item2.Wait(); + } } } private bool playerDeceased(int playerID) diff --git a/logic/Server/Properties/launchSettings.json b/logic/Server/Properties/launchSettings.json index d126864..97ec2d3 100644 --- a/logic/Server/Properties/launchSettings.json +++ b/logic/Server/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Server": { "commandName": "Project", - "commandLineArgs": "--ip 0.0.0.0 -p 8888 --characterID 2030" + "commandLineArgs": "--port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName video" } } -} +} \ No newline at end of file diff --git a/logic/Server/RpcServices.cs b/logic/Server/RpcServices.cs index 44de1bd..cac7984 100644 --- a/logic/Server/RpcServices.cs +++ b/logic/Server/RpcServices.cs @@ -16,6 +16,7 @@ namespace Server { public partial class GameServer : AvailableService.AvailableServiceBase { + private int playerCountNow = 0; protected object spectatorLock = new object(); protected bool isSpectatorJoin = false; protected bool IsSpectatorJoin @@ -59,17 +60,18 @@ namespace Server if (request.PlayerId >= spectatorMinPlayerID && options.NotAllowSpectator == false) { // 观战模式 - uint tp = (uint)request.PlayerId; - if (!spectatorList.Contains(tp)) + lock (spetatorJoinLock) // 具体原因见另一个上锁的地方 { - spectatorList.Add(tp); - Console.WriteLine("A new spectator comes to watch this game."); - var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)); - lock (semaDictLock) + if (semaDict.TryAdd(request.PlayerId, (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)))) { - semaDict.Add(request.PlayerId, temp); + Console.WriteLine("A new spectator comes to watch this game."); + IsSpectatorJoin = true; + } + else + { + Console.WriteLine($"Duplicated Spectator ID {request.PlayerId}"); + return; } - IsSpectatorJoin = true; } do { @@ -82,13 +84,30 @@ namespace Server //Console.WriteLine("Send!"); } } + catch (InvalidOperationException) + { + if (semaDict.TryRemove(request.PlayerId, out var semas)) + { + try + { + semas.Item1.Release(); + semas.Item2.Release(); + } + catch { } + Console.WriteLine($"The spectator {request.PlayerId} exited"); + } + } catch (Exception) { - //Console.WriteLine(ex); + // Console.WriteLine(ex); } finally { - semaDict[request.PlayerId].Item2.Release(); + try + { + semaDict[request.PlayerId].Item2.Release(); + } + catch { } } } while (game.GameMap.Timer.IsGaming); return; @@ -117,10 +136,12 @@ namespace Server var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)); bool start = false; Console.WriteLine($"Id: {request.PlayerId} joins."); - lock (semaDictLock) + // lock (semaDictLock) { - semaDict.Add(request.PlayerId, temp); - start = (semaDict.Count - spectatorList.Count) == playerNum; + if (semaDict.TryAdd(request.PlayerId, temp)) + { + start = Interlocked.Increment(ref playerCountNow) == playerNum; + } } if (start) StartGame(); }