// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" #include "UObject/Object.h" #include "UObject/Class.h" #include "UObject/WeakObjectPtr.h" #include "Serialization/BitReader.h" #include "Serialization/BitWriter.h" #include "Misc/NetworkGuid.h" #include "UObject/CoreNetTypes.h" #include "UObject/SoftObjectPath.h" #include "UObject/Field.h" #include "Trace/Config.h" class FOutBunch; class INetDeltaBaseState; class FNetTraceCollector; DECLARE_DELEGATE_RetVal_OneParam(bool, FNetObjectIsDynamic, const UObject*); // // Information about a field. // class COREUOBJECT_API FFieldNetCache { public: FFieldVariant Field; int32 FieldNetIndex; uint32 FieldChecksum; mutable bool bIncompatible; FFieldNetCache() {} FFieldNetCache(FFieldVariant InField, int32 InFieldNetIndex, uint32 InFieldChecksum) : Field(InField), FieldNetIndex(InFieldNetIndex), FieldChecksum(InFieldChecksum), bIncompatible(false) {} }; // // Information about a class, cached for network coordination. // class COREUOBJECT_API FClassNetCache { friend class FClassNetCacheMgr; public: FClassNetCache(); FClassNetCache(const UClass* Class); int32 GetMaxIndex() const { return FieldsBase + Fields.Num(); } const FFieldNetCache* GetFromField(FFieldVariant Field) const { FFieldNetCache* Result = NULL; for (const FClassNetCache* C = this; C; C = C->Super) { if ((Result = C->FieldMap.FindRef(Field.GetRawPointer())) != NULL) { break; } } return Result; } const FFieldNetCache* GetFromChecksum(const uint32 Checksum) const { FFieldNetCache* Result = NULL; for (const FClassNetCache* C = this; C; C = C->Super) { if ((Result = C->FieldChecksumMap.FindRef(Checksum)) != NULL) { break; } } return Result; } const FFieldNetCache* GetFromIndex(const int32 Index) const { for (const FClassNetCache* C = this; C; C = C->Super) { if (Index >= C->FieldsBase && Index < C->FieldsBase + C->Fields.Num()) { return &C->Fields[Index - C->FieldsBase]; } } return NULL; } uint32 GetClassChecksum() const { return ClassChecksum; } const FClassNetCache* GetSuper() const { return Super; } const TArray& GetFields() const { return Fields; } void CountBytes(FArchive& Ar) const; private: int32 FieldsBase; const FClassNetCache* Super; TWeakObjectPtr Class; uint32 ClassChecksum; TArray Fields; TMap FieldMap; TMap FieldChecksumMap; }; class COREUOBJECT_API FClassNetCacheMgr { public: FClassNetCacheMgr(): bDebugChecksum(false), DebugChecksumIndent(0) {} ~FClassNetCacheMgr() { ClearClassNetCache(); } /** get the cached field to index mappings for the given class */ const FClassNetCache* GetClassNetCache(UClass* Class); void ClearClassNetCache(); void SortProperties(TArray& Properties) const; uint32 SortedStructFieldsChecksum(const UStruct* Struct, uint32 Checksum) const; uint32 GetPropertyChecksum(const FProperty* Property, uint32 Checksum, const bool bIncludeChildren) const; uint32 GetFunctionChecksum(const UFunction* Function, uint32 Checksum) const; uint32 GetFieldChecksum(const UField* Field, uint32 Checksum) const; bool bDebugChecksum; int DebugChecksumIndent; void CountBytes(FArchive& Ar) const; private: TMap, FClassNetCache*> ClassFieldIndices; }; // // Maps objects and names to and from indices for network communication. // class COREUOBJECT_API UPackageMap: public UObject { DECLARE_CLASS_INTRINSIC(UPackageMap, UObject, CLASS_Transient | CLASS_Abstract | 0, TEXT("/Script/CoreUObject")); virtual bool WriteObject(FArchive& Ar, UObject* InOuter, FNetworkGUID NetGUID, FString ObjName) { return false; } // @todo document virtual bool SerializeObject(FArchive& Ar, UClass* InClass, UObject*& Obj, FNetworkGUID* OutNetGUID = NULL) { return false; } // @todo document virtual bool SerializeName(FArchive& Ar, FName& InName); static bool StaticSerializeName(FArchive& Ar, FName& InName); virtual UObject* ResolvePathAndAssignNetGUID(const FNetworkGUID& NetGUID, const FString& PathName) { return NULL; } virtual bool SerializeNewActor(FArchive& Ar, class UActorChannel* Channel, class AActor*& Actor) { return false; } virtual void ReceivedNak(const int32 NakPacketId) {} virtual void ReceivedAck(const int32 AckPacketId) {} virtual void NotifyBunchCommit(const int32 OutPacketId, const FOutBunch* OutBunch) {} virtual void GetNetGUIDStats(int32& AckCount, int32& UnAckCount, int32& PendingCount) {} virtual void NotifyStreamingLevelUnload(UObject* UnloadedLevel) {} virtual bool PrintExportBatch() { return false; } void SetDebugContextString(const FString& Str) { DebugContextString = Str; } void ClearDebugContextString() { DebugContextString.Empty(); } void ResetTrackedGuids(bool bShouldTrack) { TrackedUnmappedNetGuids.Empty(); TrackedMappedDynamicNetGuids.Empty(); bShouldTrackUnmappedGuids = bShouldTrack; } const TSet& GetTrackedUnmappedGuids() const { return TrackedUnmappedNetGuids; } const TSet& GetTrackedDynamicMappedGuids() const { return TrackedMappedDynamicNetGuids; } // For sync load debugging with LogNetSyncLoads virtual void ResetTrackedSyncLoadedGuids() {} virtual void ReportSyncLoadsForProperty(const FProperty* Property, const UObject* Object) {} virtual void LogDebugInfo(FOutputDevice& Ar) {} virtual UObject* GetObjectFromNetGUID(const FNetworkGUID& NetGUID, const bool bIgnoreMustBeMapped) { return NULL; } virtual FNetworkGUID GetNetGUIDFromObject(const UObject* InObject) const { return FNetworkGUID(); } virtual bool IsGUIDBroken(const FNetworkGUID& NetGUID, const bool bMustBeRegistered) const { return false; } virtual void Serialize(FArchive& Ar) override; protected: UE_DEPRECATED(4.25, "bSuppressLogs will be removed in a future release.") bool bSuppressLogs; bool bShouldTrackUnmappedGuids; TSet TrackedUnmappedNetGuids; TSet TrackedMappedDynamicNetGuids; FString DebugContextString; }; /** Represents a range of PacketIDs, inclusive */ struct FPacketIdRange { FPacketIdRange(int32 _First, int32 _Last): First(_First), Last(_Last) {} FPacketIdRange(int32 PacketId): First(PacketId), Last(PacketId) {} FPacketIdRange(): First(INDEX_NONE), Last(INDEX_NONE) {} int32 First; int32 Last; bool InRange(int32 PacketId) const { return (First <= PacketId && PacketId <= Last); } }; /** Information for tracking retirement and retransmission of a property. */ struct FPropertyRetirement { #if !UE_BUILD_SHIPPING static const uint32 ExpectedSanityTag = 0xDF41C9A3; uint32 SanityTag; #endif FPropertyRetirement* Next; TSharedPtr DynamicState; FPacketIdRange OutPacketIdRange; uint32 FastArrayChangelistHistory; FPropertyRetirement(): #if !UE_BUILD_SHIPPING SanityTag(ExpectedSanityTag), #endif Next(nullptr), DynamicState(nullptr), FastArrayChangelistHistory(0) {} void CountBytes(FArchive& Ar) const; }; /** FLifetimeProperty * This class is used to track a property that is marked to be replicated for the lifetime of the actor channel. * This doesn't mean the property will necessarily always be replicated, it just means: * "check this property for replication for the life of the actor, and I don't want to think about it anymore" * A secondary condition can also be used to skip replication based on the condition results */ class FLifetimeProperty { public: uint16 RepIndex; ELifetimeCondition Condition; ELifetimeRepNotifyCondition RepNotifyCondition; bool bIsPushBased; FLifetimeProperty() : RepIndex(0), Condition(COND_None), RepNotifyCondition(REPNOTIFY_OnChanged), bIsPushBased(false) { } FLifetimeProperty(int32 InRepIndex) : RepIndex(InRepIndex), Condition(COND_None), RepNotifyCondition(REPNOTIFY_OnChanged), bIsPushBased(false) { check(InRepIndex <= 65535); } FLifetimeProperty(int32 InRepIndex, ELifetimeCondition InCondition, ELifetimeRepNotifyCondition InRepNotifyCondition = REPNOTIFY_OnChanged, bool bInIsPushBased = false) : RepIndex(InRepIndex), Condition(InCondition), RepNotifyCondition(InRepNotifyCondition), bIsPushBased(bInIsPushBased) { check(InRepIndex <= 65535); } inline bool operator==(const FLifetimeProperty& Other) const { if (RepIndex == Other.RepIndex) { // Can't have different conditions if the RepIndex matches, doesn't make sense check(Condition == Other.Condition); check(RepNotifyCondition == Other.RepNotifyCondition); check(bIsPushBased == Other.bIsPushBased); return true; } return false; } }; template <> struct TIsZeroConstructType { enum { Value = true }; }; GENERATE_MEMBER_FUNCTION_CHECK(GetLifetimeReplicatedProps, void, const, TArray&) // Consider adding UE_NET_TRACE_ENABLE to build config, for now we use the UE_TRACE_ENABLED as NetTrace is not support unless tracing is enabled #if UE_TRACE_ENABLED /** * We pass a NetTraceCollector along with the NetBitWriter in order avoid modifying all API`s where we want to be able to collect Network stats * Since the pointer to the collector is temporary we need to avoid copying it around by accident */ class FNetTraceCollectorDoNotCopyWrapper { public: FNetTraceCollectorDoNotCopyWrapper(): Collector(nullptr) {} FNetTraceCollectorDoNotCopyWrapper(const FNetTraceCollectorDoNotCopyWrapper&): Collector(nullptr) {} FNetTraceCollectorDoNotCopyWrapper(FNetTraceCollectorDoNotCopyWrapper&&) { Collector = nullptr; } FNetTraceCollectorDoNotCopyWrapper& operator=(const FNetTraceCollectorDoNotCopyWrapper& Other) { Collector = nullptr; return *this; } FNetTraceCollectorDoNotCopyWrapper& operator=(FNetTraceCollectorDoNotCopyWrapper&&) { Collector = nullptr; return *this; } void Set(FNetTraceCollector* InCollector) { Collector = InCollector; } FNetTraceCollector* Get() const { return Collector; } private: FNetTraceCollector* Collector; }; #endif /** * FNetBitWriter * A bit writer that serializes FNames and UObject* through * a network packagemap. */ class COREUOBJECT_API FNetBitWriter: public FBitWriter { public: FNetBitWriter(UPackageMap* InPackageMap, int64 InMaxBits); FNetBitWriter(int64 InMaxBits); FNetBitWriter(); class UPackageMap* PackageMap; #if UE_TRACE_ENABLED FNetTraceCollectorDoNotCopyWrapper TraceCollector; #endif virtual FArchive& operator<<(FName& Name) override; virtual FArchive& operator<<(UObject*& Object) override; virtual FArchive& operator<<(FSoftObjectPath& Value) override; virtual FArchive& operator<<(FSoftObjectPtr& Value) override; virtual FArchive& operator<<(struct FWeakObjectPtr& Value) override; virtual void CountMemory(FArchive& Ar) const override; }; /** * FNetBitReader * A bit reader that serializes FNames and UObject* through * a network packagemap. */ class COREUOBJECT_API FNetBitReader: public FBitReader { public: FNetBitReader(UPackageMap* InPackageMap = NULL, uint8* Src = NULL, int64 CountBits = 0); class UPackageMap* PackageMap; virtual FArchive& operator<<(FName& Name) override; virtual FArchive& operator<<(UObject*& Object) override; virtual FArchive& operator<<(FSoftObjectPath& Value) override; virtual FArchive& operator<<(FSoftObjectPtr& Value) override; virtual FArchive& operator<<(struct FWeakObjectPtr& Value) override; virtual void CountMemory(FArchive& Ar) const override; }; bool FORCEINLINE NetworkGuidSetsAreSame(const TSet& A, const TSet& B) { if (A.Num() != B.Num()) { return false; } for (const FNetworkGUID& CompareGuid: A) { if (!B.Contains(CompareGuid)) { return false; } } return true; } /** * INetDeltaBaseState * An abstract interface for the base state used in net delta serialization. See notes in NetSerialization.h */ class INetDeltaBaseState: public TSharedFromThis { public: INetDeltaBaseState() : LastAckedHistory(0), ChangelistHistory(0) { } virtual ~INetDeltaBaseState() {} virtual bool IsStateEqual(INetDeltaBaseState* Otherstate) = 0; /** * Used when tracking memory to gather the total size of a given instance. * This should include the dynamically allocated data, as well as the classes size. */ virtual void CountBytes(FArchive& Ar) const {} uint32 GetLastAckedHistory() const { return LastAckedHistory; } void SetLastAckedHistory(uint32 InAckedHistory) { LastAckedHistory = InAckedHistory; } uint32 GetChangelistHistory() const { return ChangelistHistory; } void SetChangelistHistory(uint32 InChangelistHistory) { ChangelistHistory = InChangelistHistory; } private: uint32 LastAckedHistory; uint32 ChangelistHistory; }; struct FNetDeltaSerializeInfo; /** * An interface for handling serialization of Structs for networking. * * See notes in NetSerialization.h */ class COREUOBJECT_API INetSerializeCB { protected: using FGuidReferencesMap = TMap; public: INetSerializeCB() {} virtual ~INetSerializeCB() {} /** * Serializes an entire struct to / from the given archive. * It is up to callers to manage Guid References created during reads. * * @param Params NetDeltaSerialization Params to use. * Object must be valid. * Data must be valid. * Connection must be valid. * Map must be valid. * Struct must point to the UScriptStruct of Data. * Either Reader or Writer (but not both) must be valid. * bOutHasMoreUnmapped will be used to return whether or not we have we have unmapped guids. * Only used when reading. */ virtual void NetSerializeStruct(FNetDeltaSerializeInfo& Params) = 0; UE_DEPRECATED(4.23, "Please use the version of NetSerializeStruct that accepts an FNetDeltaSerializeInfo reference") virtual void NetSerializeStruct( class UScriptStruct* Struct, class FBitArchive& Ar, class UPackageMap* Map, void* Data, bool& bHasUnmapped); /** * Gathers any guid references for a FastArraySerializer. * @see GuidReferences.h for more info. */ virtual void GatherGuidReferencesForFastArray(struct FFastArrayDeltaSerializeParams& Params) = 0; /** * Moves a previously mapped guid to an unmapped state for a FastArraySerializer. * @see GuidReferences.h for more info. * * @return True if the guid was found and unmapped. */ virtual bool MoveGuidToUnmappedForFastArray(struct FFastArrayDeltaSerializeParams& Params) = 0; /** * Updates any unmapped guid references for a FastArraySerializer. * @see GuidReferences.h for more info. */ virtual void UpdateUnmappedGuidsForFastArray(struct FFastArrayDeltaSerializeParams& Params) = 0; /** * Similar to NetSerializeStruct, except serializes an entire FastArraySerializer at once * instead of element by element. */ virtual bool NetDeltaSerializeForFastArray(struct FFastArrayDeltaSerializeParams& Params) = 0; }; class IRepChangedPropertyTracker { public: IRepChangedPropertyTracker() {} virtual ~IRepChangedPropertyTracker() {} virtual void SetCustomIsActiveOverride( UObject* OwningObject, const uint16 RepIndex, const bool bIsActive) = 0; virtual void SetExternalData(const uint8* Src, const int32 NumBits) = 0; UE_DEPRECATED(4.26, "Will be removed in a future release.") virtual bool IsReplay() const = 0; /** * Used when tracking memory to gather the total size of a given instance. * This should include the dynamically allocated data, as well as the classes size. */ virtual void CountBytes(FArchive& Ar) const {}; }; /** * FNetDeltaSerializeInfo * This is the parameter structure for delta serialization. It is kind of a dumping ground for anything custom implementations may need. */ struct FNetDeltaSerializeInfo { /** Used when writing */ FBitWriter* Writer = nullptr; /** Used when reading */ FBitReader* Reader = nullptr; /** SharedPtr to new base state created by NetDeltaSerialize. Used when writing.*/ TSharedPtr* NewState = nullptr; /** Pointer to the previous base state. Used when writing. */ INetDeltaBaseState* OldState = nullptr; /** PackageMap that can be used to serialize objects and track Guid References. Used primarily when reading. */ class UPackageMap* Map = nullptr; /** Connection that we're currently serializing data for. */ class UNetConnection* Connection = nullptr; /** Pointer to the struct that we're serializing.*/ void* Data = nullptr; /** Type of struct that we're serializing. */ class UStruct* Struct = nullptr; /** Pointer to a NetSerializeCB implementation that can be used when serializing. */ INetSerializeCB* NetSerializeCB = nullptr; /** If true, we are updating unmapped objects */ bool bUpdateUnmappedObjects = false; /** If true, then we successfully mapped some unmapped objects. */ bool bOutSomeObjectsWereMapped = false; /** Whether or not PreNetReceive has been called on the owning object. */ bool bCalledPreNetReceive = false; /** Whether or not there are still some outstanding unmapped objects referenced by the struct. */ bool bOutHasMoreUnmapped = false; /** Whether or not we changed Guid / Object references. Used when reading. */ bool bGuidListsChanged = false; /** Whether or not we're sending / writing data from the client. */ bool bIsWritingOnClient = false; //~ TODO: This feels hacky, and a better alternative might be something like connection specific //~ capabilities. /** Whether or not we support FFastArraySerializer::FastArrayDeltaSerialize_DeltaSerializeStructs */ bool bSupportsFastArrayDeltaStructSerialization = false; /** * Whether or the connection is completely reliable. * We cache this off separate from UNetConnection so we can limit usage. */ bool bInternalAck = false; /** The object that owns the struct we're serializing. */ UObject* Object = nullptr; /** * When non-null, this indicates that we're gathering Guid References. * Any Guids the struct is referencing should be added. * This may contain gathered Guids from other structs, so do not clear this set. */ TSet* GatherGuidReferences = nullptr; /** * When we're gathering guid references, ny memory used to track Guids can be added to this. * This may be tracking Guid memory from other structs, so do not reset this. * Note, this is not guaranteed to be valid when GatherGuidReferences is. */ int32* TrackedGuidMemoryBytes = nullptr; /** When non-null, this indicates the given Guid has become unmapped and any references to it should be updated. */ const FNetworkGUID* MoveGuidToUnmapped = nullptr; uint16 CustomDeltaIndex = INDEX_NONE; // Debugging variables FString DebugName; }; struct FEncryptionData { /** Encryption key */ TArray Key; /** Encryption fingerprint */ TArray Fingerprint; /** Encryption identifier */ FString Identifier; }; /** * Checksum macros for verifying archives stay in sync */ COREUOBJECT_API void SerializeChecksum(FArchive& Ar, uint32 x, bool ErrorOK); #define NET_ENABLE_CHECKSUMS 0 #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && NET_ENABLE_CHECKSUMS #define NET_CHECKSUM_OR_END(Ser) \ { \ SerializeChecksum(Ser,0xE282FA84, true); \ } #define NET_CHECKSUM(Ser) \ { \ SerializeChecksum(Ser,0xE282FA84, false); \ } #define NET_CHECKSUM_CUSTOM(Ser, x) \ { \ SerializeChecksum(Ser,x, false); \ } // There are cases where a checksum failure is expected, but we still need to eat the next word (just dont without erroring) #define NET_CHECKSUM_IGNORE(Ser) \ { \ uint32 Magic = 0; \ Ser << Magic; \ } #else // No ops in shipping builds #define NET_CHECKSUM(Ser) #define NET_CHECKSUM_IGNORE(Ser) #define NET_CHECKSUM_CUSTOM(Ser, x) #define NET_CHECKSUM_OR_END(ser) #endif /** * Values used for initializing UNetConnection and LanBeacon */ enum { MAX_PACKET_SIZE = 1024 }; // MTU for the connection enum { LAN_BEACON_MAX_PACKET_SIZE = 1024 }; // MTU for the connection /** * Functions to assist in detecting errors during RPC calls */ COREUOBJECT_API void RPC_ResetLastFailedReason(); COREUOBJECT_API void RPC_ValidateFailed(const TCHAR* Reason); COREUOBJECT_API const TCHAR* RPC_GetLastFailedReason();