void FObjectReplicator::ReceivedNak( int32 NakPacketId ) { UObject * Object = GetObject(); if ( Object != NULL && ObjectClass != NULL ) { RepLayout->ReceivedNak( RepState, NakPacketId ); for ( int32 i = Retirement.Num() - 1; i >= 0; i-- ) { ValidateRetirementHistory( Retirement[i] ); // If this is a dynamic array property, we have to look through the list of retirement records to see if we need to reset the base state FPropertyRetirement * Rec = Retirement[i].Next; // Retirement[i] is head and not actually used in this case while ( Rec != NULL ) { if ( NakPacketId > Rec->OutPacketIdRange.Last ) { // We can assume this means this record's packet was ack'd, so we can get rid of the old state check( Retirement[i].Next == Rec ); Retirement[i].Next = Rec->Next; delete Rec; Rec = Retirement[i].Next; continue; } else if ( NakPacketId >= Rec->OutPacketIdRange.First && NakPacketId <= Rec->OutPacketIdRange.Last ) { UE_LOG( LogNet, Verbose, TEXT( "Restoring Previous Base State of dynamic property Channel %d. NakId: %d (%d -%d)" ), OwningChannel->ChIndex, NakPacketId, Rec->OutPacketIdRange.First, Rec->OutPacketIdRange.Last ); // The Nack'd packet did update this property, so we need to replace the buffer in RecentDynamic // with the buffer we used to create this update (which was dropped), so that the update will be recreated on the next replicate actor if ( Rec->DynamicState.IsValid() ) { TSharedPtr<INetDeltaBaseState> & RecentState = RecentCustomDeltaState.FindChecked( i ); RecentState.Reset(); RecentState = Rec->DynamicState; } // We can get rid of the rest of the saved off base states since we will be regenerating these updates on the next replicate actor while ( Rec != NULL ) { FPropertyRetirement * DeleteNext = Rec->Next; delete Rec; Rec = DeleteNext; } // Finished Retirement[i].Next = NULL; break; } Rec = Rec->Next; } ValidateRetirementHistory( Retirement[i] ); } } }
void FObjectReplicator::PostSendBunch( FPacketIdRange & PacketRange, uint8 bReliable ) { RepLayout->PostReplicate( RepState, PacketRange, bReliable ? true : false ); for ( int32 i = 0; i < LifetimeCustomDeltaProperties.Num(); i++ ) { FPropertyRetirement & Retire = Retirement[LifetimeCustomDeltaProperties[i]]; FPropertyRetirement * Next = Retire.Next; while ( Next != NULL ) { // This is updating the dynamic properties retirement record that was created above during property replication // (we have to wait until we actually send the bunch to know the packetID, which is why we look for .First==INDEX_NONE) if ( Next->OutPacketIdRange.First == INDEX_NONE ) { Next->OutPacketIdRange = PacketRange; Next->Reliable = bReliable; // Mark the last time on this retirement slot that a property actually changed Retire.OutPacketIdRange = PacketRange; Retire.Reliable = bReliable; } Next = Next->Next; } ValidateRetirementHistory( Retire ); } }
void FObjectReplicator::StopReplicating( class UActorChannel * InActorChannel ) { check( OwningChannel != NULL ); check( OwningChannel == InActorChannel ); OwningChannel = NULL; // Cleanup retirement records for ( int32 i = Retirement.Num() - 1; i >= 0; i-- ) { ValidateRetirementHistory( Retirement[i] ); FPropertyRetirement * Rec = Retirement[i].Next; Retirement[i].Next = NULL; // We dont need to explicitly delete Retirement[i], but anything in the Next chain needs to be. while ( Rec != NULL ) { FPropertyRetirement * Next = Rec->Next; delete Rec; Rec = Next; } } Retirement.Empty(); if ( RemoteFunctions != NULL ) { delete RemoteFunctions; RemoteFunctions = NULL; } }
void FObjectReplicator::ReplicateCustomDeltaProperties( FOutBunch & Bunch, int32 & LastIndex, bool & bContentBlockWritten ) { if ( LifetimeCustomDeltaProperties.Num() == 0 ) { // No custom properties return; } UObject * Object = GetObject(); check( Object ); check( OwningChannel ); UNetConnection * OwningChannelConnection = OwningChannel->Connection; // Replicate those properties. for ( int32 i = 0; i < LifetimeCustomDeltaProperties.Num(); i++ ) { // Get info. const int32 RetireIndex = LifetimeCustomDeltaProperties[i]; FPropertyRetirement & Retire = Retirement[RetireIndex]; FRepRecord * Rep = &ObjectClass->ClassReps[RetireIndex]; UProperty * It = Rep->Property; int32 Index = Rep->Index; const int32 BitsWrittenBeforeThis = Bunch.GetNumBits(); // If this is a dynamic array, we do the delta here TSharedPtr<INetDeltaBaseState> & OldState = RecentCustomDeltaState.FindOrAdd( RetireIndex ); TSharedPtr<INetDeltaBaseState> NewState; // Update Retirement records with this new state so we can handle packet drops. FPropertyRetirement ** Rec = UpdateAckedRetirements( Retire, OwningChannelConnection->OutAckPacketId ); ValidateRetirementHistory( Retire ); // Our temp writer should always be in a reset state here check( TempBitWriter->GetNumBits() == 0 ); //----------------------------------------- // Do delta serialization on dynamic properties //----------------------------------------- const bool WroteSomething = SerializeCustomDeltaProperty( OwningChannelConnection, (void*)Object, It, Index, *TempBitWriter, NewState, OldState ); if ( !WroteSomething ) { continue; } check( TempBitWriter->GetNumBits() > 0 ); *Rec = new FPropertyRetirement(); // Remember what the old state was at this point in time. If we get a nak, we will need to revert back to this. (*Rec)->DynamicState = OldState; // Save NewState into the RecentCustomDeltaState array (old state is a reference into our RecentCustomDeltaState map) OldState = NewState; // Write header, and data to send to the actual bunch RepLayout->WritePropertyHeader( Object, ObjectClass, OwningChannel, It, Bunch, Index, LastIndex, bContentBlockWritten ); // Send property. Bunch.SerializeBits( TempBitWriter->GetData(), TempBitWriter->GetNumBits() ); // Reset our temp bit writer TempBitWriter->Reset(); } }
void FObjectReplicator::ReplicateCustomDeltaProperties( FOutBunch & Bunch, FReplicationFlags RepFlags, bool & bContentBlockWritten ) { if ( LifetimeCustomDeltaProperties.Num() == 0 ) { // No custom properties return; } UObject* Object = GetObject(); check( Object ); check( OwningChannel ); UNetConnection * OwningChannelConnection = OwningChannel->Connection; // Initialize a map of which conditions are valid bool ConditionMap[COND_Max]; const bool bIsInitial = RepFlags.bNetInitial ? true : false; const bool bIsOwner = RepFlags.bNetOwner ? true : false; const bool bIsSimulated = RepFlags.bNetSimulated ? true : false; const bool bIsPhysics = RepFlags.bRepPhysics ? true : false; ConditionMap[COND_None] = true; ConditionMap[COND_InitialOnly] = bIsInitial; ConditionMap[COND_OwnerOnly] = bIsOwner; ConditionMap[COND_SkipOwner] = !bIsOwner; ConditionMap[COND_SimulatedOnly] = bIsSimulated; ConditionMap[COND_AutonomousOnly] = !bIsSimulated; ConditionMap[COND_SimulatedOrPhysics] = bIsSimulated || bIsPhysics; ConditionMap[COND_InitialOrOwner] = bIsInitial || bIsOwner; ConditionMap[COND_Custom] = true; // Replicate those properties. for ( int32 i = 0; i < LifetimeCustomDeltaProperties.Num(); i++ ) { // Get info. const int32 RetireIndex = LifetimeCustomDeltaProperties[i]; FPropertyRetirement & Retire = Retirement[RetireIndex]; FRepRecord * Rep = &ObjectClass->ClassReps[RetireIndex]; UProperty * It = Rep->Property; int32 Index = Rep->Index; if (LifetimeCustomDeltaPropertyConditions.IsValidIndex(i)) { // Check the replication condition here ELifetimeCondition RepCondition = LifetimeCustomDeltaPropertyConditions[i]; check(RepCondition >= 0 && RepCondition < COND_Max); if (!ConditionMap[RepCondition]) { // We didn't pass the condition so don't replicate us continue; } } const int32 BitsWrittenBeforeThis = Bunch.GetNumBits(); // If this is a dynamic array, we do the delta here TSharedPtr<INetDeltaBaseState> & OldState = RecentCustomDeltaState.FindOrAdd( RetireIndex ); TSharedPtr<INetDeltaBaseState> NewState; // Update Retirement records with this new state so we can handle packet drops. // LastNext will be pointer to the last "Next" pointer in the list (so pointer to a pointer) FPropertyRetirement ** LastNext = UpdateAckedRetirements( Retire, OwningChannelConnection->OutAckPacketId ); check( LastNext != NULL ); check( *LastNext == NULL ); ValidateRetirementHistory( Retire ); FNetBitWriter TempBitWriter( OwningChannel->Connection->PackageMap, 0 ); //----------------------------------------- // Do delta serialization on dynamic properties //----------------------------------------- const bool WroteSomething = SerializeCustomDeltaProperty( OwningChannelConnection, (void*)Object, It, Index, TempBitWriter, NewState, OldState ); if ( !WroteSomething ) { continue; } *LastNext = new FPropertyRetirement(); // Remember what the old state was at this point in time. If we get a nak, we will need to revert back to this. (*LastNext)->DynamicState = OldState; // Save NewState into the RecentCustomDeltaState array (old state is a reference into our RecentCustomDeltaState map) OldState = NewState; // Write header, and data to send to the actual bunch RepLayout->WritePropertyHeader( Object, ObjectClass, OwningChannel, It, Bunch, Index, bContentBlockWritten ); const int NumStartingBits = Bunch.GetNumBits(); // Send property. Bunch.SerializeBits( TempBitWriter.GetData(), TempBitWriter.GetNumBits() ); NETWORK_PROFILER(GNetworkProfiler.TrackReplicateProperty(It, Bunch.GetNumBits() - NumStartingBits)); } }