Unreal FFastArray实现原理

文章目录

    • 使用示例
    • 原理说明
    • 修改操作
    • 序列化保存部分
    • 序列化读取部分

使用示例

// 结构体继承FFastArraySerializerItem
USTRUCT()
struct FXXX: public FFastArraySerializerItem
{GENERATED_USTRUCT_BODY()UPROPERTY()int32 XXX;// 客户端同步回调void PreReplicatedRemove();void PostReplicatedAdd();void PostReplicatedChange();
};// 数组包成一个类,继承FFastArraySerializerItem
USTRUCT()
struct FXXXContainer: public FFastArraySerializer
{GENERATED_USTRUCT_BODY()UPROPERTY()TArray<FXXX> Items;bool NetDeltaSerialize(FNetDeltaSerializeInfo & DeltaParms){return FFastArraySerializer::FastArrayDeltaSerialize<FXXX, FXXXContainer>(Items, DeltaParms, *this);}template< typename Type, typename SerializerType >bool ShouldWriteFastArrayItem(const Type& Item, const bool bIsWritingOnClient){// Type is FXXX, 定制是否需要同步...if (bIsWritingOnClient){return Item.ReplicationID != INDEX_NONE;}return true;}void AddItem(FXX XX){FXX& Item = Items.Add_GetRef(MoveTemp(XX));// 增加或修改元素后,需要对相应的元素手动 MarkDirtyMarkItemDirty(Item);}void RemoveItem(const FXX& XX){for(int32 Idx = 0; Idx < Items.Num(); ++Idx){if(Items[Idx] == XX){Items.RemoveAt(Idx);// 删除后,需要手动 MarkArrayDirtyMarkArrayDirty();return}}}
};template<>
struct TStructOpsTypeTraits<FXXXContainer> : public TStructOpsTypeTraitsBase2<FXXXContainer>
{enum{WithNetDeltaSerializer = true,};
};

原理说明

可以看下FastArraySerializer.h内的说明,大致意思为:

  • 类似数组和结构体这种不定内存的结构,需要使用 NetDeltaSerialize 进行差异序列化
  • 结构体的默认的差异序列化就是发送不用的属性,也就是Generic Delta Replication
  • 开发者可以通过将WithNetDeltaSerializer 改为 true,定制自己的 NetDeltaSerialize
  • 对于比较通用的TArray,也提供了 Fast TArray Replication 的方式

对于 FFastArraySerializer,ArrayReplicationKey 用于判定整个数组内是否存在变化(ConditionalCreateNewDeltaState内判断)
对于 FFastArraySerializerItem,ReplicationID 为该Item的标识,ReplicationKey 用于判定是否有修改。

修改操作

void MarkItemDirty(FFastArraySerializerItem & Item)if (Item.ReplicationID == INDEX_NONE)Item.ReplicationID = ++IDCounter;// 标记Item有修改Item.ReplicationKey++;MarkArrayDirty();void MarkArrayDirty()IncrementArrayReplicationKey();// 标记数组有修改ArrayReplicationKey++;

序列化保存部分

template<typename Type, typename SerializerType >
bool FFastArraySerializer::FastArrayDeltaSerialize(TArray<Type> &Items, FNetDeltaSerializeInfo& Parms, SerializerType& ArraySerializer)// if (Parms.Writer)TMap<int32, int32> * OldMap = nullptr;int32 BaseReplicationKey = INDEX_NONE;if (Parms.OldState)OldMap = &((FNetFastTArrayBaseState*)Parms.OldState)->IDToCLMap;// 上次同步的序号BaseReplicationKey = ((FNetFastTArrayBaseState*)Parms.OldState)->ArrayReplicationKey;// 判断是否需要同步if (Parms.OldState)if (!Helper.ConditionalCreateNewDeltaState(*OldMap, BaseReplicationKey))if (ArraySerializer.ArrayReplicationKey == BaseReplicationKey)// 没有变化时使用回旧的Stateif (Parms.OldState)*Parms.NewState = Parms.OldState->AsShared();return false;FNetFastTArrayBaseState * NewState = new FNetFastTArrayBaseState();TMap<int32, int32>& NewMap = NewState->IDToCLMap;NewState->ArrayReplicationKey = ArraySerializer.ArrayReplicationKey;if (Parms.bIsInitializingBaseFromDefault)// CDO的处理Helper.BuildChangedAndDeletedBuffersFromDefault(NewMap, ChangedElements);else:Helper.BuildChangedAndDeletedBuffers(NewMap, OldMap, ChangedElements, Header.DeletedIndices);NewState->ArrayReplicationKey = ArraySerializer.ArrayReplicationKey;Header.NumChanged = ChangedElements.Num();Helper.WriteDeltaHeader(Header);for (auto It = ChangedElements.CreateIterator(); It; ++It)void* ThisElement = &Items[It->Idx];uint32 ID = It->ID;// 写入变化Item的IDWriter << ID;Parms.Struct = InnerStruct;Parms.Data = ThisElement;// 写入变化的数据Parms.NetSerializeCB->NetSerializeStruct(Parms);// 直接序列化目标if (EnumHasAnyFlags(CachedRequestState.Struct->StructFlags, STRUCT_NetSerializeNative))UScriptStruct::ICppStructOps* CppStructOps = CachedRequestState.Struct->GetCppStructOps();if (!CppStructOps->NetSerialize(Ar, Params.Map, bSuccess, Params.Data)elseCachedRequestState.RepLayout->SerializePropertiesForStruct(Params.Struct, Ar, Params.Map, Params.Data, Params.bOutHasMoreUnmapped, Params.Object);

处理差异:

template<typename Type, typename SerializerType>
void FFastArraySerializer::TFastArraySerializeHelper<Type, SerializerType>::BuildChangedAndDeletedBuffers(TMap<int32, int32>& NewIDToKeyMap,const TMap<int32, int32>* OldIDToKeyMap,TArray<FFastArraySerializer_FastArrayDeltaSerialize_FIdxIDPair, TInlineAllocator<8>>& ChangedElements,TArray<int32, TInlineAllocator<8>>& DeletedElements)// 遍历所有Item,得出还需要保持写入的数量(Remove了,或者ShouldWriteFastArrayItem返回false了)const int32 NumConsideredItems = CalcNumItemsForConsideration();// 与原来的数量的差就是需要删除的数量int32 DeleteCount = (OldIDToKeyMap ? OldIDToKeyMap->Num() : 0) - NumConsideredItems;for (int32 i = 0; i < Items.Num(); ++i)Type& Item = Items[i];// 新的需要写入的表NewIDToKeyMap.Add(Item.ReplicationID, Item.ReplicationKey);// 读出之前的ArrayReplicationKeyconst int32* OldValuePtr = OldIDToKeyMap ? OldIDToKeyMap->Find(Item.ReplicationID) : NULL;if (OldValuePtr)// ArrayReplicationKey相同表示当轮并没有修改if (*OldValuePtr == Item.ReplicationKey)continueelseChangedElements.Add(FFastArraySerializer_FastArrayDeltaSerialize_FIdxIDPair(i, Item.ReplicationID));else// 新加的也算到Change内ChangedElements.Add(FFastArraySerializer_FastArrayDeltaSerialize_FIdxIDPair(i, Item.ReplicationID));// 因为新加了,所以原来的删除应该更多一个++DeleteCount;// 再次遍历找出删除的ItemIDif (DeleteCount > 0 && OldIDToKeyMap)for (auto It = OldIDToKeyMap->CreateConstIterator(); It; ++It)if (!NewIDToKeyMap.Contains(It.Key()))DeletedElements.Add(It.Key());if (--DeleteCount <= 0)break;

序列化读取部分

template<typename Type, typename SerializerType >
bool FFastArraySerializer::FastArrayDeltaSerialize(TArray<Type> &Items, FNetDeltaSerializeInfo& Parms, SerializerType& ArraySerializer)// if (Parms.Reader)FFastArraySerializerHeader Header;for(int32 i = 0; i < Header.NumChanged; ++i)Reader << ElementID;// 读取ReplicationID对应的下标int32* ElementIndexPtr = ArraySerializer.ItemMap.Find(ElementID);if (!ElementIndexPtr)// 新建项ThisElement = &Items.AddDefaulted_GetRef();ThisElement->ReplicationID = ElementID;ElementIndex = Items.Num()-1;ArraySerializer.ItemMap.Add(ElementID, ElementIndex);else// 修改项ElementIndex = *ElementIndexPtr;ThisElement = &Items[ElementIndex];ChangedIndices.Add(ElementIndex);// 标记是这次同步的修改ThisElement->MostRecentArrayReplicationKey = Header.ArrayReplicationKey;// 客户端修改标记ThisElement->ReplicationKey++;// 序列化目标Parms.NetSerializeCB->NetSerializeStruct(Parms);// 删除并触发各种Item和Contrainer的回调Helper.template PostReceiveCleanup<>(Header, ChangedIndices, AddedIndices, ArraySerializer.GuidReferencesMap);Helper.CallPostReplicatedReceiveOrNot(OldNumItems);

客户端删除并触发各种Item的回调

template<typename Type, typename SerializerType>
template<typename GuidMapType>
void FFastArraySerializer::TFastArraySerializeHelper<Type, SerializerType>::PostReceiveCleanup(FFastArraySerializerHeader& Header,TArray<int32, TInlineAllocator<8>>& ChangedIndices,TArray<int32, TInlineAllocator<8>>& AddedIndices,GuidMapType& GuidMap)// 在ReadDeltaHeader内已经转化为下标了for (int32 idx : Header.DeletedIndices)if (Items.IsValidIndex(idx))Items[idx].PreReplicatedRemove(ArraySerializer);ArraySerializer.PreReplicatedRemove(Header.DeletedIndices, FinalSize);for (int32 idx : AddedIndices)Items[idx].PostReplicatedAdd(ArraySerializer);ArraySerializer.PostReplicatedAdd(AddedIndices, FinalSize);for (int32 idx : ChangedIndices)Items[idx].PostReplicatedChange(ArraySerializer);ArraySerializer.PostReplicatedChange(ChangedIndices, FinalSize);if (Header.DeletedIndices.Num() > 0)Header.DeletedIndices.Sort();for (int32 i = Header.DeletedIndices.Num() - 1; i >= 0; --i)Items.RemoveAtSwap(DeleteIndex, 1, false);// 下标变了,需要之后重新构建,参考ConditionalRebuildItemMap// Clear the map now that the indices are all shifted around. This kind of sucks, we could use slightly better data structures here I think.// This will force the ItemMap to be rebuilt for the current Items arrayArraySerializer.ItemMap.Empty();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/596408.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

某和医院招采系统web端数据爬取, 逆向js

目标网址:https://zbcg.sznsyy.cn/homeNotice 测试时间: 2024-01-03 1 老规矩,打开Chrome无痕浏览,打开链接,监测网络,通过刷新以及上下翻页可以猜测出数据的请求是通过接口frontPageAnnouncementList获取的,查看返回可以看出来数据大概率是经过aes加密的,如图: 通过查看该请…

智能算力网络Parrot的三个基石假设

任何一个生态系统的发展&#xff0c;包括科学知识体系的发展&#xff0c;都离不开第一性原理层面的科学假设。 欧几里得在五个公理的假设基础之上&#xff0c;推导出416条定律&#xff0c;创立了欧氏几何&#xff0c;推动了人类征服大自然的进程。 王坚博士的三个假设&#x…

014、枚举与模式匹配

枚举类型&#xff0c;通常也被简称为枚举&#xff0c;它允许我们列举所有可能的值来定义一个类型。在本篇文章中&#xff0c;我们首先会定义并使用一个枚举&#xff0c;以向你展示枚举是如何连同数据来一起编码信息的。 接着&#xff0c;我们会讨论一个特别有用的枚举&#xff…

提升设计效率:全面了解如何使用Figma插件

Figma组件库包括颜色、字体、图标、按钮、阴影、圆角、间距等。当Figma组件库的样式和Figma组件达到一定数量时&#xff0c;将难以维护&#xff0c;设计和开发的对接成本将大大提高。Figma可以在同一母版下单独设置样式&#xff0c;而不影响与母版之前的关系&#xff0c;这是Sk…

JAVA进化史: JDK13特性及说明

JDK 13于2019年9月发布。这个版本引入了一些新特性和改进&#xff0c;以下是其中一些主要特性: 动态的CDS归档文件 引入了动态的Class Data Sharing&#xff08;CDS&#xff09;归档文件&#xff0c;以提高Java应用程序的启动性能。它允许多个Java进程共享相同的已经被预先加…

9.java——(杂例)组合,代理,向上转型static,fianl,关键字(有道云笔记复制粘贴,大家整体性的把握)

组合——内部有类&#xff08;心中有对象&#xff01;&#xff01;&#xff01;&#xff09;&#xff08;足球 和足球运动员梅西和脚下的足球一样&#xff09; has和is的区别&#xff0c;has是组合&#xff0c;是有&#xff0c;持有的意思&#xff1b;is是继承&#xff0c;是…

springboot整合mongodb批量修改和添加索引

1&#xff1a;创建字段索引 collection 是集合名称&#xff0c;keys 是一个包含一个或多个字段名&#xff0c;options是排序方向&#xff0c;1正序&#xff0c;-1倒叙 db.collection.createIndex(keys, options)#单字段 db.collection.createIndex(name: 1)#多字段 db.collec…

C++八股学习心得.3

1.C 数组 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素&#xff0c;最高的地址对应最后一个…

实时计算大作业kafka+zookeeper+storm+dataV

第一章 总体需求 1.1.课题背景 近年来&#xff0c;大数据称为热门词汇&#xff0c;大数据分析随着互联网技术的发展愈加深入电商营销之 中&#xff0c;越来越多的电商企业利用大数据分析技术&#xff0c;利用信息化对产业发展营销方向进行确定&#xff0c; 对电子商务行…

双碳管理系统任务需求分析(第10套)

需求规格说明书 一、引言 &#xff08;一&#xff09;项目背景 编写本需求规格说明书的目的是为了详细呈现碳足迹产品需求和系统的功能描述&#xff0c;以进一步定制应用软件系统开发的细节问题&#xff0c;便于与项目开发协调工作。本文档面向的读者主要是项目委托单位的管…

git rebase(变基)应用场景

文章目录 git rebase(变基)应用场景1.git rebase -i HEAD~3 git rebase(变基)应用场景 使得提交记录变得简洁 现在我们模拟我们有多次提交记录&#xff0c;本地仓库有三条提交 整合成一条提交记录 1.git rebase -i HEAD~3 提交记录合并 HEAD~3合并三条记录 执行之后 然后把…

事实就是这么残酷,分享一个案例投资者是怎么一步步失败

都说交易市场要学会斗智斗勇&#xff0c;但fpmarkets澳福提醒交易者要始终记住&#xff0c;买的没有卖的精&#xff0c;下面就分享一个案例&#xff0c;让各位投资者知道现实就是这么残酷&#xff0c;一些无良的资本是怎么一步步让投资者失败的。 当在整个交易市场中渐渐地&am…

清风数学建模笔记-时间序列分析

内容&#xff1a;时间预测分析 一.时间序列 1.时点时间序列 2.时期时间序列&#xff1a;可相加 二.时间趋势分解 1.季节趋势 拓展&#xff1a;百度指数&#xff1a; 2.循环变动趋势&#xff08;和季节很像但是是以年为单位&#xff09; 3.不规则变动趋势&#xff08;像扰…

《深入理解C++11:C++11新特性解析与应用》笔记八

第八章 融入实际应用 8.1 对齐支持 8.1.1 数据对齐 c可以通过sizeof查询数据的长度&#xff0c;但是没有对对齐方式有关的查询或者设定进行标准化。c11标准定义的alignof函数可以查看数据的对齐方式。 现在的计算机通常会支持许多向量指令&#xff0c;4组8字节的浮点数据&a…

极值统计学及其在相关领域中的技术应用

受到气候变化、温室效应以及人类活动等因素的影响&#xff0c;自然界中极端高温、极端环境污染、大洪水和大暴雨等现象的发生日益频繁&#xff1b;在人类社会中&#xff0c;股市崩溃、金融危机等极端情况也时有发生&#xff1b;今年的新冠疫情就是非常典型的极端现象。研究此类…

离线Vscode 安装完成后 添加到右键菜单

复制下面代码&#xff0c;修改文件后缀名为&#xff1a;reg Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\VSCode] "Open with Code" "Icon""D:\\_Porgram_IT\\VsCode\\Code.exe"[HKEY_CLASSES_ROOT\*\shell\VSCode\comman…

AI:110-基于深度学习的药物分子结构生成与预测

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

填充点云孔洞(较大的洞)halcon算法

前言 很多时候,一些小洞可以通过平滑算法,或者三角化算法的参数调整,即可对较小的孔洞进行填充,但是较大的洞却很难通过上面的算法进行填充。 下面介绍一种填充孔洞的思路: 步骤一:对点云进行滤波处理,找到孔洞所在平面 本文为了更直观的进行讲解,去掉了去除噪声和…

数读中国这十年:研发经费超3万亿元 创新引领显成效

摘要&#xff1a;本文转载自新华社。 新华社北京12月22日电 题&#xff1a;研发经费超3万亿元 创新引领显成效 新华社记者 陈炜伟、潘德鑫 这是2023年6月11日在青海省海西蒙古族藏族自治州格尔木市拍摄的一座光热电站&#xff08;无人机照片&#xff09;。新华社记者 张宏…

Linux高级玩家必备sos_report-尚文网络xUP楠哥

进Q群11372462领取专属报名福利! # 什么是sos sos 是红帽技术支持工程师在执行任务时的常见起点分析 RHEL 系统的服务请求。 该实用程序提供了一种标准化的方式来收集红帽支持工程师可以在整个调查过程中参考的诊断信息支持案例中报告的问题。 使用 sos 报告实用程序有助于确…