引擎在网络序列化UEnum的时候,Linux DS 和 编辑器上面有区别,在于找Enum的 _Max是否存在实现有差别?

编辑器连 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版本

您好,看了一下您的分析,应该都是正确的

我可以提交一个jira问题跟踪, 不过这种问题优先级会比较低, 短期内肯定不会修复​

直接规范命名改为大写的 _MAX​可以修复建议先这样处理

编辑器下做这种区分是为了UI上一些​名字可以正确显示大小写, 例如创建资产的名字, 为了FName比较的性能在runtime是不区分大小写的

请教一下,不区分大小写,能节省多少性能。

另外,引擎对UEnum加一个MAX的意图是啥?

在网络序列化UEnum时,每次都得去遍历找enum的最大值,感觉这个也没必要,最大值可以提前缓存起来?

我表述不准确, 这里性能是指内存/包体的占用, 因为FName对比其实就是一个数字的对比

目前引擎里namemap已经非常大了,如果FName区分大小写,会变得更大

「引擎对UEnum加一个MAX的意图是啥」这个应该是UE4.13的时候添加的,记录来看当时是为了修复一个蓝图 nativization 的 bug 添加的…注释里其实是标注了需要移除掉,不过一直没有去处理…

「每次都得去遍历找enum的最大值,感觉这个也没必要」如果profile发现影响比较大, 这个是可以优化一下,比如直接对UEnum里的Names排序,都不需要缓存…不过目前UE5里也还没有处理…