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(); } }
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(); }
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(); } }