bool FObjectReplicator::SerializeCustomDeltaProperty( UNetConnection * Connection, void * Src, UProperty * Property, int32 ArrayDim, FNetBitWriter & OutBunch, TSharedPtr<INetDeltaBaseState> &NewFullState, TSharedPtr<INetDeltaBaseState> & OldState )
{
	check( NewFullState.IsValid() == false ); // NewState is passed in as NULL and instantiated within this function if necessary

	SCOPE_CYCLE_COUNTER( STAT_NetSerializeItemDeltaTime );

	UStructProperty * StructProperty = CastChecked< UStructProperty >( Property );

	//------------------------------------------------
	//	Custom NetDeltaSerialization
	//------------------------------------------------
	if ( !ensure( ( StructProperty->Struct->StructFlags & STRUCT_NetDeltaSerializeNative ) != 0 ) )
	{
		return false;
	}

	FNetDeltaSerializeInfo Parms;

	FNetSerializeCB NetSerializeCB( Connection->Driver );

	Parms.OutBunch			= &OutBunch;
	Parms.Map				= Connection->PackageMap;
	Parms.OldState			= OldState.Get();
	Parms.NewState			= &NewFullState;
	Parms.NetSerializeCB	= &NetSerializeCB;

	UScriptStruct::ICppStructOps * CppStructOps = StructProperty->Struct->GetCppStructOps();

	check(CppStructOps); // else should not have STRUCT_NetSerializeNative
	check(!StructProperty->Struct->InheritedCppStructOps()); // else should not have STRUCT_NetSerializeNative

	Parms.Struct = StructProperty->Struct;

	return CppStructOps->NetDeltaSerialize( Parms, Property->ContainerPtrToValuePtr<void>( Src, ArrayDim ) );
}
bool UStructProperty::ConvertFromType(const FPropertyTag& Tag, FArchive& Ar, uint8* Data, UStruct* DefaultsStruct, bool& bOutAdvanceProperty)
{
	bOutAdvanceProperty = false;

	auto CanSerializeFromStructWithDifferentName = [](const FArchive& InAr, const FPropertyTag& PropertyTag, const UStructProperty* StructProperty)
	{
		if (InAr.UE4Ver() < VER_UE4_STRUCT_GUID_IN_PROPERTY_TAG)
		{
			// Old Implementation
			return StructProperty && !StructProperty->UseBinaryOrNativeSerialization(InAr);
		}
		return PropertyTag.StructGuid.IsValid() && StructProperty && StructProperty->Struct && (PropertyTag.StructGuid == StructProperty->Struct->GetCustomGuid());
	};

	if (Struct)
	{
		if ((Struct->StructFlags & STRUCT_SerializeFromMismatchedTag) && (Tag.Type != NAME_StructProperty || (Tag.StructName != Struct->GetFName())) && Struct->StructFlags & STRUCT_SerializeFromMismatchedTag)
		{
			UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
			check(CppStructOps && CppStructOps->HasSerializeFromMismatchedTag()); // else should not have STRUCT_SerializeFromMismatchedTag
			void* DestAddress = ContainerPtrToValuePtr<void>(Data, Tag.ArrayIndex);
			if (CppStructOps->SerializeFromMismatchedTag(Tag, Ar, DestAddress))
			{
				bOutAdvanceProperty = true;
			}			
			else
			{
				UE_LOG(LogClass, Warning, TEXT("SerializeFromMismatchedTag failed: Type mismatch in %s of %s - Previous (%s) Current(StructProperty) for package:  %s"), *Tag.Name.ToString(), *GetName(), *Tag.Type.ToString(), *Ar.GetArchiveName());
			}
			return true;
		}
		else if (Tag.Type == NAME_StructProperty && Tag.StructName != Struct->GetFName() && !CanSerializeFromStructWithDifferentName(Ar, Tag, this))
		{
			//handle Vector -> Vector4 upgrades here because using the SerializeFromMismatchedTag system would cause a dependency from Core -> CoreUObject
			if (Tag.StructName == NAME_Vector && Struct->GetFName() == NAME_Vector4)
			{
				void* DestAddress = ContainerPtrToValuePtr<void>(Data, Tag.ArrayIndex);
				FVector OldValue;
				Ar << OldValue;

				//only set X/Y/Z.  The W should already have been set to the property specific default and we don't want to trash it by forcing 0 or 1.
				FVector4* DestValue = (FVector4*)DestAddress;				
				DestValue->X = OldValue.X;
				DestValue->Y = OldValue.Y;
				DestValue->Z = OldValue.Z;
			}
			else
			{
				UE_LOG(LogClass, Warning, TEXT("Property %s of %s has a struct type mismatch (tag %s != prop %s) in package:  %s. If that struct got renamed, add an entry to ActiveStructRedirects."),
					*Tag.Name.ToString(), *GetName(), *Tag.StructName.ToString(), *Struct->GetName(), *Ar.GetArchiveName());
			}

			return true;
		}
	}
	return false;
}
bool UStructProperty::HasNoOpConstructor() const
{
	Struct->PrepareCppStructOps();
	UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
	if (CppStructOps && CppStructOps->HasNoopConstructor())
	{
		return true;
	}
	return false;
}
Example #4
0
void UStructProperty::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const
{
	if (Struct->StructFlags & STRUCT_ExportTextItemNative)
	{
		UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
		check(CppStructOps); // else should not have STRUCT_ExportTextItemNative
		check(!Struct->InheritedCppStructOps()); // else should not have STRUCT_ExportTextItemNative
		if (CppStructOps->ExportTextItem(ValueStr, PropertyValue, DefaultValue, Parent, PortFlags, ExportRootScope))
		{
			return;
		}
	}

	UStructProperty_ExportTextItem(Struct, ValueStr, PropertyValue, DefaultValue, Parent, PortFlags, ExportRootScope);
} 
Example #5
0
void UStructProperty::SerializeItem( FArchive& Ar, void* Value, int32 MaxReadBytes, void const* Defaults ) const
{
	const bool bUseBinarySerialization = UseBinarySerialization(Ar);
	const bool bUseNativeSerialization = UseNativeSerialization();

	// Preload struct before serialization tracking to not double count time.
	if ( bUseBinarySerialization || bUseNativeSerialization)
	{
		Ar.Preload( Struct );
	}

	bool bItemSerialized = false;
	if (bUseNativeSerialization)
	{
		UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
		check(CppStructOps); // else should not have STRUCT_SerializeNative
		check(!Struct->InheritedCppStructOps()); // else should not have STRUCT_SerializeNative
		bItemSerialized = CppStructOps->Serialize(Ar, Value);
	}

	if (!bItemSerialized)
	{
		if( bUseBinarySerialization )
		{
			// Struct is already preloaded above.
			if ( !Ar.IsPersistent() && Ar.GetPortFlags() != 0 && !Struct->ShouldSerializeAtomically(Ar) )
			{
				Struct->SerializeBinEx( Ar, Value, Defaults, Struct );
			}
			else
			{
				Struct->SerializeBin( Ar, Value, MaxReadBytes );
			}
		}
		else
		{
			Struct->SerializeTaggedProperties( Ar, (uint8*)Value, Struct, (uint8*)Defaults );
		}
	}

	if (Struct->StructFlags & STRUCT_PostSerializeNative)
	{
		UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
		check(CppStructOps); // else should not have STRUCT_PostSerializeNative
		check(!Struct->InheritedCppStructOps()); // else should not have STRUCT_PostSerializeNative
		CppStructOps->PostSerialize(Ar, Value);
	}
}
bool UStructProperty::NetSerializeItem( FArchive& Ar, UPackageMap* Map, void* Data, TArray<uint8> * MetaData ) const
{
	//------------------------------------------------
	//	Custom NetSerialization
	//------------------------------------------------
	if (Struct->StructFlags & STRUCT_NetSerializeNative)
	{
		UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
		check(CppStructOps); // else should not have STRUCT_NetSerializeNative
		bool bSuccess = true;
		bool bMapped = CppStructOps->NetSerialize(Ar, Map, bSuccess, Data);
		if (!bSuccess)
		{
			UE_LOG(LogProperty, Warning, TEXT("Native NetSerialize %s (%s) failed."), *GetFullName(), *Struct->GetFullName() );
		}
		return bMapped;
	}

	UE_LOG( LogProperty, Fatal, TEXT( "Deprecated code path" ) );

	return 1;
}
	virtual void NetSerializeStruct( UScriptStruct* Struct, FArchive& Ar, UPackageMap* Map, void* Data, bool& bHasUnmapped )
	{
		if (Struct->StructFlags & STRUCT_NetSerializeNative)
		{
			UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
			check(CppStructOps); // else should not have STRUCT_NetSerializeNative
			check(!Struct->InheritedCppStructOps()); // else should not have STRUCT_NetSerializeNative
			bool bSuccess = true;
			if (!CppStructOps->NetSerialize(Ar, Map, bSuccess, Data))
			{
				bHasUnmapped = true;
			}

			if (!bSuccess)
			{
				UE_LOG(LogRep, Warning, TEXT("NetSerializeStruct: Native NetSerialize %s failed."), *Struct->GetFullName());
			}
		}
		else
		{
			TSharedPtr<FRepLayout> RepLayout = Driver->GetStructRepLayout(Struct);
			RepLayout->SerializePropertiesForStruct(Struct, Ar, Map, Data, bHasUnmapped);
		}
	}
bool FObjectReplicator::ReceivedBunch( FInBunch &Bunch, const FReplicationFlags & RepFlags )
{
	UObject *		Object		= GetObject();
	UPackageMap *	PackageMap	= OwningChannel->Connection->PackageMap;

	if ( Object == NULL )
	{
		UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Object == NULL" ) );
		return false;
	}

	const bool bIsServer = ( OwningChannel->Connection->Driver->ServerConnection == NULL );

	FClassNetCache * ClassCache = PackageMap->GetClassNetCache( ObjectClass );

	if ( ClassCache == NULL )
	{
		UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: ClassCache == NULL: %s" ), *Object->GetFullName() );
		return false;
	}

	bool bThisBunchReplicatedProperties = false;

	// First RepIndex.
	int32 RepIndex = Bunch.ReadInt( ClassCache->GetMaxIndex() + 1 );

	if ( Bunch.IsError() )
	{
		UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Error reading bunch 1: %s" ), *Object->GetFullName() );
		return false;
	}

	if ( RepIndex == ClassCache->GetMaxIndex() )
	{
		// There are no actual replicated properties or functions in this bunch. That is ok - we may have gotten this
		// actor/subobject because we want the client to spawn one (but we arent actually replicating properties on it)
		return true;
	}

	if ( RepIndex > ClassCache->GetMaxIndex() )
	{
		// We shouldn't be receiving this bunch of this object has no properties or RPC functions to process
		UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RepIndex too large: %s" ), *Object->GetFullName() );
		return false;
	}

	FFieldNetCache * FieldCache = ClassCache->GetFromIndex( RepIndex );

	if ( FieldCache == NULL )
	{
		UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: FieldCache == NULL: %s" ), *Object->GetFullName() );
		return false;
	}

	while ( FieldCache )
	{
		// Receive properties from the net.
		UProperty * ReplicatedProp	= NULL;
		int32		LastIndex		= 0;

		while ( FieldCache && ( ReplicatedProp = Cast< UProperty >( FieldCache->Field ) ) != NULL )
		{
			NET_CHECKSUM( Bunch );

			// Server shouldn't receive properties.
			if ( bIsServer )
			{
				UE_LOG( LogNet, Error, TEXT( "Server received unwanted property value %s in %s" ), *ReplicatedProp->GetName(), *Object->GetFullName() );
				return false;
			}
		
			bThisBunchReplicatedProperties = true;

			if ( !bHasReplicatedProperties )
			{
				bHasReplicatedProperties = true;		// Persistent, not reset until PostNetReceive is called
				PreNetReceive();
			}

			bool DebugProperty = false;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
			{
				static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Replication.DebugProperty"));
				if (CVar && !CVar->GetString().IsEmpty() && ReplicatedProp->GetName().Contains(CVar->GetString()) )
				{
					UE_LOG(LogNet, Log, TEXT("Replicating Property[%d] %s on %s"), RepIndex, *ReplicatedProp->GetName(), *Object->GetName());
					DebugProperty = true;
				}
			}
#endif

			if ( !Retirement[ ReplicatedProp->RepIndex ].CustomDelta )
			{
				// We hijack a non custom delta property to signify we are using FRepLayout to read the entire property block
				FPropertyRetirement & Retire = Retirement[ ReplicatedProp->RepIndex ];

				bool bDiscardLayout = false;

				if ( Bunch.PacketId >= Retire.InPacketId ) //!! problem with reliable pkts containing dynamic references, being retransmitted, and overriding newer versions. Want "OriginalPacketId" for retransmissions?
				{
					// Receive this new property.
					Retire.InPacketId = Bunch.PacketId;
				}
				else
				{
					bDiscardLayout = true;
				}
				
				if ( !RepLayout->ReceiveProperties( ObjectClass, RepState, (void*)Object, Bunch, bDiscardLayout ) )
				{
					UE_LOG( LogNet, Error, TEXT( "ReceiveProperties FAILED %s in %s" ), *ReplicatedProp->GetName(), *Object->GetFullName() );
					return false;
				}
			}
			else
			{
				// Receive array index.
				int32 Element = 0;
				if ( ReplicatedProp->ArrayDim != 1 )
				{
					// Serialize index as delta from previous index to increase chance we'll only use 1 byte
					uint32 idx;
					Bunch.SerializeIntPacked( idx );
					Element = static_cast< int32 >( idx ) + LastIndex;
					LastIndex = Element;

					if ( Element >= ReplicatedProp->ArrayDim )
					{
						UE_LOG( LogNet, Error, TEXT( "Element index too large %s in %s" ), *ReplicatedProp->GetName(), *Object->GetFullName() );
						return false;
					}
				}

				// Pointer to destination.
				uint8 * DestObj		= (uint8*)Object;
				uint8 * DestRecent	= RepState->StaticBuffer.Num() ? RepState->StaticBuffer.GetTypedData() : NULL;

				// Check property ordering.
				FPropertyRetirement & Retire = Retirement[ ReplicatedProp->RepIndex + Element ];

				if ( Bunch.PacketId >= Retire.InPacketId ) //!! problem with reliable pkts containing dynamic references, being retransmitted, and overriding newer versions. Want "OriginalPacketId" for retransmissions?
				{
					// Receive this new property.
					Retire.InPacketId = Bunch.PacketId;
				}
				else
				{
					// Skip this property, because it's out-of-date.
					UE_LOG( LogNetTraffic, Log, TEXT( "Received out-of-date %s" ), *ReplicatedProp->GetName() );

					DestObj		= NULL;
					DestRecent	= NULL;
				}

				FMemMark Mark(FMemStack::Get());
				uint8 * Data = DestObj ? ReplicatedProp->ContainerPtrToValuePtr<uint8>(DestObj, Element) : NewZeroed<uint8>(FMemStack::Get(),ReplicatedProp->ElementSize);
				TArray<uint8>	MetaData;
				PTRINT Offset = 0;

				// Copy current value over to Recent for comparison
				if ( DestRecent )
				{
					Offset = ReplicatedProp->ContainerPtrToValuePtr<uint8>(DestRecent, Element) - DestRecent;
					check( Offset >= 0 && Offset < RepState->StaticBuffer.Num() ); //@todo if we move properties outside of the memory block, then this will not work anyway
					ReplicatedProp->CopySingleValue( DestRecent + Offset, Data );
				}

				// Receive custom delta property.
				UStructProperty * StructProperty = Cast< UStructProperty >( ReplicatedProp );

				if ( StructProperty == NULL )
				{
					// This property isn't custom delta
					UE_LOG( LogNetTraffic, Error, TEXT( "Property isn't custom delta %s" ), *ReplicatedProp->GetName() );
					return false;
				}

				UScriptStruct * InnerStruct = StructProperty->Struct;

				if ( !( InnerStruct->StructFlags & STRUCT_NetDeltaSerializeNative ) )
				{
					// This property isn't custom delta
					UE_LOG( LogNetTraffic, Error, TEXT( "Property isn't custom delta %s" ), *ReplicatedProp->GetName() );
					return false;
				}

				UScriptStruct::ICppStructOps * CppStructOps = InnerStruct->GetCppStructOps();

				check( CppStructOps );
				check( !InnerStruct->InheritedCppStructOps() );

				FNetDeltaSerializeInfo Parms;

				FNetSerializeCB NetSerializeCB( OwningChannel->Connection->Driver );

				Parms.DebugName			= StructProperty->GetName();
				Parms.Struct			= InnerStruct;
				Parms.Map				= PackageMap;
				Parms.InArchive			= &Bunch;
				Parms.NetSerializeCB	= &NetSerializeCB;

				// Call the custom delta serialize function to handle it
				CppStructOps->NetDeltaSerialize( Parms, Data );

				if ( Bunch.IsError() )
				{
					UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: NetDeltaSerialize - Bunch.IsError() == true: %s" ), *Object->GetFullName() );
					return false;
				}

				// See if it changed from our local value
				bool PropertyChanged = true;

				if ( DestRecent )
				{
					// POD types can do a memcmp with a call to Identical
					if ( ReplicatedProp->Identical( DestRecent + Offset, Data ) )
					{
						PropertyChanged = false;
					}
				}

				Mark.Pop();

				// Successfully received it.
				UE_LOG( LogNetTraffic, Log, TEXT( " %s - %s - Change: %d" ), *Object->GetName(), *ReplicatedProp->GetName(), PropertyChanged );

				// Notify the Object if this var is RepNotify
				if ( PropertyChanged )
				{
					QueuePropertyRepNotify( Object, ReplicatedProp, Element, MetaData );
				}
			}	
			
			// Next.
			RepIndex = Bunch.ReadInt( ClassCache->GetMaxIndex() + 1 );

			if ( Bunch.IsError() )
			{
				UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Error reading bunch 2: %s" ), *Object->GetFullName() );
				return false;
			}

			if ( RepIndex > ClassCache->GetMaxIndex() )
			{
				UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RepIndex too large: %s" ), *Object->GetFullName() );
				return false;
			}
			
			if ( RepIndex == ClassCache->GetMaxIndex() )
			{
				// We're done
				FieldCache = NULL;
			}
			else
			{
				FieldCache = ClassCache->GetFromIndex( RepIndex );

				if ( FieldCache == NULL )
				{
					UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: FieldCache == NULL: %s" ), *Object->GetFullName() );
					return false;
				}
			}
		}

		// Handle function calls.
		if ( FieldCache && Cast< UFunction >( FieldCache->Field ) )
		{
			FName Message = FieldCache->Field->GetFName();
			UFunction * Function = Object->FindFunction( Message );

			if ( Function == NULL )
			{
				UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Function == NULL: %s" ), *Object->GetFullName() );
				return false;
			}

			if ( ( Function->FunctionFlags & FUNC_Net ) == 0 )
			{
				UE_LOG( LogNet, Error, TEXT( "Rejected non RPC function %s in %s" ), *Message.ToString(), *Object->GetFullName() );
				return false;
			}

			if ( ( Function->FunctionFlags & ( bIsServer ? FUNC_NetServer : ( FUNC_NetClient | FUNC_NetMulticast ) ) ) == 0 )
			{
				UE_LOG( LogNet, Error, TEXT( "Rejected RPC function due to access rights %s in %s" ), *Message.ToString(), *Object->GetFullName() );
				return false;
			}

			UE_LOG( LogNetTraffic, Log, TEXT( "      Received RPC: %s" ), *Message.ToString() );

			// Get the parameters.
			FMemMark Mark(FMemStack::Get());
			uint8* Parms = new(FMemStack::Get(),MEM_Zeroed,Function->ParmsSize)uint8;

			// Use the replication layout to receive the rpc parameter values
			TSharedPtr<FRepLayout> FuncRepLayout = OwningChannel->Connection->Driver->GetFunctionRepLayout( Function );

			FuncRepLayout->ReceivePropertiesForRPC( Object, Function, OwningChannel, Bunch, Parms );

			if ( Bunch.IsError() )
			{
				UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: ReceivePropertiesForRPC - Bunch.IsError() == true: %s" ), *Object->GetFullName() );
				return false;
			}

			// validate that the function is callable here
			const bool bCanExecute = ( ( !bIsServer || RepFlags.bNetOwner ) );		// we are client or net owner

			if ( bCanExecute )
			{
				// Call the function.
				RPC_ResetLastFailedReason();

				Object->ProcessEvent( Function, Parms );

				if ( RPC_GetLastFailedReason() != NULL )
				{
					UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RPC_GetLastFailedReason: %s" ), RPC_GetLastFailedReason() );
					return false;
				}
			}
			else
			{
				UE_LOG( LogNet, Warning, TEXT( "Rejected unwanted function %s in %s" ), *Message.ToString(), *Object->GetFullName() );

				if ( !OwningChannel->Connection->TrackLogsPerSecond() )	// This will disconnect the client if we get her too often
				{
					return false;
				}
			}

			// Destroy the parameters.
			//warning: highly dependent on UObject::ProcessEvent freeing of parms!
			for ( UProperty * Destruct=Function->DestructorLink; Destruct; Destruct=Destruct->DestructorLinkNext )
			{
				if( Destruct->IsInContainer(Function->ParmsSize) )
				{
					Destruct->DestroyValue_InContainer(Parms);
				}
			}

			Mark.Pop();

			if ( Object == NULL || Object->IsPendingKill() )
			{
				// replicated function destroyed Object
				return true;		// FIXME: Should this return false to kick connection?  Seems we'll cause a read misalignment here if we don't
			}

			// Next.
			RepIndex = Bunch.ReadInt( ClassCache->GetMaxIndex() + 1 );

			if ( Bunch.IsError() )
			{
				UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Error reading bunch 2: %s" ), *Object->GetFullName() );
				return false;
			}

			if ( RepIndex > ClassCache->GetMaxIndex() )
			{
				UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RepIndex too large: %s" ), *Object->GetFullName() );
				return false;
			}

			if ( RepIndex == ClassCache->GetMaxIndex() )
			{
				// We're done
				FieldCache = NULL;
			}
			else
			{
				FieldCache = ClassCache->GetFromIndex( RepIndex );

				if ( FieldCache == NULL )
				{
					UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: FieldCache == NULL: %s" ), *Object->GetFullName() );
					return false;
				}
			}
		}
		else if ( FieldCache )
		{
			UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Invalid replicated field %i in %s" ), RepIndex, *Object->GetFullName() );
			return false;
		}
	}

	return true;
}
const TCHAR* UStructProperty::ImportText_Static(UScriptStruct* InStruct, const FString& Name, const TCHAR* InBuffer, void* Data, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText)
{
	auto Struct = InStruct;

	if (Struct->StructFlags & STRUCT_ImportTextItemNative)
	{
		UScriptStruct::ICppStructOps* CppStructOps = Struct->GetCppStructOps();
		check(CppStructOps); // else should not have STRUCT_ImportTextItemNative
		if (CppStructOps->ImportTextItem(InBuffer, Data, PortFlags, Parent, ErrorText))
		{
			return InBuffer;
		}
	}
	
	TArray<FDefinedProperty> DefinedProperties;
	// this keeps track of the number of errors we've logged, so that we can add new lines when logging more than one error
	int32 ErrorCount = 0;
	const TCHAR* Buffer = InBuffer;
	if (*Buffer++ == TCHAR('('))
	{
		// Parse all properties.
		while (*Buffer != TCHAR(')'))
		{
			// parse and import the value
			Buffer = ImportSingleProperty(Buffer, Data, Struct, Parent, PortFlags | PPF_Delimited, ErrorText, DefinedProperties);

			// skip any remaining text before the next property value
			SkipWhitespace(Buffer);
			int32 SubCount = 0;
			while ( *Buffer && *Buffer != TCHAR('\r') && *Buffer != TCHAR('\n') &&
					(SubCount > 0 || *Buffer != TCHAR(')')) && (SubCount > 0 || *Buffer != TCHAR(',')) )
			{
				SkipWhitespace(Buffer);
				if (*Buffer == TCHAR('\"'))
				{
					do
					{
						Buffer++;
					} while (*Buffer && *Buffer != TCHAR('\"') && *Buffer != TCHAR('\n') && *Buffer != TCHAR('\r'));

					if (*Buffer != TCHAR('\"'))
					{
						ErrorText->Logf(TEXT("%sImportText (%s): Bad quoted string at: %s"), ErrorCount++ > 0 ? LINE_TERMINATOR : TEXT(""), *Name, Buffer);
						return NULL;
					}
				}
				else if( *Buffer == TCHAR('(') )
				{
					SubCount++;
				}
				else if( *Buffer == TCHAR(')') )
				{
					SubCount--;
					if( SubCount < 0 )
					{
						ErrorText->Logf(TEXT("%sImportText (%s): Too many closing parenthesis in: %s"), ErrorCount++ > 0 ? LINE_TERMINATOR : TEXT(""), *Name, InBuffer);
						return NULL;
					}
				}
				Buffer++;
			}
			if( SubCount > 0 )
			{
				ErrorText->Logf(TEXT("%sImportText(%s): Not enough closing parenthesis in: %s"), ErrorCount++ > 0 ? LINE_TERMINATOR : TEXT(""), *Name, InBuffer);
				return NULL;
			}

			// Skip comma.
			if( *Buffer==TCHAR(',') )
			{
				// Skip comma.
				Buffer++;
			}
			else if( *Buffer!=TCHAR(')') )
			{
				ErrorText->Logf(TEXT("%sImportText (%s): Missing closing parenthesis: %s"), ErrorCount++ > 0 ? LINE_TERMINATOR : TEXT(""), *Name, InBuffer);
				return NULL;
			}

			SkipWhitespace(Buffer);
		}

		// Skip trailing ')'.
		Buffer++;
	}
	else
	{
		ErrorText->Logf(TEXT("%sImportText (%s): Missing opening parenthesis: %s"), ErrorCount++ > 0 ? LINE_TERMINATOR : TEXT(""), *Name, InBuffer);
		return NULL;
	}
	return Buffer;
}
Example #10
0
bool FObjectReplicator::ReceivedBunch( FInBunch& Bunch, const FReplicationFlags& RepFlags, bool& bOutHasUnmapped )
{
	UObject* Object = GetObject();

	if ( Object == NULL )
	{
		UE_LOG(LogNet, Verbose, TEXT("ReceivedBunch: Object == NULL"));
		return false;
	}

	UPackageMap * PackageMap = OwningChannel->Connection->PackageMap;

	const bool bIsServer = ( OwningChannel->Connection->Driver->ServerConnection == NULL );

	const FClassNetCache * ClassCache = OwningChannel->Connection->Driver->NetCache->GetClassNetCache( ObjectClass );

	if ( ClassCache == NULL )
	{
		UE_LOG(LogNet, Error, TEXT("ReceivedBunch: ClassCache == NULL: %s"), *Object->GetFullName());
		return false;
	}

	bool bThisBunchReplicatedProperties = false;

	// Read first field
	const FFieldNetCache * FieldCache = ReadField( ClassCache, Bunch );

	if ( Bunch.IsError() )
	{
		UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Error reading field 1: %s"), *Object->GetFullName());
		return false;
	}

	if ( FieldCache == NULL )
	{
		// There are no actual replicated properties or functions in this bunch. That is ok - we may have gotten this
		// actor/sub-object because we want the client to spawn one (but we aren't actually replicating properties on it)
		return true;
	}

	while ( FieldCache )
	{
		// Receive properties from the net.
		UProperty* ReplicatedProp = NULL;

		while ( FieldCache && ( ReplicatedProp = Cast< UProperty >( FieldCache->Field ) ) != NULL )
		{
			NET_CHECKSUM( Bunch );

			// Server shouldn't receive properties.
			if ( bIsServer )
			{
				UE_LOG(LogNet, Error, TEXT("Server received unwanted property value %s in %s"), *ReplicatedProp->GetName(), *Object->GetFullName());
				return false;
			}
		
			bThisBunchReplicatedProperties = true;

			if ( !bHasReplicatedProperties )
			{
				bHasReplicatedProperties = true;		// Persistent, not reset until PostNetReceive is called
				PreNetReceive();
			}

			bool DebugProperty = false;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
			{
				static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Replication.DebugProperty"));
				if (CVar && !CVar->GetString().IsEmpty() && ReplicatedProp->GetName().Contains(CVar->GetString()) )
				{
					UE_LOG(LogRep, Log, TEXT("Replicating Property[%d] %s on %s"), ReplicatedProp->RepIndex, *ReplicatedProp->GetName(), *Object->GetName());
					DebugProperty = true;
				}
			}
#endif
			if ( !Retirement[ ReplicatedProp->RepIndex ].CustomDelta )
			{
				bool bLocalHasUnmapped = false;
				// We hijack a non custom delta property to signify we are using FRepLayout to read the entire property block
				if ( !RepLayout->ReceiveProperties( ObjectClass, RepState, (void*)Object, Bunch, bLocalHasUnmapped ) )
				{
					UE_LOG(LogRep, Error, TEXT("ReceiveProperties FAILED %s in %s"), *ReplicatedProp->GetName(), *Object->GetFullName());
					return false;
				}

				if ( bLocalHasUnmapped )
				{
					bOutHasUnmapped = true;
				}
			}
			else
			{
				// Receive array index.
				uint32 Element = 0;
				if ( ReplicatedProp->ArrayDim != 1 )
				{
					check( ReplicatedProp->ArrayDim >= 2 );

					Bunch.SerializeIntPacked( Element );

					if ( Element >= (uint32)ReplicatedProp->ArrayDim )
					{
						UE_LOG(LogRep, Error, TEXT("Element index too large %s in %s"), *ReplicatedProp->GetName(), *Object->GetFullName());
						return false;
					}
				}

				// Pointer to destination.
				uint8* Data = ReplicatedProp->ContainerPtrToValuePtr<uint8>((uint8*)Object, Element);
				TArray<uint8>	MetaData;
				const PTRINT DataOffset = Data - (uint8*)Object;

				// Receive custom delta property.
				UStructProperty * StructProperty = Cast< UStructProperty >( ReplicatedProp );

				if ( StructProperty == NULL )
				{
					// This property isn't custom delta
					UE_LOG(LogRepTraffic, Error, TEXT("Property isn't custom delta %s"), *ReplicatedProp->GetName());
					return false;
				}

				UScriptStruct * InnerStruct = StructProperty->Struct;

				if ( !( InnerStruct->StructFlags & STRUCT_NetDeltaSerializeNative ) )
				{
					// This property isn't custom delta
					UE_LOG(LogRepTraffic, Error, TEXT("Property isn't custom delta %s"), *ReplicatedProp->GetName());
					return false;
				}

				UScriptStruct::ICppStructOps * CppStructOps = InnerStruct->GetCppStructOps();

				check( CppStructOps );
				check( !InnerStruct->InheritedCppStructOps() );

				FNetDeltaSerializeInfo Parms;

				FNetSerializeCB NetSerializeCB( OwningChannel->Connection->Driver );

				Parms.DebugName			= StructProperty->GetName();
				Parms.Struct			= InnerStruct;
				Parms.Map				= PackageMap;
				Parms.Reader			= &Bunch;
				Parms.NetSerializeCB	= &NetSerializeCB;

				// Call the custom delta serialize function to handle it
				CppStructOps->NetDeltaSerialize( Parms, Data );

				if ( Bunch.IsError() )
				{
					UE_LOG(LogNet, Error, TEXT("ReceivedBunch: NetDeltaSerialize - Bunch.IsError() == true: %s"), *Object->GetFullName());
					return false;
				}

				if ( Parms.bOutHasMoreUnmapped )
				{
					UnmappedCustomProperties.Add( DataOffset, StructProperty );
					bOutHasUnmapped = true;
				}

				// Successfully received it.
				UE_LOG(LogRepTraffic, Log, TEXT(" %s - %s"), *Object->GetName(), *ReplicatedProp->GetName());

				// Notify the Object if this var is RepNotify
				QueuePropertyRepNotify( Object, ReplicatedProp, Element, MetaData );
			}	
			
			// Read next field
			FieldCache = ReadField( ClassCache, Bunch );

			if ( Bunch.IsError() )
			{
				UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Error reading field 2: %s"), *Object->GetFullName());
				return false;
			}
		}

		// Handle function calls.
		if ( FieldCache && Cast< UFunction >( FieldCache->Field ) )
		{
			FName Message = FieldCache->Field->GetFName();
			UFunction * Function = Object->FindFunction( Message );

			if ( Function == NULL )
			{
				UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Function == NULL: %s"), *Object->GetFullName());
				return false;
			}

			if ( ( Function->FunctionFlags & FUNC_Net ) == 0 )
			{
				UE_LOG(LogRep, Error, TEXT("Rejected non RPC function %s in %s"), *Message.ToString(), *Object->GetFullName());
				return false;
			}

			if ( ( Function->FunctionFlags & ( bIsServer ? FUNC_NetServer : ( FUNC_NetClient | FUNC_NetMulticast ) ) ) == 0 )
			{
				UE_LOG(LogRep, Error, TEXT("Rejected RPC function due to access rights %s in %s"), *Message.ToString(), *Object->GetFullName());
				return false;
			}

			UE_LOG(LogRepTraffic, Log, TEXT("      Received RPC: %s"), *Message.ToString());

			// Get the parameters.
			FMemMark Mark(FMemStack::Get());
			uint8* Parms = new(FMemStack::Get(),MEM_Zeroed,Function->ParmsSize)uint8;

			// Use the replication layout to receive the rpc parameter values
			TSharedPtr<FRepLayout> FuncRepLayout = OwningChannel->Connection->Driver->GetFunctionRepLayout( Function );

			FuncRepLayout->ReceivePropertiesForRPC( Object, Function, OwningChannel, Bunch, Parms );

			if ( Bunch.IsError() )
			{
				UE_LOG(LogRep, Error, TEXT("ReceivedBunch: ReceivePropertiesForRPC - Bunch.IsError() == true: Function: %s, Object: %s"), *Message.ToString(), *Object->GetFullName());
				return false;
			}

			// validate that the function is callable here
			const bool bCanExecute = ( !bIsServer || RepFlags.bNetOwner );		// we are client or net owner

			if ( bCanExecute )
			{
				// Call the function.
				RPC_ResetLastFailedReason();

				Object->ProcessEvent( Function, Parms );

				if ( RPC_GetLastFailedReason() != NULL )
				{
					UE_LOG(LogRep, Error, TEXT("ReceivedBunch: RPC_GetLastFailedReason: %s"), RPC_GetLastFailedReason());
					return false;
				}
			}
			else
			{
				UE_LOG(LogRep, Verbose, TEXT("Rejected unwanted function %s in %s"), *Message.ToString(), *Object->GetFullName());

				if ( !OwningChannel->Connection->TrackLogsPerSecond() )	// This will disconnect the client if we get here too often
				{
					UE_LOG(LogRep, Error, TEXT("Rejected too many unwanted functions %s in %s"), *Message.ToString(), *Object->GetFullName());
					return false;
				}
			}

			// Destroy the parameters.
			//warning: highly dependent on UObject::ProcessEvent freeing of parms!
			for ( UProperty * Destruct=Function->DestructorLink; Destruct; Destruct=Destruct->DestructorLinkNext )
			{
				if( Destruct->IsInContainer(Function->ParmsSize) )
				{
					Destruct->DestroyValue_InContainer(Parms);
				}
			}

			Mark.Pop();

			if ( Object == NULL || Object->IsPendingKill() )
			{
				// replicated function destroyed Object
				return true;		// FIXME: Should this return false to kick connection?  Seems we'll cause a read misalignment here if we don't
			}

			// Next.
			FieldCache = ReadField( ClassCache, Bunch );

			if ( Bunch.IsError() )
			{
				UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Error reading field 3: %s"), *Object->GetFullName());
				return false;
			}
		}
		else if ( FieldCache )
		{
			UE_LOG(LogRep, Error, TEXT("ReceivedBunch: Invalid replicated field %i in %s"), FieldCache->FieldNetIndex, *Object->GetFullName());
			return false;
		}
	}

	return true;
}
Example #11
0
void FObjectReplicator::UpdateUnmappedObjects( bool & bOutHasMoreUnmapped )
{
	UObject* Object = GetObject();

	if ( Object == NULL || Object->IsPendingKill() )
	{
		bOutHasMoreUnmapped = false;
		return;
	}

	if ( Connection->State == USOCK_Closed )
	{
		UE_LOG(LogNet, Warning, TEXT("FObjectReplicator::UpdateUnmappedObjects: Connection->State == USOCK_Closed"));
		return;
	}

	checkf( RepState->RepNotifies.Num() == 0, TEXT("Failed RepState RepNotifies check. Num=%d. Object=%s"), RepState->RepNotifies.Num(), *Object->GetFullName() );
	checkf( RepNotifies.Num() == 0, TEXT("Failed replicator RepNotifies check. Num=%d. Object=%s."), RepNotifies.Num(), *Object->GetFullName() );

	bool bSomeObjectsWereMapped = false;

	// Let the rep layout update any unmapped properties
	RepLayout->UpdateUnmappedObjects( RepState, Connection->PackageMap, Object, bSomeObjectsWereMapped, bOutHasMoreUnmapped );

	// Update unmapped objects for custom properties (currently just fast tarray)
	for ( auto It = UnmappedCustomProperties.CreateIterator(); It; ++It )
	{
		const int32			Offset			= It.Key();
		UStructProperty*	StructProperty	= It.Value();
		UScriptStruct*		InnerStruct		= StructProperty->Struct;

		check( InnerStruct->StructFlags & STRUCT_NetDeltaSerializeNative );

		UScriptStruct::ICppStructOps* CppStructOps = InnerStruct->GetCppStructOps();

		check( CppStructOps );
		check( !InnerStruct->InheritedCppStructOps() );

		FNetDeltaSerializeInfo Parms;

		FNetSerializeCB NetSerializeCB( OwningChannel->Connection->Driver );

		Parms.DebugName			= StructProperty->GetName();
		Parms.Struct			= InnerStruct;
		Parms.Map				= Connection->PackageMap;
		Parms.NetSerializeCB	= &NetSerializeCB;

		Parms.bUpdateUnmappedObjects	= true;
		Parms.bCalledPreNetReceive		= bSomeObjectsWereMapped;	// RepLayout used this to flag whether PreNetReceive was called
		Parms.Object					= Object;

		// Call the custom delta serialize function to handle it
		CppStructOps->NetDeltaSerialize( Parms, (uint8*)Object + Offset );

		// Merge in results
		bSomeObjectsWereMapped	|= Parms.bOutSomeObjectsWereMapped;
		bOutHasMoreUnmapped		|= Parms.bOutHasMoreUnmapped;

		if ( Parms.bOutSomeObjectsWereMapped )
		{
			// If we mapped a property, call the rep notify
			TArray<uint8> MetaData;
			QueuePropertyRepNotify( Object, StructProperty, 0, MetaData );
		}

		// If this property no longer has unmapped objects, we can stop checking it
		if ( !Parms.bOutHasMoreUnmapped )
		{
			It.RemoveCurrent();
		}
	}

	// Call any rep notifies that need to happen when object pointers change
	// Pass in false to override the check for queued bunches. Otherwise, if the owning channel has queued bunches,
	// the RepNotifies will remain in the list and the check for 0 RepNotifies above will fail next time.
	CallRepNotifies(false);

	if ( bSomeObjectsWereMapped )
	{
		// If we mapped some objects, make sure to call PostNetReceive (some game code will need to think this was actually replicated to work)
		PostNetReceive();
	}
}