FGameplayAbilityTargetDataHandle UAbilitySystemBlueprintLibrary::FilterTargetData(FGameplayAbilityTargetDataHandle TargetDataHandle, FGameplayTargetDataFilterHandle FilterHandle) { FGameplayAbilityTargetDataHandle ReturnDataHandle; for (int32 i = 0; TargetDataHandle.IsValid(i); ++i) { FGameplayAbilityTargetData* UnfilteredData = TargetDataHandle.Get(i); check(UnfilteredData); if (UnfilteredData->GetActors().Num() > 0) { TArray<TWeakObjectPtr<AActor>> FilteredActors = UnfilteredData->GetActors().FilterByPredicate(FilterHandle); if (FilteredActors.Num() > 0) { //Copy the data first, since we don't understand the internals of it UScriptStruct* ScriptStruct = UnfilteredData->GetScriptStruct(); FGameplayAbilityTargetData* NewData = (FGameplayAbilityTargetData*)FMemory::Malloc(ScriptStruct->GetCppStructOps()->GetSize()); ScriptStruct->InitializeStruct(NewData); ScriptStruct->CopyScriptStruct(NewData, UnfilteredData); ReturnDataHandle.Data.Add(TSharedPtr<FGameplayAbilityTargetData>(NewData)); if (FilteredActors.Num() < UnfilteredData->GetActors().Num()) { //We have lost some, but not all, of our actors, so replace the array. This should only be possible with targeting types that permit actor-array setting. if (!NewData->SetActors(FilteredActors)) { //This is an error, though we could ignore it. We somehow filtered out part of a list, but the class doesn't support changing the list, so now it's all or nothing. check(false); } } } } } return ReturnDataHandle; }
void UDataTable::LoadStructData(FArchive& Ar) { UScriptStruct* LoadUsingStruct = RowStruct; if (LoadUsingStruct == NULL) { UE_LOG(LogDataTable, Error, TEXT("Missing RowStruct while loading DataTable '%s'!"), *GetPathName()); LoadUsingStruct = FTableRowBase::StaticStruct(); } int32 NumRows; Ar << NumRows; for (int32 RowIdx = 0; RowIdx < NumRows; RowIdx++) { // Load row name FName RowName; Ar << RowName; // Load row data uint8* RowData = (uint8*)FMemory::Malloc(LoadUsingStruct->PropertiesSize); LoadUsingStruct->InitializeStruct(RowData); // And be sure to call DestroyScriptStruct later LoadUsingStruct->SerializeTaggedProperties(Ar, RowData, LoadUsingStruct, NULL); // Add to map RowMap.Add(RowName, RowData); } }
bool FGameplayEffectContextHandle::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) { UScriptStruct* ScriptStruct = Data.IsValid() ? Data->GetScriptStruct() : NULL; Ar << ScriptStruct; if (ScriptStruct) { if (Ar.IsLoading()) { // For now, just always reset/reallocate the data when loading. // Longer term if we want to generalize this and use it for property replication, we should support // only reallocating when necessary check(!Data.IsValid()); FGameplayEffectContext * NewData = (FGameplayEffectContext*)FMemory::Malloc(ScriptStruct->GetCppStructOps()->GetSize()); ScriptStruct->InitializeStruct(NewData); Data = TSharedPtr<FGameplayEffectContext>(NewData); } void* ContainerPtr = Data.Get(); if (ScriptStruct->StructFlags & STRUCT_NetSerializeNative) { ScriptStruct->GetCppStructOps()->NetSerialize(Ar, Map, bOutSuccess, Data.Get()); } else { // This won't work since UStructProperty::NetSerializeItem is deprecrated. // 1) we have to manually crawl through the topmost struct's fields since we don't have a UStructProperty for it (just the UScriptProperty) // 2) if there are any UStructProperties in the topmost struct's fields, we will assert in UStructProperty::NetSerializeItem. ABILITY_LOG(Fatal, TEXT("FGameplayEffectContextHandle::NetSerialize called on data struct %s without a native NetSerialize"), *ScriptStruct->GetName()); for (TFieldIterator<UProperty> It(ScriptStruct); It; ++It) { if (It->PropertyFlags & CPF_RepSkip) { continue; } void * PropertyData = It->ContainerPtrToValuePtr<void*>(ContainerPtr); It->NetSerializeItem(Ar, Map, PropertyData); } } } bOutSuccess = true; return true; }
void UDataTable::EmptyTable() { UScriptStruct* LoadUsingStruct = RowStruct; if (LoadUsingStruct == NULL) { UE_LOG(LogDataTable, Error, TEXT("Missing RowStruct while emptying DataTable '%s'!"), *GetPathName()); LoadUsingStruct = FTableRowBase::StaticStruct(); } // Iterate over all rows in table and free mem for (auto RowIt = RowMap.CreateIterator(); RowIt; ++RowIt) { uint8* RowData = RowIt.Value(); LoadUsingStruct->DestroyStruct(RowData); FMemory::Free(RowData); } // Finally empty the map RowMap.Empty(); }
bool UDataTableFunctionLibrary::Generic_GetDataTableRowFromName(UDataTable* Table, FName RowName, void* OutRowPtr) { bool bFoundRow = false; if (OutRowPtr && Table) { void* RowPtr = Table->FindRowUnchecked(RowName); if (RowPtr != NULL) { UScriptStruct* StructType = Table->RowStruct; if (StructType != NULL) { StructType->CopyScriptStruct(OutRowPtr, RowPtr); bFoundRow = true; } } } return bFoundRow; }
TArray<UScriptStruct*> FDataTableEditorUtils::GetPossibleStructs() { TArray< UScriptStruct* > RowStructs; UScriptStruct* TableRowStruct = FindObjectChecked<UScriptStruct>(ANY_PACKAGE, TEXT("TableRowBase")); if (TableRowStruct != NULL) { // Make combo of table rowstruct options for (TObjectIterator<UScriptStruct> It; It; ++It) { UScriptStruct* Struct = *It; // If a child of the table row struct base, but not itself const bool bBasedOnTableRowBase = Struct->IsChildOf(TableRowStruct) && (Struct != TableRowStruct); const bool bUDStruct = Struct->IsA<UUserDefinedStruct>(); const bool bValidStruct = (Struct->GetOutermost() != GetTransientPackage()); if ((bBasedOnTableRowBase || bUDStruct) && bValidStruct) { RowStructs.Add(Struct); } } } return RowStructs; }
bool FGameplayEffectContextHandle::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) { bool ValidData = Data.IsValid(); Ar.SerializeBits(&ValidData,1); if (ValidData) { if (Ar.IsLoading()) { // For now, just always reset/reallocate the data when loading. // Longer term if we want to generalize this and use it for property replication, we should support // only reallocating when necessary if (Data.IsValid() == false) { Data = TSharedPtr<FGameplayEffectContext>(UAbilitySystemGlobals::Get().AllocGameplayEffectContext()); } } void* ContainerPtr = Data.Get(); UScriptStruct* ScriptStruct = Data->GetScriptStruct(); if (ScriptStruct->StructFlags & STRUCT_NetSerializeNative) { ScriptStruct->GetCppStructOps()->NetSerialize(Ar, Map, bOutSuccess, Data.Get()); } else { // This won't work since UStructProperty::NetSerializeItem is deprecrated. // 1) we have to manually crawl through the topmost struct's fields since we don't have a UStructProperty for it (just the UScriptProperty) // 2) if there are any UStructProperties in the topmost struct's fields, we will assert in UStructProperty::NetSerializeItem. ABILITY_LOG(Fatal, TEXT("FGameplayEffectContextHandle::NetSerialize called on data struct %s without a native NetSerialize"), *ScriptStruct->GetName()); } } bOutSuccess = true; return true; }
bool FGameplayAbilityTargetDataHandle::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) { uint8 DataNum; if (Ar.IsSaving()) { UE_CLOG(Data.Num() > MAX_uint8, LogAbilitySystem, Warning, TEXT("Too many TargetData sources (%d!) to net serialize. Clamping to %d"), Data.Num(), MAX_uint8); DataNum = FMath::Min<int32>( Data.Num(), MAX_uint8 ); } Ar << DataNum; if (Ar.IsLoading()) { Data.SetNumZeroed(DataNum); } for (int32 i = 0; i < DataNum && !Ar.IsError(); ++i) { UScriptStruct* ScriptStruct = Data[i].IsValid() ? Data[i]->GetScriptStruct() : NULL; Ar << ScriptStruct; if (ScriptStruct) { if (Ar.IsLoading()) { // For now, just always reset/reallocate the data when loading. // Longer term if we want to generalize this and use it for property replication, we should support // only reallocating when necessary check(!Data[i].IsValid()); FGameplayAbilityTargetData * NewData = (FGameplayAbilityTargetData*)FMemory::Malloc(ScriptStruct->GetCppStructOps()->GetSize()); ScriptStruct->InitializeStruct(NewData); Data[i] = TSharedPtr<FGameplayAbilityTargetData>(NewData); } void* ContainerPtr = Data[i].Get(); if (ScriptStruct->StructFlags & STRUCT_NetSerializeNative) { ScriptStruct->GetCppStructOps()->NetSerialize(Ar, Map, bOutSuccess, Data[i].Get()); } else { // This won't work since UStructProperty::NetSerializeItem is deprecrated. // 1) we have to manually crawl through the topmost struct's fields since we don't have a UStructProperty for it (just the UScriptProperty) // 2) if there are any UStructProperties in the topmost struct's fields, we will assert in UStructProperty::NetSerializeItem. ABILITY_LOG(Fatal, TEXT("FGameplayAbilityTargetDataHandle::NetSerialize called on data struct %s without a native NetSerialize"), *ScriptStruct->GetName()); for (TFieldIterator<UProperty> It(ScriptStruct); It; ++It) { if (It->PropertyFlags & CPF_RepSkip) { continue; } void* PropertyData = It->ContainerPtrToValuePtr<void*>(ContainerPtr); It->NetSerializeItem(Ar, Map, PropertyData); } } } } //ABILITY_LOG(Warning, TEXT("FGameplayAbilityTargetDataHandle Serialized: %s"), ScriptStruct ? *ScriptStruct->GetName() : TEXT("NULL") ); bOutSuccess = true; return true; }
void FEmitDefaultValueHelper::OuterGenerate(FEmitterLocalContext& Context , const UProperty* Property , const FString& OuterPath , const uint8* DataContainer , const uint8* OptionalDefaultDataContainer , EPropertyAccessOperator AccessOperator , bool bAllowProtected) { // Determine if the given property contains an instanced default subobject reference. We only get here if the values are not identical. auto IsInstancedSubobjectLambda = [&](int32 ArrayIndex) -> bool { if (auto ObjectProperty = Cast<UObjectProperty>(Property)) { check(DataContainer); check(OptionalDefaultDataContainer); auto ObjectPropertyValue = ObjectProperty->GetObjectPropertyValue_InContainer(DataContainer, ArrayIndex); auto DefaultObjectPropertyValue = ObjectProperty->GetObjectPropertyValue_InContainer(OptionalDefaultDataContainer, ArrayIndex); if (ObjectPropertyValue && ObjectPropertyValue->IsDefaultSubobject() && DefaultObjectPropertyValue && DefaultObjectPropertyValue->IsDefaultSubobject() && ObjectPropertyValue->GetFName() == DefaultObjectPropertyValue->GetFName()) { return true; } } return false; }; if (Property->HasAnyPropertyFlags(CPF_EditorOnly | CPF_Transient)) { UE_LOG(LogK2Compiler, Verbose, TEXT("FEmitDefaultValueHelper Skip EditorOnly or Transient property: %s"), *Property->GetPathName()); return; } for (int32 ArrayIndex = 0; ArrayIndex < Property->ArrayDim; ++ArrayIndex) { if (!OptionalDefaultDataContainer || (!Property->Identical_InContainer(DataContainer, OptionalDefaultDataContainer, ArrayIndex) && !IsInstancedSubobjectLambda(ArrayIndex))) { FString PathToMember; UBlueprintGeneratedClass* PropertyOwnerAsBPGC = Cast<UBlueprintGeneratedClass>(Property->GetOwnerClass()); UScriptStruct* PropertyOwnerAsScriptStruct = Cast<UScriptStruct>(Property->GetOwnerStruct()); const bool bNoexportProperty = PropertyOwnerAsScriptStruct && PropertyOwnerAsScriptStruct->IsNative() && (PropertyOwnerAsScriptStruct->StructFlags & STRUCT_NoExport) // && !PropertyOwnerAsScriptStruct->GetBoolMetaData(TEXT("BlueprintType")) && ensure(EPropertyAccessOperator::Dot == AccessOperator); if (PropertyOwnerAsBPGC && !Context.Dependencies.WillClassBeConverted(PropertyOwnerAsBPGC)) { ensure(EPropertyAccessOperator::None != AccessOperator); const FString OperatorStr = (EPropertyAccessOperator::Dot == AccessOperator) ? TEXT("&") : TEXT(""); const FString ContainerStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("this") : FString::Printf(TEXT("%s(%s)"), *OperatorStr, *OuterPath); PathToMember = FString::Printf(TEXT("FUnconvertedWrapper__%s(%s).GetRef__%s()"), *FEmitHelper::GetCppName(PropertyOwnerAsBPGC), *ContainerStr , *UnicodeToCPPIdentifier(Property->GetName(), false, nullptr)); } else if (bNoexportProperty || Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate) || (!bAllowProtected && Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierProtected))) { ensure(EPropertyAccessOperator::None != AccessOperator); const FString OperatorStr = (EPropertyAccessOperator::Dot == AccessOperator) ? TEXT("&") : TEXT(""); const FString ContainerStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("this") : OuterPath; const FString GetPtrStr = bNoexportProperty ? FEmitHelper::AccessInaccessiblePropertyUsingOffset(Context, Property, ContainerStr, OperatorStr, ArrayIndex) : FEmitHelper::AccessInaccessibleProperty(Context, Property, ContainerStr, OperatorStr, ArrayIndex, false); PathToMember = Context.GenerateUniqueLocalName(); Context.AddLine(FString::Printf(TEXT("auto& %s = %s;"), *PathToMember, *GetPtrStr)); } else { const FString AccessOperatorStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("") : ((EPropertyAccessOperator::Pointer == AccessOperator) ? TEXT("->") : TEXT(".")); const bool bStaticArray = (Property->ArrayDim > 1); const FString ArrayPost = bStaticArray ? FString::Printf(TEXT("[%d]"), ArrayIndex) : TEXT(""); PathToMember = FString::Printf(TEXT("%s%s%s%s"), *OuterPath, *AccessOperatorStr, *FEmitHelper::GetCppName(Property), *ArrayPost); } const uint8* ValuePtr = Property->ContainerPtrToValuePtr<uint8>(DataContainer, ArrayIndex); const uint8* DefaultValuePtr = OptionalDefaultDataContainer ? Property->ContainerPtrToValuePtr<uint8>(OptionalDefaultDataContainer, ArrayIndex) : nullptr; InnerGenerate(Context, Property, PathToMember, ValuePtr, DefaultValuePtr); } } }
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; }
/** * Finds the metadata for the property specified * * @param Prop the property to search for * * @return pointer to the metadata for the property specified, or NULL * if the property doesn't exist in the list (for example, if it * is declared in a package that is already compiled and has had its * source stripped) */ FTokenData* FClassMetaData::FindTokenData( UProperty* Prop ) { check(Prop); FTokenData* Result = nullptr; UObject* Outer = Prop->GetOuter(); UClass* OuterClass = nullptr; if (Outer->IsA<UStruct>()) { Result = GlobalPropertyData.Find(Prop); if (Result == nullptr) { OuterClass = Cast<UClass>(Outer); if (Result == nullptr && OuterClass != nullptr && OuterClass->GetSuperClass() != OuterClass) { OuterClass = OuterClass->GetSuperClass(); } } } else { UFunction* OuterFunction = Cast<UFunction>(Outer); if ( OuterFunction != NULL ) { // function parameter, return, or local property FFunctionData* FuncData = nullptr; if (FFunctionData::TryFindForFunction(OuterFunction, FuncData)) { FPropertyData& FunctionParameters = FuncData->GetParameterData(); Result = FunctionParameters.Find(Prop); if ( Result == NULL ) { Result = FuncData->GetReturnTokenData(); } } else { OuterClass = OuterFunction->GetOwnerClass(); } } else { // struct property UScriptStruct* OuterStruct = Cast<UScriptStruct>(Outer); check(OuterStruct != NULL); TScopedPointer<FStructData>* pStructInfo = StructData.Find(OuterStruct); if ( pStructInfo != NULL ) { FStructData* StructInfo = pStructInfo->GetOwnedPointer(); check(StructInfo); FPropertyData& StructProperties = StructInfo->GetStructPropertyData(); Result = StructProperties.Find(Prop); } else { OuterClass = OuterStruct->GetOwnerClass(); } } } if (Result == nullptr && OuterClass != nullptr) { FClassMetaData* SuperClassData = GScriptHelper.FindClassData(OuterClass); if (SuperClassData && SuperClassData != this) { Result = SuperClassData->FindTokenData(Prop); } } return Result; }
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; }
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(); } }
void FKismetBytecodeDisassembler::ProcessCommon(int32& ScriptIndex, EExprToken Opcode) { switch (Opcode) { case EX_PrimitiveCast: { // A type conversion. uint8 ConversionType = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: PrimitiveCast of type %d"), *Indents, (int32)Opcode, ConversionType); AddIndent(); Ar.Logf(TEXT("%s Argument:"), *Indents); ProcessCastByte(ConversionType, ScriptIndex); //@TODO: //Ar.Logf(TEXT("%s Expression:"), *Indents); //SerializeExpr( ScriptIndex ); break; } case EX_ObjToInterfaceCast: { // A conversion from an object variable to a native interface variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass* InterfaceClass = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: ObjToInterfaceCast to %s"), *Indents, (int32)Opcode, *InterfaceClass->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_CrossInterfaceCast: { // A conversion from one interface variable to a different interface variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass* InterfaceClass = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: InterfaceToInterfaceCast to %s"), *Indents, (int32)Opcode, *InterfaceClass->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_InterfaceToObjCast: { // A conversion from an interface variable to a object variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass* ObjectClass = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: InterfaceToObjCast to %s"), *Indents, (int32)Opcode, *ObjectClass->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_Let: { Ar.Logf(TEXT("%s $%X: Let (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetObj: case EX_LetWeakObjPtr: { if( Opcode == EX_LetObj ) { Ar.Logf(TEXT("%s $%X: Let Obj (Variable = Expression)"), *Indents, (int32)Opcode); } else { Ar.Logf(TEXT("%s $%X: Let WeakObjPtr (Variable = Expression)"), *Indents, (int32)Opcode); } AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetBool: { Ar.Logf(TEXT("%s $%X: LetBool (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case Ex_LetValueOnPersistentFrame: { Ar.Logf(TEXT("%s $%X: LetValueOnPersistentFrame"), *Indents, (int32)Opcode); AddIndent(); auto Prop = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s Destination variable: %s, offset: %d"), *Indents, *GetNameSafe(Prop), Prop ? Prop->GetOffset_ForDebug() : 0); Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr(ScriptIndex); DropIndent(); break; } case EX_StructMemberContext: { Ar.Logf(TEXT("%s $%X: Struct member context "), *Indents, (int32)Opcode); AddIndent(); UProperty* Prop = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s Expression within struct %s, offset %d"), *Indents, *(Prop->GetName()), Prop->GetOffset_ForDebug()); // although that isn't a UFunction, we are not going to indirect the props of a struct, so this should be fine Ar.Logf(TEXT("%s Expression to struct:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetDelegate: { Ar.Logf(TEXT("%s $%X: LetDelegate (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetMulticastDelegate: { Ar.Logf(TEXT("%s $%X: LetMulticastDelegate (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_ComputedJump: { Ar.Logf(TEXT("%s $%X: Computed Jump, offset specified by expression:"), *Indents, (int32)Opcode); AddIndent(); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_Jump: { CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: Jump to offset 0x%X"), *Indents, (int32)Opcode, SkipCount); break; } case EX_LocalVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Local variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_InstanceVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Instance variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_LocalOutVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Local out variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_InterfaceContext: { Ar.Logf(TEXT("%s $%X: EX_InterfaceContext:"), *Indents, (int32)Opcode); SerializeExpr(ScriptIndex); break; } case EX_DeprecatedOp4A: { Ar.Logf(TEXT("%s $%X: This opcode has been removed and does nothing."), *Indents, (int32)Opcode); break; } case EX_Nothing: { Ar.Logf(TEXT("%s $%X: EX_Nothing"), *Indents, (int32)Opcode); break; } case EX_EndOfScript: { Ar.Logf(TEXT("%s $%X: EX_EndOfScript"), *Indents, (int32)Opcode); break; } case EX_EndFunctionParms: { Ar.Logf(TEXT("%s $%X: EX_EndFunctionParms"), *Indents, (int32)Opcode); break; } case EX_EndStructConst: { Ar.Logf(TEXT("%s $%X: EX_EndStructConst"), *Indents, (int32)Opcode); break; } case EX_EndArray: { Ar.Logf(TEXT("%s $%X: EX_EndArray"), *Indents, (int32)Opcode); break; } case EX_IntZero: { Ar.Logf(TEXT("%s $%X: EX_IntZero"), *Indents, (int32)Opcode); break; } case EX_IntOne: { Ar.Logf(TEXT("%s $%X: EX_IntOne"), *Indents, (int32)Opcode); break; } case EX_True: { Ar.Logf(TEXT("%s $%X: EX_True"), *Indents, (int32)Opcode); break; } case EX_False: { Ar.Logf(TEXT("%s $%X: EX_False"), *Indents, (int32)Opcode); break; } case EX_NoObject: { Ar.Logf(TEXT("%s $%X: EX_NoObject"), *Indents, (int32)Opcode); break; } case EX_NoInterface: { Ar.Logf(TEXT("%s $%X: EX_NoObject"), *Indents, (int32)Opcode); break; } case EX_Self: { Ar.Logf(TEXT("%s $%X: EX_Self"), *Indents, (int32)Opcode); break; } case EX_EndParmValue: { Ar.Logf(TEXT("%s $%X: EX_EndParmValue"), *Indents, (int32)Opcode); break; } case EX_Return: { Ar.Logf(TEXT("%s $%X: Return expression"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); // Return expression. break; } case EX_FinalFunction: { UStruct* StackNode = ReadPointer<UStruct>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Final Function (stack node %s::%s)"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); while (SerializeExpr( ScriptIndex ) != EX_EndFunctionParms) { // Params } break; } case EX_CallMulticastDelegate: { UStruct* StackNode = ReadPointer<UStruct>(ScriptIndex); Ar.Logf(TEXT("%s $%X: CallMulticastDelegate (signature %s::%s) delegate:"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); SerializeExpr( ScriptIndex ); Ar.Logf(TEXT("Params:")); while (SerializeExpr( ScriptIndex ) != EX_EndFunctionParms) { // Params } break; } case EX_VirtualFunction: { FString FunctionName = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: Virtual Function named %s"), *Indents, (int32)Opcode, *FunctionName); while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) { } break; } case EX_Context: case EX_Context_FailSilent: { Ar.Logf(TEXT("%s $%X: %s"), *Indents, (int32)Opcode, TEXT("Context")); AddIndent(); // Object expression. Ar.Logf(TEXT("%s ObjectExpression:"), *Indents); SerializeExpr( ScriptIndex ); if (Opcode == EX_Context_FailSilent) { Ar.Logf(TEXT(" Can fail silently on access none ")); } // Code offset for NULL expressions. CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s Skip Bytes: 0x%X"), *Indents, SkipCount); // Property corresponding to the r-value data, in case the l-value needs to be mem-zero'd UField* Field = ReadPointer<UField>(ScriptIndex); Ar.Logf(TEXT("%s R-Value Property: %s"), *Indents, Field ? *Field->GetName() : TEXT("(null)")); // Property type, in case the r-value is a non-property such as dynamic array length uint8 PropType = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s PropertyTypeIfNeeded: %d"), *Indents, PropType); // Context expression. Ar.Logf(TEXT("%s ContextExpression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_IntConst: { int32 ConstValue = ReadINT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal int32 %d"), *Indents, (int32)Opcode, ConstValue); break; } case EX_SkipOffsetConst: { CodeSkipSizeType ConstValue = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal CodeSkipSizeType 0x%X"), *Indents, (int32)Opcode, ConstValue); break; } case EX_FloatConst: { float ConstValue = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal float %f"), *Indents, (int32)Opcode, ConstValue); break; } case EX_StringConst: { FString ConstValue = ReadString8(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal ansi string \"%s\""), *Indents, (int32)Opcode, *ConstValue); break; } case EX_UnicodeStringConst: { FString ConstValue = ReadString16(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal unicode string \"%s\""), *Indents, (int32)Opcode, *ConstValue); break; } case EX_TextConst: { Ar.Logf(TEXT("%s $%X: literal text"), *Indents, (int32)Opcode); break; } case EX_ObjectConst: { UObject* Pointer = ReadPointer<UObject>(ScriptIndex); Ar.Logf(TEXT("%s $%X: EX_ObjectConst (%p:%s)"), *Indents, (int32)Opcode, Pointer, *Pointer->GetFullName()); break; } case EX_NameConst: { FString ConstValue = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal name %s"), *Indents, (int32)Opcode, *ConstValue); break; } case EX_RotationConst: { float Pitch = ReadFLOAT(ScriptIndex); float Yaw = ReadFLOAT(ScriptIndex); float Roll = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal rotation (%f,%f,%f)"), *Indents, (int32)Opcode, Pitch, Yaw, Roll); break; } case EX_VectorConst: { float X = ReadFLOAT(ScriptIndex); float Y = ReadFLOAT(ScriptIndex); float Z = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal vector (%f,%f,%f)"), *Indents, (int32)Opcode, X, Y, Z); break; } case EX_TransformConst: { float RotX = ReadFLOAT(ScriptIndex); float RotY = ReadFLOAT(ScriptIndex); float RotZ = ReadFLOAT(ScriptIndex); float RotW = ReadFLOAT(ScriptIndex); float TransX = ReadFLOAT(ScriptIndex); float TransY = ReadFLOAT(ScriptIndex); float TransZ = ReadFLOAT(ScriptIndex); float ScaleX = ReadFLOAT(ScriptIndex); float ScaleY = ReadFLOAT(ScriptIndex); float ScaleZ = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal transform R(%f,%f,%f,%f) T(%f,%f,%f) S(%f,%f,%f)"), *Indents, (int32)Opcode, TransX, TransY, TransZ, RotX, RotY, RotZ, RotW, ScaleX, ScaleY, ScaleZ); break; } case EX_StructConst: { UScriptStruct* Struct = ReadPointer<UScriptStruct>(ScriptIndex); int32 SerializedSize = ReadINT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal struct %s (serialized size: %d)"), *Indents, (int32)Opcode, *Struct->GetName(), SerializedSize); while( SerializeExpr(ScriptIndex) != EX_EndStructConst ) { // struct contents } break; } case EX_SetArray: { Ar.Logf(TEXT("%s $%X: set array"), *Indents, (int32)Opcode); SerializeExpr(ScriptIndex); while( SerializeExpr(ScriptIndex) != EX_EndArray) { // Array contents } break; } case EX_ByteConst: { uint8 ConstValue = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal byte %d"), *Indents, (int32)Opcode, ConstValue); break; } case EX_IntConstByte: { int32 ConstValue = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal int %d"), *Indents, (int32)Opcode, ConstValue); break; } case EX_MetaCast: { UClass* Class = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: MetaCast to %s of expr:"), *Indents, (int32)Opcode, *Class->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_DynamicCast: { UClass* Class = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: DynamicCast to %s of expr:"), *Indents, (int32)Opcode, *Class->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_JumpIfNot: { // Code offset. CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: Jump to offset 0x%X if not expr:"), *Indents, (int32)Opcode, SkipCount); // Boolean expr. SerializeExpr( ScriptIndex ); break; } case EX_Assert: { uint16 LineNumber = ReadWORD(ScriptIndex); uint8 InDebugMode = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: assert at line %d, in debug mode = %d with expr:"), *Indents, (int32)Opcode, LineNumber, InDebugMode); SerializeExpr( ScriptIndex ); // Assert expr. break; } case EX_Skip: { CodeSkipSizeType W = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: possibly skip 0x%X bytes of expr:"), *Indents, (int32)Opcode, W); // Expression to possibly skip. SerializeExpr( ScriptIndex ); break; } case EX_InstanceDelegate: { // the name of the function assigned to the delegate. FString FuncName = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: instance delegate function named %s"), *Indents, (int32)Opcode, *FuncName); break; } case EX_AddMulticastDelegate: { Ar.Logf(TEXT("%s $%X: Add MC delegate"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); SerializeExpr( ScriptIndex ); break; } case EX_RemoveMulticastDelegate: { Ar.Logf(TEXT("%s $%X: Remove MC delegate"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); SerializeExpr( ScriptIndex ); break; } case EX_ClearMulticastDelegate: { Ar.Logf(TEXT("%s $%X: Clear MC delegate"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); break; } case EX_BindDelegate: { // the name of the function assigned to the delegate. FString FuncName = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: BindDelegate '%s' "), *Indents, (int32)Opcode, *FuncName); Ar.Logf(TEXT("%s Delegate:"), *Indents); SerializeExpr( ScriptIndex ); Ar.Logf(TEXT("%s Object:"), *Indents); SerializeExpr( ScriptIndex ); break; } case EX_PushExecutionFlow: { CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: FlowStack.Push(0x%X);"), *Indents, (int32)Opcode, SkipCount); break; } case EX_PopExecutionFlow: { Ar.Logf(TEXT("%s $%X: if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! }"), *Indents, (int32)Opcode); break; } case EX_PopExecutionFlowIfNot: { Ar.Logf(TEXT("%s $%X: if (!condition) { if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! } }"), *Indents, (int32)Opcode); // Boolean expr. SerializeExpr( ScriptIndex ); break; } case EX_Breakpoint: { Ar.Logf(TEXT("%s $%X: <<< BREAKPOINT >>>"), *Indents, (int32)Opcode); break; } case EX_WireTracepoint: { Ar.Logf(TEXT("%s $%X: .. wire debug site .."), *Indents, (int32)Opcode); break; } case EX_Tracepoint: { Ar.Logf(TEXT("%s $%X: .. debug site .."), *Indents, (int32)Opcode); break; } default: { // This should never occur. UE_LOG(LogScriptDisassembler, Warning, TEXT("Unknown bytecode 0x%02X; ignoring it"), (uint8)Opcode ); break; } } }
/** Creates a property named PropertyName of type PropertyType in the Scope or returns NULL if the type is unknown, but does *not* link that property in */ UProperty* FKismetCompilerUtilities::CreatePropertyOnScope(UStruct* Scope, const FName& PropertyName, const FEdGraphPinType& Type, UClass* SelfClass, uint64 PropertyFlags, const UEdGraphSchema_K2* Schema, FCompilerResultsLog& MessageLog) { //@TODO: Check for name conflicts! // Properties are non-transactional as they're regenerated on every compile const EObjectFlags ObjectFlags = RF_Public; UProperty* NewProperty = NULL; UObject* PropertyScope = NULL; FName ValidatedPropertyName = PropertyName; // Check to see if there's already a property on this scope, and throw an internal compiler error if so // If this happens, it breaks the property link, which causes stack corruption and hard-to-track errors, so better to fail at this point { UProperty* ExistingProperty = FindObject<UProperty>(Scope, *PropertyName.ToString(), false); if( ExistingProperty ) { MessageLog.Error(*FString::Printf(TEXT("Internal Compiler Error: Duplicate property %s on scope %s"), *PropertyName.ToString(), (Scope ? *Scope->GetName() : TEXT("None")))); // Find a free name, so we can still create the property to make it easier to spot the duplicates, and avoid crashing uint32 Counter = 0; FString TestNameString; do { TestNameString = PropertyName.ToString() + FString::Printf(TEXT("_ERROR_DUPLICATE_%d"), Counter++); } while (FindObject<UProperty>(Scope, *TestNameString, false) != NULL); ValidatedPropertyName = FName(*TestNameString); } } // Handle creating an array property, if necessary const bool bIsArrayProperty = Type.bIsArray; UArrayProperty* NewArrayProperty = NULL; if( bIsArrayProperty ) { NewArrayProperty = NewNamedObject<UArrayProperty>(Scope, ValidatedPropertyName, ObjectFlags); PropertyScope = NewArrayProperty; } else { PropertyScope = Scope; } //@TODO: Nasty string if-else tree if (Type.PinCategory == Schema->PC_Object) { UClass* SubType = (Type.PinSubCategory == Schema->PSC_Self) ? SelfClass : Cast<UClass>(Type.PinSubCategoryObject.Get()); if( SubType == NULL ) { // If this is from a degenerate pin, because the object type has been removed, default this to a UObject subtype so we can make a dummy term for it to allow the compiler to continue SubType = UObject::StaticClass(); } if (SubType != NULL) { if (SubType->HasAnyClassFlags(CLASS_Interface)) { UInterfaceProperty* NewPropertyObj = NewNamedObject<UInterfaceProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); NewPropertyObj->InterfaceClass = SubType; NewProperty = NewPropertyObj; } else { UObjectPropertyBase* NewPropertyObj = NULL; if( Type.bIsWeakPointer ) { NewPropertyObj = NewNamedObject<UWeakObjectProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } else { NewPropertyObj = NewNamedObject<UObjectProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } NewPropertyObj->PropertyClass = SubType; NewProperty = NewPropertyObj; } } } else if (Type.PinCategory == Schema->PC_Struct) { UScriptStruct* SubType = Cast<UScriptStruct>(Type.PinSubCategoryObject.Get()); if (SubType != NULL) { FString StructureError; if (FStructureEditorUtils::EStructureError::Ok == FStructureEditorUtils::IsStructureValid(SubType, NULL, &StructureError)) { UStructProperty* NewPropertyStruct = NewNamedObject<UStructProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); NewPropertyStruct->Struct = SubType; NewProperty = NewPropertyStruct; } else { MessageLog.Error( *FString::Printf( *LOCTEXT("InvalidStructForField_Error", "Invalid property '%s' structure '%s' error: %s").ToString(), *PropertyName.ToString(), *SubType->GetName(), *StructureError )); } } } else if (Type.PinCategory == Schema->PC_Class) { UClass* SubType = Cast<UClass>(Type.PinSubCategoryObject.Get()); if (SubType != NULL) { UClassProperty* NewPropertyClass = NewNamedObject<UClassProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); NewPropertyClass->MetaClass = SubType; NewPropertyClass->PropertyClass = UClass::StaticClass(); NewProperty = NewPropertyClass; } } else if (Type.PinCategory == Schema->PC_Delegate) { if (UFunction* SignatureFunction = Cast<UFunction>(Type.PinSubCategoryObject.Get())) { UDelegateProperty* NewPropertyDelegate = NewNamedObject<UDelegateProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); NewPropertyDelegate->SignatureFunction = SignatureFunction; NewProperty = NewPropertyDelegate; } } else if (Type.PinCategory == Schema->PC_MCDelegate) { UFunction* const SignatureFunction = Cast<UFunction>(Type.PinSubCategoryObject.Get()); UMulticastDelegateProperty* NewPropertyDelegate = NewNamedObject<UMulticastDelegateProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); NewPropertyDelegate->SignatureFunction = SignatureFunction; NewProperty = NewPropertyDelegate; } else if (Type.PinCategory == Schema->PC_Int) { NewProperty = NewNamedObject<UIntProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } else if (Type.PinCategory == Schema->PC_Float) { NewProperty = NewNamedObject<UFloatProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } else if (Type.PinCategory == Schema->PC_Boolean) { UBoolProperty* BoolProperty = NewNamedObject<UBoolProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); BoolProperty->SetBoolSize(sizeof(bool), true); NewProperty = BoolProperty; } else if (Type.PinCategory == Schema->PC_String) { NewProperty = NewNamedObject<UStrProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } else if (Type.PinCategory == Schema->PC_Text) { NewProperty = NewNamedObject<UTextProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } else if (Type.PinCategory == Schema->PC_Byte) { UByteProperty* ByteProp = NewNamedObject<UByteProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); ByteProp->Enum = Cast<UEnum>(Type.PinSubCategoryObject.Get()); NewProperty = ByteProp; } else if (Type.PinCategory == Schema->PC_Name) { NewProperty = NewNamedObject<UNameProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } else { // Failed to resolve the type-subtype, create a generic property to survive VM bytecode emission NewProperty = NewNamedObject<UIntProperty>(PropertyScope, ValidatedPropertyName, ObjectFlags); } if( bIsArrayProperty ) { // Fix up the array property to have the new type-specific property as its inner, and return the new UArrayProperty NewArrayProperty->Inner = NewProperty; NewProperty = NewArrayProperty; } return NewProperty; }
void FKismetBytecodeDisassembler::ProcessCommon(int32& ScriptIndex, EExprToken Opcode) { switch (Opcode) { case EX_PrimitiveCast: { // A type conversion. uint8 ConversionType = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: PrimitiveCast of type %d"), *Indents, (int32)Opcode, ConversionType); AddIndent(); Ar.Logf(TEXT("%s Argument:"), *Indents); ProcessCastByte(ConversionType, ScriptIndex); //@TODO: //Ar.Logf(TEXT("%s Expression:"), *Indents); //SerializeExpr( ScriptIndex ); break; } case EX_ObjToInterfaceCast: { // A conversion from an object variable to a native interface variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass* InterfaceClass = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: ObjToInterfaceCast to %s"), *Indents, (int32)Opcode, *InterfaceClass->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_CrossInterfaceCast: { // A conversion from one interface variable to a different interface variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass* InterfaceClass = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: InterfaceToInterfaceCast to %s"), *Indents, (int32)Opcode, *InterfaceClass->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_InterfaceToObjCast: { // A conversion from an interface variable to a object variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass* ObjectClass = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: InterfaceToObjCast to %s"), *Indents, (int32)Opcode, *ObjectClass->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_Let: { Ar.Logf(TEXT("%s $%X: Let (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); ReadPointer<UProperty>(ScriptIndex); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetObj: case EX_LetWeakObjPtr: { if( Opcode == EX_LetObj ) { Ar.Logf(TEXT("%s $%X: Let Obj (Variable = Expression)"), *Indents, (int32)Opcode); } else { Ar.Logf(TEXT("%s $%X: Let WeakObjPtr (Variable = Expression)"), *Indents, (int32)Opcode); } AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetBool: { Ar.Logf(TEXT("%s $%X: LetBool (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetValueOnPersistentFrame: { Ar.Logf(TEXT("%s $%X: LetValueOnPersistentFrame"), *Indents, (int32)Opcode); AddIndent(); auto Prop = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s Destination variable: %s, offset: %d"), *Indents, *GetNameSafe(Prop), Prop ? Prop->GetOffset_ForDebug() : 0); Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr(ScriptIndex); DropIndent(); break; } case EX_StructMemberContext: { Ar.Logf(TEXT("%s $%X: Struct member context "), *Indents, (int32)Opcode); AddIndent(); UProperty* Prop = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s Expression within struct %s, offset %d"), *Indents, *(Prop->GetName()), Prop->GetOffset_ForDebug()); // although that isn't a UFunction, we are not going to indirect the props of a struct, so this should be fine Ar.Logf(TEXT("%s Expression to struct:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetDelegate: { Ar.Logf(TEXT("%s $%X: LetDelegate (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_LetMulticastDelegate: { Ar.Logf(TEXT("%s $%X: LetMulticastDelegate (Variable = Expression)"), *Indents, (int32)Opcode); AddIndent(); // Variable expr. Ar.Logf(TEXT("%s Variable:"), *Indents); SerializeExpr( ScriptIndex ); // Assignment expr. Ar.Logf(TEXT("%s Expression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_ComputedJump: { Ar.Logf(TEXT("%s $%X: Computed Jump, offset specified by expression:"), *Indents, (int32)Opcode); AddIndent(); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_Jump: { CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: Jump to offset 0x%X"), *Indents, (int32)Opcode, SkipCount); break; } case EX_LocalVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Local variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_DefaultVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Default variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_InstanceVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Instance variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_LocalOutVariable: { UProperty* PropertyPtr = ReadPointer<UProperty>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Local out variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); break; } case EX_InterfaceContext: { Ar.Logf(TEXT("%s $%X: EX_InterfaceContext:"), *Indents, (int32)Opcode); SerializeExpr(ScriptIndex); break; } case EX_DeprecatedOp4A: { Ar.Logf(TEXT("%s $%X: This opcode has been removed and does nothing."), *Indents, (int32)Opcode); break; } case EX_Nothing: { Ar.Logf(TEXT("%s $%X: EX_Nothing"), *Indents, (int32)Opcode); break; } case EX_EndOfScript: { Ar.Logf(TEXT("%s $%X: EX_EndOfScript"), *Indents, (int32)Opcode); break; } case EX_EndFunctionParms: { Ar.Logf(TEXT("%s $%X: EX_EndFunctionParms"), *Indents, (int32)Opcode); break; } case EX_EndStructConst: { Ar.Logf(TEXT("%s $%X: EX_EndStructConst"), *Indents, (int32)Opcode); break; } case EX_EndArray: { Ar.Logf(TEXT("%s $%X: EX_EndArray"), *Indents, (int32)Opcode); break; } case EX_EndArrayConst: { Ar.Logf(TEXT("%s $%X: EX_EndArrayConst"), *Indents, (int32)Opcode); break; } case EX_IntZero: { Ar.Logf(TEXT("%s $%X: EX_IntZero"), *Indents, (int32)Opcode); break; } case EX_IntOne: { Ar.Logf(TEXT("%s $%X: EX_IntOne"), *Indents, (int32)Opcode); break; } case EX_True: { Ar.Logf(TEXT("%s $%X: EX_True"), *Indents, (int32)Opcode); break; } case EX_False: { Ar.Logf(TEXT("%s $%X: EX_False"), *Indents, (int32)Opcode); break; } case EX_NoObject: { Ar.Logf(TEXT("%s $%X: EX_NoObject"), *Indents, (int32)Opcode); break; } case EX_NoInterface: { Ar.Logf(TEXT("%s $%X: EX_NoObject"), *Indents, (int32)Opcode); break; } case EX_Self: { Ar.Logf(TEXT("%s $%X: EX_Self"), *Indents, (int32)Opcode); break; } case EX_EndParmValue: { Ar.Logf(TEXT("%s $%X: EX_EndParmValue"), *Indents, (int32)Opcode); break; } case EX_Return: { Ar.Logf(TEXT("%s $%X: Return expression"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); // Return expression. break; } case EX_CallMath: { UStruct* StackNode = ReadPointer<UStruct>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Call Math (stack node %s::%s)"), *Indents, (int32)Opcode, *GetNameSafe(StackNode ? StackNode->GetOuter() : nullptr), *GetNameSafe(StackNode)); while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) { // Params } break; } case EX_FinalFunction: { UStruct* StackNode = ReadPointer<UStruct>(ScriptIndex); Ar.Logf(TEXT("%s $%X: Final Function (stack node %s::%s)"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); while (SerializeExpr( ScriptIndex ) != EX_EndFunctionParms) { // Params } break; } case EX_CallMulticastDelegate: { UStruct* StackNode = ReadPointer<UStruct>(ScriptIndex); Ar.Logf(TEXT("%s $%X: CallMulticastDelegate (signature %s::%s) delegate:"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); SerializeExpr( ScriptIndex ); Ar.Logf(TEXT("Params:")); while (SerializeExpr( ScriptIndex ) != EX_EndFunctionParms) { // Params } break; } case EX_VirtualFunction: { FString FunctionName = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: Virtual Function named %s"), *Indents, (int32)Opcode, *FunctionName); while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) { } break; } case EX_ClassContext: case EX_Context: case EX_Context_FailSilent: { Ar.Logf(TEXT("%s $%X: %s"), *Indents, (int32)Opcode, Opcode == EX_ClassContext ? TEXT("Class Context") : TEXT("Context")); AddIndent(); // Object expression. Ar.Logf(TEXT("%s ObjectExpression:"), *Indents); SerializeExpr( ScriptIndex ); if (Opcode == EX_Context_FailSilent) { Ar.Logf(TEXT(" Can fail silently on access none ")); } // Code offset for NULL expressions. CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s Skip Bytes: 0x%X"), *Indents, SkipCount); // Property corresponding to the r-value data, in case the l-value needs to be mem-zero'd UField* Field = ReadPointer<UField>(ScriptIndex); Ar.Logf(TEXT("%s R-Value Property: %s"), *Indents, Field ? *Field->GetName() : TEXT("(null)")); // Context expression. Ar.Logf(TEXT("%s ContextExpression:"), *Indents); SerializeExpr( ScriptIndex ); DropIndent(); break; } case EX_IntConst: { int32 ConstValue = ReadINT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal int32 %d"), *Indents, (int32)Opcode, ConstValue); break; } case EX_SkipOffsetConst: { CodeSkipSizeType ConstValue = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal CodeSkipSizeType 0x%X"), *Indents, (int32)Opcode, ConstValue); break; } case EX_FloatConst: { float ConstValue = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal float %f"), *Indents, (int32)Opcode, ConstValue); break; } case EX_StringConst: { FString ConstValue = ReadString8(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal ansi string \"%s\""), *Indents, (int32)Opcode, *ConstValue); break; } case EX_UnicodeStringConst: { FString ConstValue = ReadString16(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal unicode string \"%s\""), *Indents, (int32)Opcode, *ConstValue); break; } case EX_TextConst: { // What kind of text are we dealing with? const EBlueprintTextLiteralType TextLiteralType = (EBlueprintTextLiteralType)Script[ScriptIndex++]; switch (TextLiteralType) { case EBlueprintTextLiteralType::Empty: { Ar.Logf(TEXT("%s $%X: literal text - empty"), *Indents, (int32)Opcode); } break; case EBlueprintTextLiteralType::LocalizedText: { const FString SourceString = ReadString(ScriptIndex); const FString KeyString = ReadString(ScriptIndex); const FString Namespace = ReadString(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal text - localized text { namespace: \"%s\", key: \"%s\", source: \"%s\" }"), *Indents, (int32)Opcode, *Namespace, *KeyString, *SourceString); } break; case EBlueprintTextLiteralType::InvariantText: { const FString SourceString = ReadString(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal text - invariant text: \"%s\""), *Indents, (int32)Opcode, *SourceString); } break; case EBlueprintTextLiteralType::LiteralString: { const FString SourceString = ReadString(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal text - literal string: \"%s\""), *Indents, (int32)Opcode, *SourceString); } break; default: checkf(false, TEXT("Unknown EBlueprintTextLiteralType! Please update FKismetBytecodeDisassembler::ProcessCommon to handle this type of text.")); break; } break; } case EX_ObjectConst: { UObject* Pointer = ReadPointer<UObject>(ScriptIndex); Ar.Logf(TEXT("%s $%X: EX_ObjectConst (%p:%s)"), *Indents, (int32)Opcode, Pointer, *Pointer->GetFullName()); break; } case EX_AssetConst: { Ar.Logf(TEXT("%s $%X: EX_AssetConst"), *Indents, (int32)Opcode); SerializeExpr(ScriptIndex); break; } case EX_NameConst: { FString ConstValue = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal name %s"), *Indents, (int32)Opcode, *ConstValue); break; } case EX_RotationConst: { float Pitch = ReadFLOAT(ScriptIndex); float Yaw = ReadFLOAT(ScriptIndex); float Roll = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal rotation (%f,%f,%f)"), *Indents, (int32)Opcode, Pitch, Yaw, Roll); break; } case EX_VectorConst: { float X = ReadFLOAT(ScriptIndex); float Y = ReadFLOAT(ScriptIndex); float Z = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal vector (%f,%f,%f)"), *Indents, (int32)Opcode, X, Y, Z); break; } case EX_TransformConst: { float RotX = ReadFLOAT(ScriptIndex); float RotY = ReadFLOAT(ScriptIndex); float RotZ = ReadFLOAT(ScriptIndex); float RotW = ReadFLOAT(ScriptIndex); float TransX = ReadFLOAT(ScriptIndex); float TransY = ReadFLOAT(ScriptIndex); float TransZ = ReadFLOAT(ScriptIndex); float ScaleX = ReadFLOAT(ScriptIndex); float ScaleY = ReadFLOAT(ScriptIndex); float ScaleZ = ReadFLOAT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal transform R(%f,%f,%f,%f) T(%f,%f,%f) S(%f,%f,%f)"), *Indents, (int32)Opcode, TransX, TransY, TransZ, RotX, RotY, RotZ, RotW, ScaleX, ScaleY, ScaleZ); break; } case EX_StructConst: { UScriptStruct* Struct = ReadPointer<UScriptStruct>(ScriptIndex); int32 SerializedSize = ReadINT(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal struct %s (serialized size: %d)"), *Indents, (int32)Opcode, *Struct->GetName(), SerializedSize); while( SerializeExpr(ScriptIndex) != EX_EndStructConst ) { // struct contents } break; } case EX_SetArray: { Ar.Logf(TEXT("%s $%X: set array"), *Indents, (int32)Opcode); SerializeExpr(ScriptIndex); while( SerializeExpr(ScriptIndex) != EX_EndArray) { // Array contents } break; } case EX_ArrayConst: { UProperty* InnerProp = ReadPointer<UProperty>(ScriptIndex); int32 Num = ReadINT(ScriptIndex); Ar.Logf(TEXT("%s $%X: set array const - elements number: %d, inner property: %s"), *Indents, (int32)Opcode, Num, *GetNameSafe(InnerProp)); while (SerializeExpr(ScriptIndex) != EX_EndArrayConst) { // Array contents } break; } case EX_ByteConst: { uint8 ConstValue = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal byte %d"), *Indents, (int32)Opcode, ConstValue); break; } case EX_IntConstByte: { int32 ConstValue = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: literal int %d"), *Indents, (int32)Opcode, ConstValue); break; } case EX_MetaCast: { UClass* Class = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: MetaCast to %s of expr:"), *Indents, (int32)Opcode, *Class->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_DynamicCast: { UClass* Class = ReadPointer<UClass>(ScriptIndex); Ar.Logf(TEXT("%s $%X: DynamicCast to %s of expr:"), *Indents, (int32)Opcode, *Class->GetName()); SerializeExpr( ScriptIndex ); break; } case EX_JumpIfNot: { // Code offset. CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: Jump to offset 0x%X if not expr:"), *Indents, (int32)Opcode, SkipCount); // Boolean expr. SerializeExpr( ScriptIndex ); break; } case EX_Assert: { uint16 LineNumber = ReadWORD(ScriptIndex); uint8 InDebugMode = ReadBYTE(ScriptIndex); Ar.Logf(TEXT("%s $%X: assert at line %d, in debug mode = %d with expr:"), *Indents, (int32)Opcode, LineNumber, InDebugMode); SerializeExpr( ScriptIndex ); // Assert expr. break; } case EX_Skip: { CodeSkipSizeType W = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: possibly skip 0x%X bytes of expr:"), *Indents, (int32)Opcode, W); // Expression to possibly skip. SerializeExpr( ScriptIndex ); break; } case EX_InstanceDelegate: { // the name of the function assigned to the delegate. FString FuncName = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: instance delegate function named %s"), *Indents, (int32)Opcode, *FuncName); break; } case EX_AddMulticastDelegate: { Ar.Logf(TEXT("%s $%X: Add MC delegate"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); SerializeExpr( ScriptIndex ); break; } case EX_RemoveMulticastDelegate: { Ar.Logf(TEXT("%s $%X: Remove MC delegate"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); SerializeExpr( ScriptIndex ); break; } case EX_ClearMulticastDelegate: { Ar.Logf(TEXT("%s $%X: Clear MC delegate"), *Indents, (int32)Opcode); SerializeExpr( ScriptIndex ); break; } case EX_BindDelegate: { // the name of the function assigned to the delegate. FString FuncName = ReadName(ScriptIndex); Ar.Logf(TEXT("%s $%X: BindDelegate '%s' "), *Indents, (int32)Opcode, *FuncName); Ar.Logf(TEXT("%s Delegate:"), *Indents); SerializeExpr( ScriptIndex ); Ar.Logf(TEXT("%s Object:"), *Indents); SerializeExpr( ScriptIndex ); break; } case EX_PushExecutionFlow: { CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: FlowStack.Push(0x%X);"), *Indents, (int32)Opcode, SkipCount); break; } case EX_PopExecutionFlow: { Ar.Logf(TEXT("%s $%X: if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! }"), *Indents, (int32)Opcode); break; } case EX_PopExecutionFlowIfNot: { Ar.Logf(TEXT("%s $%X: if (!condition) { if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! } }"), *Indents, (int32)Opcode); // Boolean expr. SerializeExpr( ScriptIndex ); break; } case EX_Breakpoint: { Ar.Logf(TEXT("%s $%X: <<< BREAKPOINT >>>"), *Indents, (int32)Opcode); break; } case EX_WireTracepoint: { Ar.Logf(TEXT("%s $%X: .. wire debug site .."), *Indents, (int32)Opcode); break; } case EX_InstrumentationEvent: { const uint8 EventType = ReadBYTE(ScriptIndex); switch (EventType) { case EScriptInstrumentation::InlineEvent: Ar.Logf(TEXT("%s $%X: .. instrumented inline event .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::Stop: Ar.Logf(TEXT("%s $%X: .. instrumented event stop .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::PureNodeEntry: Ar.Logf(TEXT("%s $%X: .. instrumented pure node entry site .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::NodeDebugSite: Ar.Logf(TEXT("%s $%X: .. instrumented debug site .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::NodeEntry: Ar.Logf(TEXT("%s $%X: .. instrumented wire entry site .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::NodeExit: Ar.Logf(TEXT("%s $%X: .. instrumented wire exit site .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::PushState: Ar.Logf(TEXT("%s $%X: .. push execution state .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::RestoreState: Ar.Logf(TEXT("%s $%X: .. restore execution state .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::ResetState: Ar.Logf(TEXT("%s $%X: .. reset execution state .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::SuspendState: Ar.Logf(TEXT("%s $%X: .. suspend execution state .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::PopState: Ar.Logf(TEXT("%s $%X: .. pop execution state .."), *Indents, (int32)Opcode); break; case EScriptInstrumentation::TunnelEndOfThread: Ar.Logf(TEXT("%s $%X: .. tunnel end of thread .."), *Indents, (int32)Opcode); break; } break; } case EX_Tracepoint: { Ar.Logf(TEXT("%s $%X: .. debug site .."), *Indents, (int32)Opcode); break; } case EX_SwitchValue: { const auto NumCases = ReadWORD(ScriptIndex); const auto AfterSkip = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s $%X: Switch Value %d cases, end in 0x%X"), *Indents, (int32)Opcode, NumCases, AfterSkip); AddIndent(); Ar.Logf(TEXT("%s Index:"), *Indents); SerializeExpr(ScriptIndex); for (uint16 CaseIndex = 0; CaseIndex < NumCases; ++CaseIndex) { Ar.Logf(TEXT("%s [%d] Case Index (label: 0x%X):"), *Indents, CaseIndex, ScriptIndex); SerializeExpr(ScriptIndex); // case index value term const auto OffsetToNextCase = ReadSkipCount(ScriptIndex); Ar.Logf(TEXT("%s [%d] Offset to the next case: 0x%X"), *Indents, CaseIndex, OffsetToNextCase); Ar.Logf(TEXT("%s [%d] Case Result:"), *Indents, CaseIndex); SerializeExpr(ScriptIndex); // case term } Ar.Logf(TEXT("%s Default result (label: 0x%X):"), *Indents, ScriptIndex); SerializeExpr(ScriptIndex); Ar.Logf(TEXT("%s (label: 0x%X)"), *Indents, ScriptIndex); DropIndent(); break; } case EX_ArrayGetByRef: { Ar.Logf(TEXT("%s $%X: Array Get-by-Ref Index"), *Indents, (int32)Opcode); AddIndent(); SerializeExpr(ScriptIndex); SerializeExpr(ScriptIndex); DropIndent(); break; } default: { // This should never occur. UE_LOG(LogScriptDisassembler, Warning, TEXT("Unknown bytecode 0x%02X; ignoring it"), (uint8)Opcode ); break; } } }