void FObjectReplicator::PostReceivedBunch() { if ( GetObject() == NULL ) { UE_LOG(LogNet, Verbose, TEXT("PostReceivedBunch: Object == NULL")); return; } // Call PostNetReceive const bool bIsServer = (OwningChannel->Connection->Driver->ServerConnection == NULL); if (!bIsServer && bHasReplicatedProperties) { PostNetReceive(); bHasReplicatedProperties = false; } // Check if PostNetReceive() destroyed Object UObject *Object = GetObject(); if (Object == NULL || Object->IsPendingKill()) { return; } // Call RepNotifies CallRepNotifies(true); if (!Object->IsPendingKill()) { Object->PostRepNotifies(); } }
FExportObjectInnerContext::FExportObjectInnerContext(TArray<UObject*>& ObjsToIgnore) { // For each object . . . for ( TObjectIterator<UObject> It ; It ; ++It ) { UObject* InnerObj = *It; if ( !InnerObj->IsPendingKill() ) { if ( !ObjsToIgnore.Contains(InnerObj) ) { UObject* OuterObj = InnerObj->GetOuter(); if ( OuterObj && !OuterObj->IsPendingKill() ) { InnerList* Inners = ObjectToInnerMap.Find( OuterObj ); if ( Inners ) { // Add object to existing inner list. Inners->Add( InnerObj ); } else { // Create a new inner list for the outer object. InnerList& InnersForOuterObject = ObjectToInnerMap.Add( OuterObj, InnerList() ); InnersForOuterObject.Add( InnerObj ); } } } } } }
FExportObjectInnerContext::FExportObjectInnerContext(TArray<UObject*>& ObjsToIgnore) { // For each object . . . for (UObject* InnerObj : TObjectRange<UObject>(RF_ClassDefaultObject | RF_PendingKill)) { if (!ObjsToIgnore.Contains(InnerObj)) { UObject* OuterObj = InnerObj->GetOuter(); if (OuterObj && !OuterObj->IsPendingKill()) { InnerList* Inners = ObjectToInnerMap.Find(OuterObj); if (Inners) { // Add object to existing inner list. Inners->Add(InnerObj); } else { // Create a new inner list for the outer object. InnerList& InnersForOuterObject = ObjectToInnerMap.Add(OuterObj, InnerList()); InnersForOuterObject.Add(InnerObj); } } } } }
bool FFrontendFilter_InUseByLoadedLevels::PassesFilter(FAssetFilterType InItem) const { bool bObjectInUse = false; if ( InItem.IsAssetLoaded() ) { UObject* Asset = InItem.GetAsset(); const bool bUnreferenced = !Asset->HasAnyMarks( OBJECTMARK_TagExp ); const bool bIndirectlyReferencedObject = Asset->HasAnyMarks( OBJECTMARK_TagImp ); const bool bRejectObject = Asset->GetOuter() == NULL || // Skip objects with null outers Asset->HasAnyFlags( RF_Transient ) || // Skip transient objects (these shouldn't show up in the CB anyway) Asset->IsPendingKill() || // Objects that will be garbage collected bUnreferenced || // Unreferenced objects bIndirectlyReferencedObject; // Indirectly referenced objects if( !bRejectObject && Asset->HasAnyFlags( RF_Public ) ) { // The object is in use bObjectInUse = true; } } return bObjectInUse; }
void FGameplayCueHandler::ClearEffects(TArray< TSharedPtr<FGameplayCueViewEffects> > &Effects) { bool RemovedSomething = false; for (TSharedPtr<FGameplayCueViewEffects> &EffectPtr : Effects) { if (!EffectPtr.IsValid()) { continue; } FGameplayCueViewEffects &Effect = *EffectPtr.Get(); if (Effect.SpawnedActor.IsValid()) { Effect.SpawnedActor->Destroy(); RemovedSomething = true; } if (Effect.AudioComponent.IsValid()) { Effect.AudioComponent->Stop(); RemovedSomething = true; } if (Effect.ParticleSystemComponent.IsValid()) { Effect.ParticleSystemComponent->DestroyComponent(); RemovedSomething = true; } } Effects.Empty(); // Cleanup flat array if (RemovedSomething) { for (int32 idx=0; idx < SpawnedObjects.Num(); ++idx) { UObject *Obj = SpawnedObjects[idx]; if (!Obj || Obj->IsPendingKill()) { SpawnedObjects.RemoveAtSwap(idx); idx--; } } } }
void FObjectReplicator::PostReceivedBunch() { // Call PostNetReceive const bool bIsServer = (OwningChannel->Connection->Driver->ServerConnection == NULL); if (!bIsServer && bHasReplicatedProperties) { PostNetReceive(); bHasReplicatedProperties = false; } // Check if PostNetReceive() destroyed Object UObject *Object = GetObject(); if (Object == NULL || Object->IsPendingKill()) { return; } RepLayout->CallRepNotifies( RepState, Object ); // Call RepNotifies if ( RepNotifies.Num() > 0 ) { for (int32 RepNotifyIdx = 0; RepNotifyIdx < RepNotifies.Num(); RepNotifyIdx++) { //UE_LOG(LogNet, Log, TEXT("Calling Object->%s with %s"), *RepNotifies(RepNotifyIdx)->RepNotifyFunc.ToString(), *RepNotifies(RepNotifyIdx)->GetName()); UProperty* RepProperty = RepNotifies[RepNotifyIdx]; UFunction* RepNotifyFunc = Object->FindFunctionChecked(RepProperty->RepNotifyFunc); if (RepNotifyFunc->NumParms == 0) { Object->ProcessEvent(RepNotifyFunc, NULL); } else if (RepNotifyFunc->NumParms == 1) { Object->ProcessEvent(RepNotifyFunc, RepProperty->ContainerPtrToValuePtr<uint8>(RepState->StaticBuffer.GetTypedData()) ); } else if (RepNotifyFunc->NumParms == 2) { // Fixme: this isn't as safe as it could be. Right now we have two types of parameters: MetaData (a TArray<uint8>) // and the last local value (pointer into the Recent[] array). // // Arrays always expect MetaData. Everything else, including structs, expect last value. // This is enforced with UHT only. If a ::NetSerialize function ever starts producing a MetaData array thats not in UArrayProperty, // we have no static way of catching this and the replication system could pass the wrong thing into ProcessEvent here. // // But this is all sort of an edge case feature anyways, so its not worth tearing things up too much over. FMemMark Mark(FMemStack::Get()); uint8* Parms = new(FMemStack::Get(),MEM_Zeroed,RepNotifyFunc->ParmsSize)uint8; TFieldIterator<UProperty> Itr(RepNotifyFunc); check(Itr); Itr->CopyCompleteValue( Itr->ContainerPtrToValuePtr<void>(Parms), RepProperty->ContainerPtrToValuePtr<uint8>(RepState->StaticBuffer.GetTypedData()) ); ++Itr; check(Itr); TArray<uint8> *NotifyMetaData = RepNotifyMetaData.Find(RepNotifies[RepNotifyIdx]); check(NotifyMetaData); Itr->CopyCompleteValue( Itr->ContainerPtrToValuePtr<void>(Parms), NotifyMetaData ); Object->ProcessEvent(RepNotifyFunc, Parms ); Mark.Pop(); } if (Object == NULL || Object->IsPendingKill()) { // script event destroyed Object break; } } } RepNotifies.Reset(); RepNotifyMetaData.Empty(); }
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; }
void UExporter::ExportObjectInner(const FExportObjectInnerContext* Context, UObject* Object, FOutputDevice& Ar, uint32 PortFlags, bool bSkipComponents) { // indent all the text in here TextIndent += 3; FExportObjectInnerContext::InnerList ObjectInners; if ( Context ) { const FExportObjectInnerContext::InnerList* Inners = Context->ObjectToInnerMap.Find( Object ); if ( Inners ) { ObjectInners = *Inners; } } else { for (TObjectIterator<UObject> It; It; ++It) { if ( It->GetOuter() == Object ) { ObjectInners.Add( *It ); } } } TArray<UObject*> Components; if (!bSkipComponents) { // first export the components Object->CollectDefaultSubobjects(Components, false); } if (!(PortFlags & PPF_SeparateDefine)) { for ( int32 ObjIndex = 0 ; ObjIndex < ObjectInners.Num() ; ++ObjIndex ) { // NOTE: We ignore inner objects that have been tagged for death UObject* Obj = ObjectInners[ObjIndex]; if ( !Obj->IsPendingKill() && !Obj->IsDefaultSubobject() && !Obj->HasAnyFlags(RF_TextExportTransient) && FCString::Stricmp(*Obj->GetClass()->GetName(), TEXT("Model")) != 0) { // export the object UExporter::ExportToOutputDevice( Context, Obj, NULL, Ar, (PortFlags & PPF_Copy) ? TEXT("Copy") : TEXT("T3D"), TextIndent, PortFlags | PPF_SeparateDeclare, false, ExportRootScope ); } } if (!bSkipComponents) { ExportComponentDefinitions(Context, Components, Ar, PortFlags | PPF_SeparateDeclare); } } if (!(PortFlags & PPF_SeparateDeclare)) { for ( int32 ObjIndex = 0 ; ObjIndex < ObjectInners.Num() ; ++ObjIndex ) { // NOTE: We ignore inner objects that have been tagged for death UObject* Obj = ObjectInners[ObjIndex]; if ( !Obj->IsPendingKill() && !Obj->IsDefaultSubobject() && !Obj->HasAnyFlags(RF_TextExportTransient) && FCString::Stricmp(*Obj->GetClass()->GetName(), TEXT("Model")) != 0) { // export the object UExporter::ExportToOutputDevice( Context, Obj, NULL, Ar, (PortFlags & PPF_Copy) ? TEXT("Copy") : TEXT("T3D"), TextIndent, PortFlags | PPF_SeparateDefine, false, ExportRootScope ); // don't reexport below in ExportProperties Obj->Mark(OBJECTMARK_TagImp); } } if (!bSkipComponents) { ExportComponentDefinitions(Context, Components, Ar, PortFlags | PPF_SeparateDefine); } // export the object's properties // Note: we use archetype as the object to diff properties against before they exported. When object is created, they should create from archetype // and using this system, it should recover all properties it needs to copy uint8 *CompareObject; if (Object->HasAnyFlags(RF_ClassDefaultObject)) { CompareObject = (uint8*)Object; } else { CompareObject = (uint8*)Object->GetArchetype(); } ExportProperties( Context, Ar, Object->GetClass(), (uint8*)Object, TextIndent, Object->GetClass(), CompareObject, Object, PortFlags, ExportRootScope ); if (!bSkipComponents) { // Export anything extra for the components. Used for instanced foliage. // This is done after the actor properties so these are set when regenerating the extra data objects. ExportComponentExtra( Context, Components, Ar, PortFlags ); } } // remove indent TextIndent -= 3; }
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 FMovieSceneSequenceInstance::PreUpdate(class IMovieScenePlayer& Player) { // Remove any stale runtime objects TMap<FGuid, FMovieSceneObjectBindingInstance>::TIterator ObjectIt = ObjectBindingInstances.CreateIterator(); for(; ObjectIt; ++ObjectIt ) { FMovieSceneObjectBindingInstance& ObjectBindingInstance = ObjectIt.Value(); for (int32 ObjectIndex = 0; ObjectIndex < ObjectBindingInstance.RuntimeObjects.Num(); ) { UObject* RuntimeObject = ObjectBindingInstance.RuntimeObjects[ObjectIndex].Get(); if (RuntimeObject == nullptr || RuntimeObject->HasAnyFlags(RF_BeginDestroyed|RF_FinishDestroyed) || RuntimeObject->IsPendingKill()) { ObjectBindingInstance.RuntimeObjects.RemoveAt(ObjectIndex); } else { ++ObjectIndex; } } } Player.GetSpawnRegister().PreUpdateSequenceInstance(*this, Player); }