ET实现游戏中邮件系统逻辑思路(服务端)_游戏邮件系统设计-CSDN博客
场景:今天遇到一个BUG,在服务器重启的时候(体验服),玩家之前接收的邮件又重新接收了一次,但是两封邮件的ID是不同的。ID不同说明玩家自身邮件组件向EmailManager组件拉取了两次,同时也说明EmailManager组件在玩家拉取完邮件后没清理保存,于是我先想着在接受邮件之前判断这个邮件是不是已经接收了。
修复BUG的方式很直接暴力,直接用EmailInfo与玩家自身邮件组件做对比,不过我第一时间想到的竟然是 ‘==’ ,又一次被自己气笑了。
==运算符对于基本数据类型比较值,对于引用类型默认比较引用(内存地址)。
由于两封邮件内容、时间、奖励等信息相同但是ID不同那只能比较除ID外的内容是否相等。
引出正题:
1. 使用 SequenceEqual
方法
System.Linq
命名空间中的 Enumerable.SequenceEqual
方法可以用于比较两个序列的内容是否相等。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 1, 2, 3 };
List<int> list3 = new List<int> { 1, 2, 4 };
bool areEqual1 = list1.SequenceEqual(list2); // True
bool areEqual2 = list1.SequenceEqual(list3); // False
Console.WriteLine($"list1 and list2 are equal: {areEqual1}");
Console.WriteLine($"list1 and list3 are equal: {areEqual2}");
}
}
2. 使用 SetEquals
方法
如果你只关心两个集合是否包含相同的元素,而不关心顺序,可以使用 HashSet<T>
的 SetEquals
方法。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 3, 2, 1 };
HashSet<int> set1 = new HashSet<int>(list1);
HashSet<int> set2 = new HashSet<int>(list2);
bool areEqual = set1.SetEquals(set2); // True,顺序不重要
Console.WriteLine($"list1 and list2 contain the same elements: {areEqual}");
}
}
3. 手动比较
如果你需要更复杂的比较逻辑(例如自定义对象),可以手动遍历两个列表并进行比较。
- 属性
Value
:这是一个整型属性,用于存储MyClass
对象的值。 - 重写
Equals
方法:- 该方法用于比较两个
MyClass
对象是否相等。 - 首先检查传入的对象
obj
是否是MyClass
的实例。如果是,则比较它们的Value
属性。 - 如果
Value
相等,则返回true
,否则返回false
。
- 该方法用于比较两个
- 重写
GetHashCode
方法:- 该方法返回
Value
属性的哈希码。重写这个方法是为了确保在使用哈希表等数据结构时,MyClass
对象的哈希值是基于其内容的。
- 该方法返回
using System;
using System.Collections.Generic;
class MyClass
{
public int Value { get; set; }
public override bool Equals(object obj)
{
if (obj is MyClass other)
{
return this.Value == other.Value;
}
return false;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
class Program
{
static void Main()
{
List<MyClass> list1 = new List<MyClass> { new MyClass { Value = 1 }, new MyClass { Value = 2 } };
List<MyClass> list2 = new List<MyClass> { new MyClass { Value = 1 }, new MyClass { Value = 2 } };
bool areEqual = list1.Count == list2.Count && !list1.Except(list2).Any(); // True
Console.WriteLine($"list1 and list2 are equal: {areEqual}");
}
}
- 创建
list1
和list2
:list1
和list2
是两个List<MyClass>
,它们各自包含两个MyClass
对象,Value
分别为1
和2
。
- 比较两个列表的内容:
areEqual
变量的值是通过以下逻辑计算得到的:- 首先检查两个列表的元素数量是否相等(
list1.Count == list2.Count
)。 - 然后使用
Except
方法找出list1
中不在list2
中的元素,如果没有这样的元素(即!list1.Except(list2).Any()
为true
),则说明两个列表的内容相等。
- 首先检查两个列表的元素数量是否相等(
- 输出结果:
- 最后,使用
Console.WriteLine
输出两个列表是否相等的结果。
- 最后,使用
总结
- 使用
SequenceEqual
是最简单和直接的方法,适用于顺序敏感的比较。 - 使用
SetEquals
适用于无序比较。 - 手动比较适用于需要自定义比较逻辑的情况。
其实这个修改BUG的方式是很蠢的,还是要从根源上分析为什么会出现两封一样的邮件。
BUG出现的原因
ET框架新起一个服务及实现服务之间的消息通讯_et startsceneconfig-CSDN博客
先说答案:出现这个BUG的原因是线上版本修改了Data服的配置文件(线上版本最开始的配置和本地文件相同,后来修改了Data服的线上版配置文件导致读取数据不一致)
EmailManager组件放在了自己起的一个服务器Data服上,在SceneFactory添加组件的时候生成的ID和Data服在StartSceneConfig@s.xlsx文件配置的ID相同。
比如你本地Data服配置的ID是10,那EmailManager组件保存到MongoDB中的ID也是10。因为修改了线上版本的配置文件,线上Data服配置的ID改为8,那EmailManager组件保存到MongoDB中的ID就多了一个8。
所以数据库中就会有ID为10的数据和ID为8的数据 。
体验服在程序启动的时候把ID为10和8的数据同时拉取了,之后运行过程中又是以ID为8的数据进行保存,所以服务器重启的时候又拉了一遍ID为10的数据造成不一致,让逻辑误以为邮件没领又重新拉了一遍。
解决办法
解决办法就是在读取和保存数据的时候按照配置文件的ID进行动态操作(本地就用10,线上就用8)
public static async ETTask LoadEmailInfo(this EmailManagerComponent self)
{
//按照配置文件ID进行读取数据: d.Id == self.DomainScene().Id (self.DomainScene()是Data)
var EmailManagerList = await DBManagerComponent.Instance.GetZoneDB(self.DomainZone()).Query<EmailManagerComponent>(d => true && d.Id == self.DomainScene().Id,collection:"EmailManagerComponent");
}
//也可以通过条件: self.DomainScene().Name == "Data" 来筛选你要操作的数据