Replication: StaticMeshComponent->SetStaticMesh()

The static mesh of a UStaticMeshComponent is swapped out using SetStaticMesh()

If the player is the server, then all clients will see this just fine. If the player is a client, only they will see this.

I understand that with replicating values like Score, then it’s standard procedure to do something like:



void UMyClass::UpdateScore(int Points)
{
   if (GetRole() < Role_AUTHORITY)
   {
       ServerUpdateScore(Points);
   }
   else
   {
      Score += Points;
   }
}

void UMyClass::ServerUpdateScore(int Points)
{
   UpdateScore(Points);
}


But I’m struggling to understand how to apply that logic to SetStaticMesh() in a UStaticMeshComponent. If I try the same setup using the code below, then the players all disappear from each others’ viewports (???)



void UMyComponent::SwapMesh()
{
    if (GetOwnerRole() < Role_AUTHORITY)
    {
        ServerSwapMesh();
    }
    else
    {
        SetStaticMesh(MyStaticMeshArray[MyIndex]);
    }
}

void UMyComponent::ServerSwapMesh()
{
    SwapMesh();
}


Any ideas?

Edit: The constructor of the component has SetIsReplicated(true)

Solved. Some takeaways for posterity:
​​​​​​
Even if the component is replicated, SetStaticMesh() will not replicate. I had to make and call a function on the ACharacter that owned the component instead. Code in the UStaticMeshComponent derived class:

.h


UPROPERTY(EditDefaultsOnly)
UStaticMesh* MeshOne;

UPROPERTY(EditDefaultsOnly)
UStaticMesh* MeshTwo;

UStaticMesh* GetMeshOne();
UStaticMesh* GetMeshTwo();

.cpp


UStaticMesh* UMyComponent::GetMeshOne()
{
    return MeshOne;
}

UStaticMesh* UMyComponent::GetMeshTwo()
{
    return MeshTwo;
}

void UMyComponent::AskOwnerToSwapMeshes()
{
    AActor* MyOwner = GetOwner();

    if (MyOwner)
    {
        ABaseCharacter* MyChar = Cast<ABaseCharacter>(MyOwner);
        if (MyChar)
        {
            MyChar->SwapTheMesh(MyMeshIndex);
        }
    }
}

void UMyComponent::SwapCompMesh(UStaticMesh* MyMesh)
{
    SetStaticMesh(MyMesh);
}

Then I had to do the logic in the owning ABaseCharacter actor (an index of 0 will simply remove the mesh, which is desired)

.cpp


void ABaseCharacter::SwapTheMesh(int MyMeshIndex)
{
    if (GetLocalRole() == ROLE_Authority)
    {
        UBaseComponent* ActiveComponent = AvailableComps[CompArrayIndex];

        if (ActiveComponent)
        {
            UMyComponent* MyComponent = Cast<UMyComponent>(ActiveComponent);

            if (MyComponent)
            {
                UStaticMesh* MyNewMesh = nullptr;

                switch (MyMeshIndex)
                {
                case 0:
                {
                    MyNewMesh = nullptr;
                    break;
                }
                case 1:
                {
                    MyNewMesh = MyComponent->GetMeshOne();
                    break;
                }
                case 2:
                {
                    MyNewMesh = MyComponent->GetMeshTwo();
                    break;
                }
                }

                MyComponent->SwapCompMesh(MyNewMesh);
            }
        }
    }
    else
    {
        ServerSwapTheMesh(MyMeshIndex);
    }
}

void ABaseCharacter::ServerSwapTheMesh_Implementation(int MyMeshIndex)
{
    SwapBuilderMesh(MyMeshIndex);
}

bool ABaseCharacter::ServerSwapTheMesh_Validate(int MyMeshIndex)
{
    return true;
}

This will update the static mesh of the static mesh component in a way that replicates for all clients and server as you’d want it to.

Cheers

You seem to have jumped through a few hoops here. The Static Mesh Component, when replicated, will replicate the change of a static mesh.

Replication however is one-directional - it only ever goes from Server->Client. A Client cannot change the mesh and then propagate that to all other clients. They must ask the Server to do so via an RPC. Components can call RPC’s so long as the player owns the components actor and the component is replicated.

Aall you really need to do is the following:



class UMyStaticMeshComponent : public UStaticMeshComponent
{
    GENERATED_BODY()
public:
    void SetMeshCustom(UStaticMesh* NewMesh)
    {
        if (GetOwner()->HasAuthority())
        {
            SetStaticMesh(NewMesh);
        }
        else
        {
            Server_SwapMesh(NewMesh);
        }
    }

private:
    UFUNCTION(Server, Reliable)
    void Server_SwapMesh(UStaticMesh* NewMesh);
    void Server_SwapMesh_Implementation(UStaticMesh* NewMesh)
    {
        SetStaticMesh(NewMesh);
    }
}


The client also needs to be the “Owner” of the object which has the replicated mesh component, since you cannot call RPC’s on an object you do not own.

If you are not the owner, then you have to pass the RPC through an object you do own (e.g, the player controller, player state or pawn).

That is a lot less code than I wrote :eek: lol

You’re correct, the problem was the one-way nature of the replication. I tried to do it all within the component itself, but I was getting a very weird effect where all the **other **clients would disappear off each others’ screens. The leap to pushing it up into the owning character was a desperate measure.

Thanks for your reply though, I might try that and see if I get some better results than I was before. Cheers!

Yep, I just tried that code and it has the same effect. Calling SetStaticMesh() from within the component as the Authority will make every other player disappear off your screen and off each other’s. Bizarre.

Hmm that is odd… perhaps there’s something unique to your setup here. That code is untested but should definitely work… bizarre indeed!

It could well just be my bad aura. Happens at work all the time too :frowning: Thanks again for your time and patience though.