编辑器连 Linux DS 的时候,序列化Enum 在编辑器上会crash。
这个Enum需要具备:
枚举值有四个,最大值是3。并且没有 _MAX 的值,但是有_Max的值。这个枚举属性标记为可同步的,在客户端收到这个同步包就会出现FArchive overflow,我们这边把 overflow 在测试环境改成crash了。
例如枚举的定义如下:注意最后一个是_Max,其值是3。
UENUM(BlueprintType)
enum class ETestA : uint8
{
ETestA_None,
ETestA_One,
ETestA_Two,
ETestA_Max,
};
引擎在序列化enum的时候,会去找max值,见 FEnumProperty::NetSerializeItem 函数,发现在linux ds上找的最大值是 ETestA_Max,而在Windows编辑器上引擎会自动加一个ETestA_MAX 的最大值,导致两边找的最大值一个是3一个是4,在序列化的时候使用bit数正好一个是2bit一个是3bit,导致编辑器在收到包后解析失败。
判断是否要加一个_MAX的值的函数中:UEnum::ContainsExistingMax()
bool UEnum::ContainsExistingMax() const
{
if (GetIndexByName(*GenerateFullEnumName(TEXT(“MAX”)), EGetByNameFlags::CaseSensitive) != INDEX_NONE)
{
return true;
}
FName MaxEnumItem = *GenerateFullEnumName(*(GenerateEnumPrefix() + TEXT(“_MAX”)));
if (GetIndexByName(MaxEnumItem, EGetByNameFlags::CaseSensitive) != INDEX_NONE)
{
return true;
}
return false;
}
这个函数中去找是否有Prefix_MAX的枚举值,在Linux ds上找到了 _Max,但是在Windows上找不到,然后自动加了一个_MAX的值。
并且在Linux DS 上打印上述函数中的MaxEnumItem 的字符串(调用的FName::Tostring函数),打印出来是 _Max 值,很奇怪,感觉是FName 在不同端,大小写处理的不一样。
然后看了一下 FNameEntryId FNamePool::Store(FNameStringView Name) 的实现,其中有个宏 WITH_CASE_PRESERVING_NAME 这个貌似是编辑器下才生效,不知道是否和这个有关系:
using FNameComparisonValue = FNameValue<ENameCase::IgnoreCase>;
#if WITH_CASE_PRESERVING_NAME
using FNameDisplayValue = FNameValue<ENameCase::CaseSensitive>;
#endif
ComparisonValue还是个IgnoreCase的
FName::IsEqual函数会调用GetDisplayIndexFast,而这个函数的实现如下,同样用了宏处理,在判断Fname是否相等的情况会出现差别
FORCEINLINE FNameEntryId GetDisplayIndexFast() const
{
#if WITH_CASE_PRESERVING_NAME
return DisplayIndex;
#else
return ComparisonIndex;
#endif
}
总结:
1.在linux ds 上应该是存储FName的时候,已经存了_Max的值,后续再去生成了_MAX的Fname时,直接取了事先生成的_Max的FName。所以打印的_MAX 的tostring值 是 _Max
2.是我上面说的路径导致的吗,后续有打算修复这种情况吗
3.我们是有编辑器连Linux ds 的需求,只在内部测试使用到。
4.我们临时修复这个问题,可以把枚举的定义中的_Max改成_MAX来解决序列化不一致的情况。但不是根本性解决问题的手段
5.为什么要区分编辑器和其他对FName的处理,这里区别对待,影响了网络序列化?
另外,引擎是4.26版本