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