{"record":{"id":"e045698f6ef8d25f","repo":"egametang/ET","slug":"player-not-in-wait-list-playerid","errorCode":null,"errorMessage":"player not in wait list: {playerId}","messagePattern":"player not in wait list: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.mapmanager/Scripts/Hotfix/Server/MapManagerComponentSystem.cs","lineNumber":25,"sourceCode":"    [EntitySystemOf(typeof(MapCopy))]\n    public static partial class MapCopySystem\n    {\n        [EntitySystem]\n        private static void Destroy(this MapCopy self)\n        {\n            self.Fiber().RemoveFiber(self.FiberId).Coroutine();\n        }\n        [EntitySystem]\n        private static void Awake(this MapCopy self, long fiberId)\n        {\n            self.FiberId = fiberId;\n        }\n\n        public static void AddPlayer(this MapCopy self, long playerId)\n        {\n            if (!self.WaitEnterPlayer.Remove(playerId))\n            {\n                throw new Exception($\"player not in wait list: {playerId}\");\n            }\n            self.Players.Add(playerId);\n        }\n\n        public static void AddWaitPlayer(this MapCopy self, long playerId)\n        {\n            self.WaitEnterPlayer.Add(playerId, self.GetSingleton<TimeInfo>().ServerNow());\n        }\n    }\n\n    [EntitySystemOf(typeof(MapInfo))]\n    public static partial class MapInfoSystem\n    {\n        [EntitySystem]\n        private static void Awake(this MapInfo self, string mapName)\n        {\n            self.MapName = mapName;\n        }","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.mapmanager/Scripts/Hotfix/Server/MapManagerComponentSystem.cs#L7-L43","documentation":"Thrown by MapCopy.AddPlayer when the playerId is not found in the WaitEnterPlayer dictionary (Remove returns false). The contract is: a player must first be added via AddWaitPlayer before AddPlayer is called. If AddPlayer is called for a player not in the wait list, or one that was already moved/removed, the exception fires.","triggerScenarios":"AddPlayer(playerId) is called without a prior AddWaitPlayer(playerId). Or AddPlayer is called twice for the same player (the first removes them from WaitEnterPlayer, the second fails). Or the player was removed from the wait list by another path before AddPlayer was called.","commonSituations":"A player enters a map copy without going through the wait-list registration flow. Race condition: two enter-requests for the same player processed concurrently. Player was kicked/timed-out from the wait list before the enter message arrived. Stale duplicate enter request after the player already entered.","solutions":["Ensure AddWaitPlayer(playerId) is always called before AddPlayer(playerId) in the map copy lifecycle.","Guard with ContainsKey before AddPlayer, or make AddPlayer idempotent by checking if the player is already in Players.","Investigate race conditions if multiple fibers can process enter-requests for the same player simultaneously."],"exampleFix":"// before\npublic static void AddPlayer(this MapCopy self, long playerId)\n{\n    if (!self.WaitEnterPlayer.Remove(playerId))\n        throw new Exception($\"player not in wait list: {playerId}\");\n    self.Players.Add(playerId);\n}\n// after: idempotent guard\npublic static void AddPlayer(this MapCopy self, long playerId)\n{\n    if (self.Players.Contains(playerId))\n        return; // already added\n    if (!self.WaitEnterPlayer.Remove(playerId))\n    {\n        Log.Error($\"player not in wait list: {playerId}\");\n        return;\n    }\n    self.Players.Add(playerId);\n}","handlingStrategy":"validation","validationCode":"// Before calling AddPlayer, verify the player is in the wait list\nif (!self.WaitEnterPlayer.ContainsKey(playerId))\n{\n    if (self.Players.Contains(playerId))\n    {\n        Log.Warning($\"Player {playerId} already in map copy, skipping\");\n        return;\n    }\n    Log.Error($\"Player {playerId} not in wait list and not already added\");\n    return;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call AddWaitPlayer before AddPlayer in the map entry lifecycle.","Make AddPlayer idempotent to handle duplicate or stale enter-requests gracefully.","Use server-side deduplication to prevent concurrent enter-requests for the same player."],"tags":["map","server","player-management","state-contract"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}