快速启停游戏时Discord.Net游戏状态同步异常问题排查
问题分析与修复方案
核心问题1:Activity对象引用比较失效
Discord的Activity对象在每次PresenceUpdated事件触发时会生成新实例,即使游戏名称和类型完全一致,对象引用也不同。原代码中用!newPresence.Activities.Contains(game)做引用比较,会导致无法正确识别已关闭的游戏,进而无法赋值正确的gameName,后续移除逻辑自然失效。
核心问题2:字符串转换枚举的不必要风险
原代码用Convert.ToString(e.Type) == "Playing"判断活动类型,既低效又容易出错(比如枚举字符串名变更),应该直接使用Discord API提供的ActivityType.Playing枚举值比较。
核心问题3:共享集合的线程安全保障不足
即使尝试过锁,如果锁的范围没有覆盖所有对users集合的读写操作(Add/Any/FindIndex/RemoveAt),快速操作时依然会出现数据不一致的情况。
具体修复代码
1. 修复游戏关闭时的名称识别逻辑
将原代码中获取关闭游戏名的部分替换为:
// 直接用枚举判断Playing类型,避免字符串转换 var oldPlayingActivities = oldPresence.Activities.Where(e => e.Type == ActivityType.Playing).ToList(); var newPlayingActivities = newPresence.Activities.Where(e => e.Type == ActivityType.Playing).ToList(); // 判断是否有Playing活动被移除 if (oldPlayingActivities.Count > newPlayingActivities.Count) { // 通过游戏名称匹配找到被移除的游戏,而非引用比较 var closedGame = oldPlayingActivities.FirstOrDefault(game => !newPlayingActivities.Any(n => n.Name == game.Name)); if (closedGame != null) { gameName = closedGame.Name; shouldDo = true; } }
2. 给users集合添加完整的线程锁
首先在类级别声明锁对象:
private readonly object _usersLock = new object();
然后所有操作users的代码都包裹在锁内:
// 游戏移除逻辑 if (shouldDo) { User localUser = new User(Convert.ToString(user), gameName); lock (_usersLock) { int index = users.FindIndex(u => u.userName == localUser.userName && u.game == localUser.game); if (index != -1) { Console.WriteLine(gameName); Console.WriteLine(DateTime.Now.Subtract(users[index].date)); // dataBase.AddPlayerGame(1, "saba", DateTime.Now.Subtract(users[index].date)); users.RemoveAt(index); } } } // 游戏添加逻辑 if (!shouldDo && newPlayingActivities.Any()) { string newGame = newPlayingActivities.First().Name; User localUser = new User(Convert.ToString(user), newGame); lock (_usersLock) { if (!users.Any(e => e.userName == localUser.userName && e.game == localUser.game)) { users.Add(localUser); Console.WriteLine("game added"); } } }
3. 优化线程调用(可选)
如果单线程运行仍有问题,建议用Task替代手动创建Thread,或通过信号量控制并发:
// 替换原线程创建代码 await Task.Run(() => ActivityHandler(user, oldPresence, newPresence));
额外注意事项
- 确保
User类的userName和game属性能正确匹配,避免因大小写、空格等格式问题导致无法找到对应项。 - 快速操作时,Discord可能合并或延迟触发
PresenceUpdated事件,可增加活动时间戳判断,避免重复或错误的添加/移除操作。
内容的提问来源于stack exchange,提问作者skami0_0
相关产品推荐
相关产品推荐

