EM_Task/CoreUObject/Private/UObject/CoreNet.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

564 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
UnCoreNet.cpp: Core networking support.
=============================================================================*/
#include "UObject/CoreNet.h"
#include "UObject/UnrealType.h"
#include "Misc/NetworkVersion.h"
DEFINE_LOG_CATEGORY_STATIC(LogCoreNet, Log, All);
/*-----------------------------------------------------------------------------
FClassNetCache implementation.
-----------------------------------------------------------------------------*/
FClassNetCache::FClassNetCache()
{
}
FClassNetCache::FClassNetCache(const UClass* InClass): Class(InClass)
{
}
void FClassNetCacheMgr::SortProperties(TArray<FProperty*>& Properties) const
{
// Sort NetProperties so that their ClassReps are sorted by memory offset
struct FCompareUFieldOffsets
{
FORCEINLINE bool operator()(FProperty& A, FProperty& B) const
{
// Ensure stable sort
if (A.GetOffset_ForGC() == B.GetOffset_ForGC())
{
return A.GetName() < B.GetName();
}
return A.GetOffset_ForGC() < B.GetOffset_ForGC();
}
};
Sort(Properties.GetData(), Properties.Num(), FCompareUFieldOffsets());
}
uint32 FClassNetCacheMgr::SortedStructFieldsChecksum(const UStruct* Struct, uint32 Checksum) const
{
// Generate a list that we can sort, to make sure we process these deterministically
TArray<FProperty*> Fields;
for (TFieldIterator<FProperty> It(Struct); It; ++It)
{
if (It->PropertyFlags & CPF_RepSkip)
{
continue;
}
Fields.Add(*It);
}
// Sort them
SortProperties(Fields);
// Evolve the checksum on the sorted list
for (auto Field: Fields)
{
Checksum = GetPropertyChecksum(Field, Checksum, true);
}
return Checksum;
}
uint32 FClassNetCacheMgr::GetPropertyChecksum(const FProperty* Property, uint32 Checksum, const bool bIncludeChildren) const
{
if (bDebugChecksum)
{
UE_LOG(LogCoreNet, Warning, TEXT("%s%s [%s] [%u] [%u]"), FCString::Spc(2 * DebugChecksumIndent), *Property->GetName().ToLower(), *Property->GetClass()->GetName().ToLower(), Property->ArrayDim, Checksum);
}
Checksum = FCrc::StrCrc32(*Property->GetName().ToLower(), Checksum); // Evolve checksum on name
Checksum = FCrc::StrCrc32(*Property->GetCPPType(nullptr, 0).ToLower(), Checksum); // Evolve by property type
Checksum = FCrc::StrCrc32(*FString::Printf(TEXT("%u"), Property->ArrayDim), Checksum); // Evolve checksum on array dim (to detect when static arrays change size)
if (bIncludeChildren)
{
const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property);
// Evolve checksum on array inner
if (ArrayProperty != NULL)
{
return GetPropertyChecksum(ArrayProperty->Inner, Checksum, bIncludeChildren);
}
const FStructProperty* StructProperty = CastField<FStructProperty>(Property);
// Evolve checksum on property struct fields
if (StructProperty != NULL)
{
if (bDebugChecksum)
{
UE_LOG(LogCoreNet, Warning, TEXT("%s [%s] [%u]"), FCString::Spc(2 * DebugChecksumIndent), *StructProperty->Struct->GetName().ToLower(), Checksum);
}
// Evolve checksum on struct name
Checksum = FCrc::StrCrc32(*StructProperty->Struct->GetName().ToLower(), Checksum);
const_cast<FClassNetCacheMgr*>(this)->DebugChecksumIndent++;
Checksum = SortedStructFieldsChecksum(StructProperty->Struct, Checksum);
const_cast<FClassNetCacheMgr*>(this)->DebugChecksumIndent--;
}
}
return Checksum;
}
uint32 FClassNetCacheMgr::GetFunctionChecksum(const UFunction* Function, uint32 Checksum) const
{
// Evolve checksum on function name
Checksum = FCrc::StrCrc32(*Function->GetName().ToLower(), Checksum);
// Evolve the checksum on function flags
Checksum = FCrc::StrCrc32(*FString::Printf(TEXT("%u"), (uint32)Function->FunctionFlags), Checksum);
#if 0 // This is disabled now that we have backwards compatibility for RPC parameters working in replays
TArray< FProperty * > Parms;
for ( TFieldIterator< FProperty > It( Function ); It && ( It->PropertyFlags & ( CPF_Parm | CPF_ReturnParm ) ) == CPF_Parm; ++It )
{
Parms.Add( *It );
}
// Sort parameters by offset/name
SortProperties( Parms );
// Evolve checksum on sorted function parameters
for ( FProperty* Parm : Parms )
{
Checksum = GetPropertyChecksum( Parm, Checksum, true );
}
#endif
return Checksum;
}
uint32 FClassNetCacheMgr::GetFieldChecksum(const UField* Field, uint32 Checksum) const
{
if (Cast<UFunction>(Field) != NULL)
{
return GetFunctionChecksum((UFunction*)Field, Checksum);
}
UE_LOG(LogCoreNet, Warning, TEXT("GetFieldChecksum: Unknown field: %s"), *Field->GetName());
return Checksum;
}
const FClassNetCache* FClassNetCacheMgr::GetClassNetCache(UClass* Class)
{
FClassNetCache* Result = ClassFieldIndices.FindRef(Class);
if (!Result)
{
Class->SetUpRuntimeReplicationData();
Result = ClassFieldIndices.Add(Class, new FClassNetCache(Class));
Result->Super = NULL;
Result->FieldsBase = 0;
Result->ClassChecksum = 0;
if (Class->GetSuperClass())
{
Result->Super = GetClassNetCache(Class->GetSuperClass());
Result->FieldsBase = Result->Super->GetMaxIndex();
Result->ClassChecksum = Result->Super->ClassChecksum;
}
Result->Fields.Empty(Class->NetFields.Num());
TArray<FProperty*> Properties;
Properties.Empty(Class->ClassReps.Num() - Class->FirstOwnedClassRep);
for (int32 i = Class->FirstOwnedClassRep; i < Class->ClassReps.Num(); i++)
{
// Add each net field to cache, and assign index/checksum
FProperty* Property = Class->ClassReps[i].Property;
check(Property);
Properties.Add(Property);
// Get individual checksum
const uint32 Checksum = GetPropertyChecksum(Property, 0, false);
// Get index
const int32 ThisIndex = Result->GetMaxIndex();
// Add to cached fields on this class
Result->Fields.Add(FFieldNetCache(Property, ThisIndex, Checksum));
// Skip over static array properties.
i += (Property->ArrayDim - 1);
}
for (int32 i = 0; i < Class->NetFields.Num(); i++)
{
// Add each net field to cache, and assign index/checksum
UField* Field = Class->NetFields[i];
// Get individual checksum
const uint32 Checksum = GetFieldChecksum(Field, 0);
// Get index
const int32 ThisIndex = Result->GetMaxIndex();
// Add to cached fields on this class
Result->Fields.Add(FFieldNetCache(Field, ThisIndex, Checksum));
}
Result->Fields.Shrink();
// Add fields to the appropriate hash maps
for (TArray<FFieldNetCache>::TIterator It(Result->Fields); It; ++It)
{
Result->FieldMap.Add(It->Field.GetRawPointer(), &*It);
if (Result->FieldChecksumMap.Contains(It->FieldChecksum))
{
UE_LOG(LogCoreNet, Error, TEXT("Duplicate checksum: %s, %u"), *It->Field.GetFullName(), It->FieldChecksum);
}
Result->FieldChecksumMap.Add(It->FieldChecksum, &*It);
}
// Initialize class checksum (just use properties for this)
SortProperties(Properties);
for (auto Property: Properties)
{
Result->ClassChecksum = GetPropertyChecksum(Property, Result->ClassChecksum, true);
}
}
return Result;
}
void FClassNetCacheMgr::ClearClassNetCache()
{
for (auto It = ClassFieldIndices.CreateIterator(); It; ++It)
{
delete It.Value();
}
ClassFieldIndices.Empty();
}
void FClassNetCacheMgr::CountBytes(FArchive& Ar) const
{
ClassFieldIndices.CountBytes(Ar);
for (const auto& ClassFieldPair: ClassFieldIndices)
{
if (ClassFieldPair.Value != nullptr)
{
Ar.CountBytes(sizeof(FClassNetCache), sizeof(FClassNetCache));
ClassFieldPair.Value->CountBytes(Ar);
}
}
}
void FClassNetCache::CountBytes(FArchive& Ar) const
{
Fields.CountBytes(Ar);
FieldMap.CountBytes(Ar);
FieldChecksumMap.CountBytes(Ar);
}
/*-----------------------------------------------------------------------------
UPackageMap implementation.
-----------------------------------------------------------------------------*/
bool UPackageMap::SerializeName(FArchive& Ar, FName& InName)
{
return StaticSerializeName(Ar, InName);
}
bool UPackageMap::StaticSerializeName(FArchive& Ar, FName& InName)
{
if (Ar.IsLoading())
{
uint8 bHardcoded = 0;
Ar.SerializeBits(&bHardcoded, 1);
if (bHardcoded)
{
// replicated by hardcoded index
uint32 NameIndex;
if (Ar.EngineNetVer() < HISTORY_CHANNEL_NAMES)
{
Ar.SerializeInt(NameIndex, MAX_NETWORKED_HARDCODED_NAME + 1);
}
else
{
Ar.SerializeIntPacked(NameIndex);
}
if (NameIndex < NAME_MaxHardcodedNameIndex)
{
InName = EName(NameIndex);
// hardcoded names never have a Number
}
else
{
Ar.SetError();
}
}
else
{
// replicated by string
FString InString;
int32 InNumber;
Ar << InString << InNumber;
InName = FName(*InString, InNumber);
}
}
else if (Ar.IsSaving())
{
const EName* InEName = InName.ToEName();
uint8 bHardcoded = InEName && ShouldReplicateAsInteger(*InEName);
Ar.SerializeBits(&bHardcoded, 1);
if (bHardcoded && /* silence static analyzer */ InEName)
{
// send by hardcoded index
checkSlow(InName.GetNumber() <= 0); // hardcoded names should never have a Number
uint32 NameIndex = *InEName;
Ar.SerializeIntPacked(NameIndex);
}
else
{
// send by string
FString OutString = InName.GetPlainNameString();
int32 OutNumber = InName.GetNumber();
Ar << OutString << OutNumber;
}
}
return true;
}
IMPLEMENT_CORE_INTRINSIC_CLASS(UPackageMap, UObject,
{});
void UPackageMap::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
if (Ar.IsCountingMemory())
{
TrackedUnmappedNetGuids.CountBytes(Ar);
TrackedMappedDynamicNetGuids.CountBytes(Ar);
Ar << DebugContextString;
}
}
// ----------------------------------------------------------------
void SerializeChecksum(FArchive& Ar, uint32 x, bool ErrorOK)
{
if (Ar.IsLoading())
{
uint32 Magic = 0;
Ar << Magic;
if ((!ErrorOK || !Ar.IsError()) && !ensure(Magic == x))
{
UE_LOG(LogCoreNet, Warning, TEXT("%d == %d"), Magic, x);
}
}
else
{
uint32 Magic = x;
Ar << Magic;
}
}
void FPropertyRetirement::CountBytes(FArchive& Ar) const
{
for (const FPropertyRetirement* NextRetirement = Next; NextRetirement; NextRetirement = NextRetirement->Next)
{
Ar.CountBytes(sizeof(FPropertyRetirement), sizeof(FPropertyRetirement));
}
}
// ----------------------------------------------------------------
// FNetBitWriter
// ----------------------------------------------------------------
FNetBitWriter::FNetBitWriter()
: FBitWriter(0), PackageMap(nullptr)
{}
FNetBitWriter::FNetBitWriter(int64 InMaxBits)
: FBitWriter(InMaxBits, true), PackageMap(nullptr)
{
}
FNetBitWriter::FNetBitWriter(UPackageMap* InPackageMap, int64 InMaxBits)
: FBitWriter(InMaxBits, true), PackageMap(InPackageMap)
{
}
FArchive& FNetBitWriter::operator<<(class FName& N)
{
if (PackageMap)
{
PackageMap->SerializeName(*this, N);
}
else
{
UPackageMap::StaticSerializeName(*this, N);
}
return *this;
}
FArchive& FNetBitWriter::operator<<(UObject*& Object)
{
PackageMap->SerializeObject(*this, UObject::StaticClass(), Object);
return *this;
}
FArchive& FNetBitWriter::operator<<(FSoftObjectPath& Value)
{
// It's more efficient to serialize as a string then name+string
FString Path = Value.ToString();
*this << Path;
return *this;
}
FArchive& FNetBitWriter::operator<<(FSoftObjectPtr& Value)
{
return FArchiveUObject::SerializeSoftObjectPtr(*this, Value);
}
FArchive& FNetBitWriter::operator<<(struct FWeakObjectPtr& WeakObjectPtr)
{
return FArchiveUObject::SerializeWeakObjectPtr(*this, WeakObjectPtr);
}
void FNetBitWriter::CountMemory(FArchive& Ar) const
{
FBitWriter::CountMemory(Ar);
const SIZE_T MemberSize = sizeof(*this) - sizeof(FBitWriter);
Ar.CountBytes(MemberSize, MemberSize);
}
// ----------------------------------------------------------------
// FNetBitReader
// ----------------------------------------------------------------
FNetBitReader::FNetBitReader(UPackageMap* InPackageMap, uint8* Src, int64 CountBits)
: FBitReader(Src, CountBits), PackageMap(InPackageMap)
{
}
FArchive& FNetBitReader::operator<<(UObject*& Object)
{
PackageMap->SerializeObject(*this, UObject::StaticClass(), Object);
return *this;
}
FArchive& FNetBitReader::operator<<(class FName& N)
{
if (PackageMap)
{
PackageMap->SerializeName(*this, N);
}
else
{
UPackageMap::StaticSerializeName(*this, N);
}
return *this;
}
FArchive& FNetBitReader::operator<<(FSoftObjectPath& Value)
{
FString Path;
*this << Path;
Value.SetPath(MoveTemp(Path));
return *this;
}
FArchive& FNetBitReader::operator<<(FSoftObjectPtr& Value)
{
return FArchiveUObject::SerializeSoftObjectPtr(*this, Value);
}
FArchive& FNetBitReader::operator<<(struct FWeakObjectPtr& WeakObjectPtr)
{
return FArchiveUObject::SerializeWeakObjectPtr(*this, WeakObjectPtr);
return *this;
}
void FNetBitReader::CountMemory(FArchive& Ar) const
{
FBitReader::CountMemory(Ar);
const SIZE_T MemberSize = sizeof(*this) - sizeof(FBitReader);
Ar.CountBytes(MemberSize, MemberSize);
}
static const TCHAR* GLastRPCFailedReason = NULL;
void RPC_ResetLastFailedReason()
{
GLastRPCFailedReason = NULL;
}
void RPC_ValidateFailed(const TCHAR* Reason)
{
GLastRPCFailedReason = Reason;
}
const TCHAR* RPC_GetLastFailedReason()
{
return GLastRPCFailedReason;
}
const TCHAR* LexToString(const EChannelCloseReason Value)
{
switch (Value)
{
case EChannelCloseReason::Destroyed:
return TEXT("Destroyed");
case EChannelCloseReason::Dormancy:
return TEXT("Dormancy");
case EChannelCloseReason::LevelUnloaded:
return TEXT("LevelUnloaded");
case EChannelCloseReason::Relevancy:
return TEXT("Relevancy");
case EChannelCloseReason::TearOff:
return TEXT("TearOff");
}
return TEXT("Unknown");
}
void INetSerializeCB::NetSerializeStruct(
class UScriptStruct* Struct,
class FBitArchive& Ar,
class UPackageMap* Map,
void* Data,
bool& bHasUnmapped)
{
FNetDeltaSerializeInfo Params;
Params.Struct = Struct;
Params.Map = Map;
Params.Data = Data;
if (Ar.IsSaving())
{
Params.Writer = static_cast<FBitWriter*>(&Ar);
}
else
{
Params.Reader = static_cast<FBitReader*>(&Ar);
}
NetSerializeStruct(Params);
bHasUnmapped = Params.bOutHasMoreUnmapped;
}