x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Variable Replication Order

Is there anyway to force the replication order for variables?

I am getting a race condition at line 190 between AbilityStates and ActiveAbility.

If it hits line 190 before AbilityStates is replicated then the Phase will be Active.

If it hits it after then the phase will be Cooldown.

 // ** FPSAbilityComponent.h **
 UENUM(BlueprintType)
 enum class EFPSAbilityPhase : uint8
 {
     Ready,
     Start,
     Active,
     Cooldown,
     Disabled
 };
 
 USTRUCT(BlueprintType)
 struct FFPSAbilityState
 {
     GENERATED_BODY()
 
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AbilityState)
     EFPSAbilityPhase Phase;
 
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AbilityState)
     float Charges;
 
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AbilityState)
     float MaxCharges;
 
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AbilityState)
     float Cooldown;
 
     // Duration Remaining
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AbilityState)
     float Duration;
 };
 
 USTRUCT()
 struct FFPSActiveAbility
 {
     GENERATED_BODY()
 
     UPROPERTY()
     uint8 ID;
 
     UPROPERTY()
     FFPSAbilityAnimation Animation;
 
     UPROPERTY()
     uint8 ForceRep : 1;
 };
 
 UCLASS( ClassGroup=(Abilities), meta=(BlueprintSpawnableComponent) )
 class FPSGAME_API UFPSAbilityComponent : public UActorComponent
 {
     GENERATED_BODY()
 
 public:
 
     // Called when the game starts
     virtual void BeginPlay() override;
     
     UFUNCTION()
     void OnRep_ActiveAbility();
     
     UFUNCTION()
     void OnRep_AbilityStates();
     
     
     // Set of abilities
     UPROPERTY(Transient, Replicated, BlueprintReadOnly, Category = Abilities)
     class UFPSAbilitySet* AbilitySet;
 
     UPROPERTY(EditDefaultsOnly, Category = Abilities)
     TArray<TSubclassOf<class UFPSAbility>> DefaultAbilityClasses;
 
     UPROPERTY(Transient, Replicated)
     TArray<class UFPSAbility*> Abilities;
 
     // Replicated ability state
     UPROPERTY(Transient, VisibleAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_AbilityStates, Category = Ability)
     TArray<FFPSAbilityState> AbilityStates;
 
     UPROPERTY(Transient, ReplicatedUsing = OnRep_ActiveAbility)
     FFPSActiveAbility ActiveAbility;
 }
 
 // ** FPSAbilityComponent.cpp**
 void UFPSAbilityComponent::BeginPlay()
 {
     Super::BeginPlay();
 
     if (GetOwner() && GetOwner()->HasAuthority())
     {
         // Create the AbilitySet
         AbilitySet = NewObject<UFPSAbilitySet>(this, DefaultAbilitySetClass);
 
         for (int i = 0; i < AbilitySet->DefaultAbilityClasses.Num(); i++)
         {
             // Create the Ability and set it's Outer
             UFPSAbility* Ability = NewObject<UFPSAbility>(this, AbilitySet->DefaultAbilityClasses[i]);
             Ability->ID = i;
 
             // Add the Ability to the AbilitySet
             AbilitySet->Abilities.Add(Ability);
 
             // Create the AbilityStates
             FFPSAbilityState AbilityState;
             // Set the initial property values
             AbilityState.Charges = Ability->Charges;
             AbilityState.MaxCharges = Ability->MaxCharges;
             AbilityState.Cooldown = Ability->Cooldown;
             AbilityState.Duration = Ability->Duration;
             AbilityState.Phase = EFPSAbilityPhase::Ready;
 
             // Add the AbilityState to the Array
             AbilityStates.Add(AbilityState);
         }
     }
 }
 
 void UFPSAbilityComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
 {
     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
 
     DOREPLIFETIME(UFPSAbilityComponent, AbilityStates);
 
     // Replicate the Ability Set to everyone
     // The Ability Set is responsible for finer replication granularity
     DOREPLIFETIME(UFPSAbilityComponent, AbilitySet);
 
     // Replicate the Active Ability to everyone except owner
     // The owner already has the Abilities since they are replicated to him
     DOREPLIFETIME_CONDITION(UFPSAbilityComponent, ActiveAbility, COND_SkipOwner);
 }
 
 void UFPSAbilityComponent::StartAbility(int32 Index)
 {
     // Local simulation for owning client
     if (GetOwner() && Cast<AFPSCharacter>(GetOwner())->IsLocallyControlled())
     {
         if (AbilitySet)
         {
             if (AbilityStates.IsValidIndex(Index) && AbilityStates[Index].Phase == EFPSAbilityPhase::Ready 
                 && AbilityStates[ActiveAbility.ID].Phase != EFPSAbilityPhase::Start && AbilityStates[ActiveAbility.ID].Phase != EFPSAbilityPhase::Active)
             {
                 LocalActiveAbilityID = Index;
 
                 SimulateAbilityLocally(Index);
             }
         }
     }
 
     ServerStartAbility(Index);
 }
 
 bool UFPSAbilityComponent::ServerStartAbility_Validate(int32 Index)
 {
     return true;
 }
 
 void UFPSAbilityComponent::ServerStartAbility_Implementation(int32 Index)
 {
     if (AbilitySet && AbilitySet->Abilities.IsValidIndex(Index) && AbilitySet->Abilities[Index] != nullptr)
     {
         if (AbilityStates[Index].Phase == EFPSAbilityPhase::Ready
                 && AbilityStates[ActiveAbility.ID].Phase != EFPSAbilityPhase::Start && AbilityStates[ActiveAbility.ID].Phase != EFPSAbilityPhase::Active)
         {
             ConsumeCharge(Index);
             AbilitySet->Abilities[Index]->Start();
         }
     }
 }
 
 void UFPSAbilityComponent::SimulateAbility(int32 Index)
 {
     // Animation
     if (ActiveAbility.Animation.Pawn3P)
     {
         AFPSCharacter* Character = Cast<AFPSCharacter>(GetOwner());
         // Play the Third Person Animation
         Character->PlayAnimMontage(ActiveAbility.Animation.Pawn3P);
         // Play the First Person Animation
         Character->PlayAnimMontageFP(ActiveAbility.Animation.Pawn1P);
     }
 
     // Create the Ability since we aren't replicating to other clients (simulated proxies)
     UFPSAbility* Ability = NewObject<UFPSAbility>(this, AbilitySet->DefaultAbilityClasses[Index]);
     
     if (AbilityStates[Index].Phase == EFPSAbilityPhase::Start)
     {
         Ability->Simulate();
     }
     // ** RACE CONDITION, SOMETIMES PHASE IS ACTIVE AND SOMETIMES IT'S COOLDOWN
     else if (AbilityStates[Index].Phase == EFPSAbilityPhase::Active || AbilityStates[Index].Phase == EFPSAbilityPhase::Cooldown)
     {
         Ability->SimulateExecute();
     }
 }
 
 void UFPSAbilityComponent::SimulateAbilityLocally(int32 Index)
 {
     if (AbilitySet)
     {
         AFPSCharacter* Character = Cast<AFPSCharacter>(GetOwner());
 
         // Play the Third Person Animation
         Character->PlayAnimMontage(AbilitySet->Abilities[Index]->Animation.Pawn3P);
         // Play the First Person Animation
         Character->PlayAnimMontageFP(AbilitySet->Abilities[Index]->Animation.Pawn1P);
 
         if (AbilitySet->Abilities[Index]->bExecuteOnStart)
         {
             AbilitySet->Abilities[Index]->SimulateExecute();
         }
         else
         {
             AbilitySet->Abilities[Index]->Simulate();
         }
     }
 }
 
 void UFPSAbilityComponent::ConsumeCharge(int32 Index)
 {
     AbilityStates[Index].Charges--;
 }
 
 void UFPSAbilityComponent::OnRep_AbilityStates()
 {
 }
 
 void UFPSAbilityComponent::OnRep_ActiveAbility()
 {
     SimulateAbility(ActiveAbility.ID);
 }
 
 void UFPSAbilityComponent::SetActiveAbility(int32 AbilityID, FFPSAbilityAnimation Animation)
 {
     ActiveAbility.ID = AbilityID;
     ActiveAbility.Animation = Animation;
 }
 
 // ** FPSAbility.h **
 UCLASS(Blueprintable, BlueprintType)
 class FPSGAME_API UFPSAbility : public UObject
 {
     GENERATED_BODY()
     
 public:
     UFUNCTION(BlueprintCallable, Category = Ability)
     void Start();
 
     UFUNCTION(BlueprintCallable, Category = Ability)
     void Execute();
 
     UFUNCTION(BlueprintCallable, Category = Ability)
     void End();
 
     UFUNCTION(BlueprintImplementableEvent, Category = Ability)
     void Simulate();
 
     UFUNCTION(BlueprintImplementableEvent, Category = Ability)
     void SimulateExecute();
 
     UFUNCTION(BlueprintImplementableEvent, Category = Ability)
     void OnStart();
 
     // Blueprint implementable version of Execute
     UFUNCTION(BlueprintImplementableEvent, Category = Ability)
     void OnExecute();
 
     UFUNCTION(BlueprintImplementableEvent, Category = Ability)
     void OnEnd();
     
     // Number of charges that can be stored at a time
     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Ability)
     float Charges;
 
     // Max number of charges that a player can hold at a time (0 for infinite)
     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Ability)
     float MaxCharges;
 
     // Number of seconds before this ability can be recast (0 for none)
     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Ability)
     float Cooldown;
 
     // How charges are restored after use
     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Ability)
     EFPSAbilityChargeMode ChargeMode;
 
     // Animation to be played when this ability is executed
     UPROPERTY(EditDefaultsOnly, Category = Animation)
     FFPSAbilityAnimation Animation;
 
     UPROPERTY()
     int32 ID;
 
     // Cache the casted AbilityComponent we belong to
     UPROPERTY(BlueprintReadOnly, Category = Ability)
     class UFPSAbilityComponent* AbilityComponent;
 
     // Whether it should call Execute on Start
     UPROPERTY(EditDefaultsOnly, Category = Ability)
     bool bExecuteOnStart;
 
     UPROPERTY(EditDefaultsOnly, Category = Ability)
     float Duration;
 
 }
 
 // ** FPSAbility.cpp **
 void UFPSAbility::Start()
 {
     // Update Phase
     if (AbilityComponent)
     {
         AbilityComponent->AbilityStates[ID].Phase = EFPSAbilityPhase::Start;
     }
 
     // Instant
     if (bExecuteOnStart)
     {
         Execute();
 
         AbilityComponent->SetActiveAbility(ID, Animation);
     }
     // Animation
     else if (Animation.Pawn3P)
     {
         AbilityComponent->SetActiveAbility(ID, Animation);
         // Manually call OnRep for the Server
         AbilityComponent->OnRep_ActiveAbility();
     }
 
     // Force replication, so the same ability can be called multiple times in a row
     AbilityComponent->ActiveAbility.ForceRep = AbilityComponent->ActiveAbility.ForceRep >= 1 ? 0 : AbilityComponent->ActiveAbility.ForceRep + 1;
 
     OnStart();
 }
 
 void UFPSAbility::Execute()
 {
     // Update Phase
     if (AbilityComponent)
     {
         AbilityComponent->AbilityStates[ID].Phase = EFPSAbilityPhase::Active;
     }
 
     // Call the blueprint event
     OnExecute();
 }
 
 void UFPSAbility::End()
 {
     OnEnd();
 
     // Update phase
     if (AbilityComponent)
     {
         AbilityComponent->AbilityStates[ID].Phase = EFPSAbilityPhase::Cooldown;
     }
 }

 // ** FPSAbilitySet.cpp **
 void UFPSAbilitySet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
 {
     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
 
     // Replicate Abilities to owning client only
     DOREPLIFETIME_CONDITION(UFPSAbilitySet, Abilities, COND_OwnerOnly);
 
     // Replicate Ability Classes to other clients, so they can create the active one when needed
     DOREPLIFETIME_CONDITION(UFPSAbilitySet, DefaultAbilityClasses, COND_SkipOwner);
 }
 


Product Version: UE 4.12
Tags:
more ▼

asked Jul 25 '16 at 08:36 PM in C++ Programming

avatar image

erebel55
380 41 44 65

(comments are locked)
10|2000 characters needed characters left

1 answer: sort voted first

As I understand it, UObject property replication is reliable, but it is neither atomic nor is it in guaranteed order. To work around it, I believe you can create a struct that contains the properties that you need to have replicated atomically.

In this example, that would mean putting both AbilityStates and ActiveAbility in a new USTRUCT() struct FAbilityData, and then putting that FAbilityData as a replicated variable inside UFPSAbilityComponent.

more ▼

answered Jul 27 '16 at 02:09 AM

avatar image

Joergen.Tjernoe
36 1 2 4

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question