Ue4 NetworkGUID

1. NetworkGUID

NetworkGUID有何作用?

在网络同步的过程中,在传递一个UObject类型的指针时,这个UObject是怎么传递的?

这个处理就需要通过FNetworkGUID了。服务器在同步一个对象引用(指针)的时候,会给其分配专门的FNetworkGUID并通过网络进行发送。客户端上通过识别这个ID,就可以找到对应的UObject

服务器在同步一个UObject的时候,需要对一个UObject序列化(UPackageMapClient::SerializeObject ),

而在序列化对象前,需要检查GUID缓存表(TMap<FNetworkGUID, FNetGuidCacheObject>ObjectLookup),如果GUID缓存表里面有,证明已经分配过,反之则需要分配一个GUID,并写到数据流里面。不过一般来说,GUID分配并不是在发送数据的时候才进行,而是在创建FObjectReplicator(SetChannelActor)的时候(通过NetDriverGuidCache分配)。

那么这个GUID是在什么时候分配的?服务器如何分配,客户端如何分配的呢?

2. 服务器分配GUID

服务器中是如何分配GUID的呢?

一开始是在SetChannelActor中创建FObjectReplicator的时候通过GetOrAssignNetGUID创建并分配到GuidCache中去。

在这里插入图片描述

通过上图,我们可以看到,如果我们要注册一个UObject的GUID,不仅仅是要注册自己的GUID,还得注册自己Outer的GUID(如果Outer的GUID已经注册,则不需要注册)。

而且,GUID还分动态和非动态(动态GUID为偶数,静态GUID为奇数)

 
FNetworkGUID FNetGUIDCache::AssignNewNetGUID_Server( UObject* Object )
{
	check( IsNetGUIDAuthority() );

#define COMPOSE_NET_GUID( Index, IsStatic )	( ( ( Index ) << 1 ) | ( IsStatic ) )
#define ALLOC_NEW_NET_GUID( IsStatic )		( COMPOSE_NET_GUID( ++UniqueNetIDs[ IsStatic ], IsStatic ) )

	// Generate new NetGUID and assign it
	const int32 IsStatic = IsDynamicObject( Object ) ? 0 : 1;

	const FNetworkGUID NewNetGuid( ALLOC_NEW_NET_GUID( IsStatic ) );

	RegisterNetGUID_Server( NewNetGuid, Object );

	return NewNetGuid;
}

最终注册到GuidCache的注册表中

在这里插入图片描述

3. 客户端分配GUID

客户端分配GUID,是通关接收服务器发送过来的Bunch反序列化出来的GUID,是通过两种方法来注册到注册表中。

第一种方法

是通过接收服务器发送过来的GUID和PathName来分配GUID。

在UPackageMapClient::InternalLoadObject中,通过反序列化出GUID和PathName,然后通过PathName去寻找到UObject,然后再注册进注册表中。

FNetworkGUID UPackageMapClient::InternalLoadObject( FArchive & Ar, UObject *& Object, const int InternalLoadObjectRecursionCount )
{
	// ----------------
	// Read the NetGUID
	// ----------------
	FNetworkGUID NetGUID;
	Ar << NetGUID;
	NET_CHECKSUM_OR_END( Ar );

	if ( Ar.IsError() )
	{
		Object = NULL;
		return NetGUID;
	}
	if ( !NetGUID.IsValid() )
	{
		Object = NULL;
		return NetGUID;
	}
	// ----------------	
	// Try to resolve NetGUID
	// ----------------	
	if ( NetGUID.IsValid() && !NetGUID.IsDefault() )
	{		Object = GetObjectFromNetGUID( NetGUID, GuidCache->IsExportingNetGUIDBunch );
		UE_CLOG( !bSuppressLogs, LogNetPackageMap, Log, TEXT( "InternalLoadObject loaded %s from NetGUID <%s>" ), Object ? *Object->GetFullName() : TEXT( "NULL" ), *NetGUID.ToString() );
	}
	// ----------------	
	// Read the full if its there
	// ----------------	
	FExportFlags ExportFlags;

	if ( NetGUID.IsDefault() || GuidCache->IsExportingNetGUIDBunch )
	{
		Ar << ExportFlags.Value;

		if ( Ar.IsError() )
		{
			Object = NULL;
			return NetGUID;
		}
	}

	if ( GuidCache->IsExportingNetGUIDBunch )
	{
		GuidCache->ImportedNetGuids.Add( NetGUID );
	}

	if ( ExportFlags.bHasPath )
	{
		UObject* ObjOuter = NULL;

		FNetworkGUID OuterGUID = InternalLoadObject( Ar, ObjOuter, InternalLoadObjectRecursionCount + 1 );

		FString PathName;
		uint32	NetworkChecksum = 0;
		Ar << PathName;
		if ( ExportFlags.bHasNetworkChecksum )
		{
			Ar << NetworkChecksum;
		}
		const bool bIsPackage = NetGUID.IsStatic() && !OuterGUID.IsValid();

		if ( Ar.IsError() )
		{
			UE_LOG( LogNetPackageMap, Error, TEXT( "InternalLoadObject: Failed to load path name" ) );
			Object = NULL;
			return NetGUID;
		}

		// Remap name for PIE
		GEngine->NetworkRemapPath( Connection->Driver, PathName, true );
		//
		// At this point, only the client gets this far
		//
		const bool bIgnoreWhenMissing = ExportFlags.bNoLoad;
		// Register this path and outer guid combo with the net guid
		GuidCache->RegisterNetGUIDFromPath_Client( NetGUID, PathName, OuterGUID, NetworkChecksum, ExportFlags.bNoLoad, bIgnoreWhenMissing );
		// Try again now that we've registered the path
		Object = GuidCache->GetObjectFromNetGUID( NetGUID, GuidCache->IsExportingNetGUIDBunch );
    }	

	return NetGUID;
}

通过上述代码,我们可以看出,在InternalLoadObject中先反序列化获取其GUID,然后递归注册Outer的GUID,接着反序列出PathName,通过Outer和PathName来找到自身Object。

本文来源于网络整理,由作者美梦成真发布,其观点不代表番茄鱼的立场,转载请联系原作者。
5

发表评论