Voltar ao Blog

Multiplayer Desyncs: Corrigindo o Unreal Engine RPC Replication Issue que quebra seus estados

Publicado em 3 de março de 2026
Multiplayer Desyncs: Corrigindo o Unreal Engine RPC Replication Issue que quebra seus estados

Todo desenvolvedor indie de jogos multiplayer conhece o momento exato em que seu netcode o trai. Você dispara um RPC Run on Server para equipar uma arma. Os logs do servidor confirmam que a arma está equipada. O cilindro de colisão do servidor mostra você em posição de mira. Mas na tela do seu cliente? Seu personagem está apenas parado em uma pose idle padrão, completamente indiferente à mudança de estado.

Quando sua lógica de equipar armas, estados de mira, interações de inventário e sistemas de crafting param de atualizar repentinamente no cliente, o pânico se instala. Você pode descobrir que mudar o RPC para Multicast corrige magicamente os bugs visuais.

Não deixe no Multicast.

Usar Multicast para corrigir bugs de estado persistente é um paliativo que acabará destruindo a performance de rede do seu jogo e arruinando a experiência para jogadores que entrarem depois (late-joining). Neste mergulho profundo, vamos desvendar a causa raiz do temido unreal engine rpc replication issue, explicar por que seus estados de servidor ignoram seus clientes e arquitetar uma sincronização de estado server-authoritative à prova de balas usando C++.

A Armadilha do Multicast: Por que "funciona" (e por que vai arruinar seu jogo)

Quando os desenvolvedores encontram esse bug, o processo de pensamento geralmente é este:

  1. O cliente chama Server_EquipWeapon().
  2. O servidor equipa a arma.
  3. O visual do cliente não atualiza.
  4. Altera Server_EquipWeapon() para chamar Multicast_EquipWeapon().
  5. O visual do cliente atualiza! Bug corrigido, certo?

Errado. Para entender o porquê, você deve compreender a diferença fundamental entre RPCs (Remote Procedure Calls) e Property Replication.

Um RPC é um evento de rede transitório. É um grito no vazio. Se um jogador estiver dentro da network cull distance quando o Multicast disparar, ele ouvirá o grito e reproduzirá a animação de equipar.

Mas o que acontece se um jogador entrar no servidor 10 segundos depois? O que acontece se um jogador estiver a 5.000 Unreal Units de distância, entrar no alcance de relevância (relevancy range) e vir seu personagem? Como o Multicast já disparou no passado, o novo cliente nunca recebe o evento. Ele verá seu personagem segurando uma arma invisível, deslizando em pose idle enquanto atira balas pelo peito.

Multicast é para eventos transitórios e não críticos para o gameplay: um visual de explosão, um efeito sonoro ou um efeito de partículas cosmético.

Para qualquer coisa que persista ao longo do tempo — como qual arma você está segurando, se está mirando ou o que está no seu inventário — você deve usar Property Replication.

Causa Raiz: Por que quebrou de repente?

Se seus RPCs Run on Server funcionavam anteriormente e de repente quebraram em vários sistemas (armas, mira, crafting), você provavelmente é vítima de uma destas três mudanças arquitetônicas no seu projeto:

1. A ilusão do Listen Server vs. Dedicated Server

Se você estava testando anteriormente no Play-In-Editor (PIE) usando um Listen Server, o jogador host é tanto o cliente quanto o servidor. Um RPC "Run on Server" executado pelo host atualiza imediatamente o estado visual local porque o host é o servidor. Quando você finalmente muda para testes em Dedicated Server (ou testa como Cliente 2), a ilusão se quebra. O servidor atualiza sua memória isolada e o cliente fica para trás.

2. Ownership de ActorComponent quebrado

Se você refatorou recentemente sua lógica de inventário ou armas em classes UActorComponent, pode ter quebrado a cadeia de replicação. RPCs só podem ser invocados de clientes se o cliente possuir (owns) o Actor. Se o seu componente for spawnado dinamicamente e não for explicitamente atribuído a um dono via SetOwner(PlayerController), o servidor simplesmente descartará o RPC. Cobrimos esse pesadelo arquitetônico em nosso guia sobre Multiplayer Inventory Nightmares Fixing Swapped Actorcomponent Owners In Unreal Engine.

3. Ignorando o estado local

Anteriormente, seu evento de entrada no lado do cliente poderia estar definindo o booleano bIsAiming local antes de chamar o Server RPC. Se você refatorou seu código para ser puramente "Server Authoritative" (esperando o servidor ditar o estado), mas esqueceu de replicar esse estado de volta para o cliente, seu cliente esperará permanentemente por uma atualização que nunca chega.

Tutorial Passo a Passo: Arquitetando uma Replicação de Estado à Prova de Balas

Para corrigir este unreal engine rpc replication issue, devemos transitar de uma arquitetura baseada em RPC para uma State-Driven Architecture usando RepNotifies.

Aqui está como implementar corretamente um sistema de equipar armas e mira server-authoritative que atualiza o cliente perfeitamente.

Passo 1: Definir Propriedades Replicadas com RepNotifies

Em vez de confiar em um RPC para disparar animações, declaramos variáveis persistentes. Quando o servidor altera essas variáveis, o Net Driver da Unreal as sincroniza automaticamente com os clientes. Ao anexar uma função ReplicatedUsing (um RepNotify), podemos disparar as animações exatamente quando o cliente toma conhecimento da mudança de estado.

No seu arquivo de Header do Personagem (.h):

UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    AMyCharacter();

    // O estado persistente. Replicado para todos os clientes.
    UPROPERTY(ReplicatedUsing = OnRep_EquippedWeapon)
    AWeapon* EquippedWeapon;

    UPROPERTY(ReplicatedUsing = OnRep_IsAiming)
    bool bIsAiming;

    // Funções RepNotify. Executadas no cliente quando o servidor atualiza a variável.
    UFUNCTION()
    void OnRep_EquippedWeapon();

    UFUNCTION()
    void OnRep_IsAiming();

    // RPCs de Servidor para solicitar mudanças de estado
    UFUNCTION(Server, Reliable, WithValidation)
    void Server_EquipWeapon(AWeapon* NewWeapon);

    UFUNCTION(Server, Reliable, WithValidation)
    void Server_SetAiming(bool bWantsToAim);

    // Configuração principal de replicação
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};

Passo 2: Implementar os RPCs de Servidor e Regras de Replicação

No seu arquivo .cpp, você deve registrar essas variáveis em GetLifetimeReplicatedProps. Em seguida, defina os RPCs de Servidor para atualizar apenas o estado authoritative.

#include "MyCharacter.h"
#include "Net/UnrealNetwork.h"

void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    // Replicar estas variáveis para todos os clientes conectados
    DOREPLIFETIME(AMyCharacter, EquippedWeapon);
    DOREPLIFETIME(AMyCharacter, bIsAiming);
}

// --- LÓGICA DE MIRA ---

bool AMyCharacter::Server_SetAiming_Validate(bool bWantsToAim)
{
    // Anti-cheat: Garantir que o jogador pode mirar (ex: não está morto)
    return !bIsDead;
}

void AMyCharacter::Server_SetAiming_Implementation(bool bWantsToAim)
{
    bIsAiming = bWantsToAim;
    
    // CRÍTICO: RepNotifies NÃO rodam automaticamente no servidor em C++.
    // Se o servidor for um Listen Server, devemos chamar manualmente.
    if (GetNetMode() != NM_DedicatedServer)
    {
        OnRep_IsAiming();
    }
}

Passo 3: Implementar os RepNotifies para Atualizações Visuais

Aqui é onde entra sua lógica de animação, atualizações de UI e anexos de mesh. Como isso depende do estado replicado, jogadores que entrarem depois dispararão automaticamente essa lógica no momento em que seu personagem se tornar relevante para eles.

void AMyCharacter::OnRep_IsAiming()
{
    if (UAnimInstance* AnimInst = GetMesh()->GetAnimInstance())
    {
        if (UMyAnimInstance* MyAnim = Cast<UMyAnimInstance>(AnimInst))
        { 
            MyAnim->bIsAiming = bIsAiming;
        }
    }

    GetCharacterMovement()->MaxWalkSpeed = bIsAiming ? 300.f : 600.f;
}

void AMyCharacter::OnRep_EquippedWeapon()
{
    if (EquippedWeapon)
    {
        EquippedWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, FName("WeaponSocket"));
        PlayAnimMontage(EquipMontage);
    }
}

O Toque Profissional: Client-Side Prediction

Sem isso, você terá Input Latency. Com um ping de 100ms, o jogador sentirá um atraso de 200ms antes do personagem mirar. Em um shooter moderno, isso é terrível.

Solução: Client-Side Prediction. O cliente simula visualmente a mudança de estado imediatamente, enquanto pede permissão ao servidor.

void AMyCharacter::StartAiming()
{
    // 1. Predizer localmente imediatamente (Latência zero para o jogador)
    bIsAiming = true;
    OnRep_IsAiming(); 

    // 2. Avisar o servidor para oficializar
    if (!HasAuthority())
    {
        Server_SetAiming(true);
    }
}

Se o servidor discordar, o estado replicado corrigirá o cliente. Esta é a base de uma arquitetura multiplayer robusta, como discutido em The Unreal Engine Multiplayer Sync Bug Ruining Your World States And How To Fix It.

Além da Partida: Persistência do Estado do Jogador

A replicação cuida do estado durante a partida. Mas e o inventário após o jogo? Para manter o progresso, o estado deve sair do Unreal Engine para um banco de dados seguro.

Construir isso (load balancers, APIs, SSL) leva semanas. Com horizOn, esses serviços de backend vêm pré-configurados. Você pode salvar o ID da EquippedWeapon e o inventário diretamente na nuvem via SDKs nativos.

5 Melhores Práticas para Replicação na Unreal Engine

  1. Nunca use Multicast para estados persistentes: Use Replicated Properties. Reserve Multicast para efeitos visuais "fire and forget".
  2. Chame RepNotifies manualmente no servidor: Em C++, as funções OnRep_ não disparam sozinhas no servidor.
  3. Valide seus Server RPCs: Nunca confie no cliente. Use _Validate para checagens anti-cheat.
  4. Atenção à NetUpdateFrequency: Se o estado visual parecer lagar, verifique a frequência de atualização do Actor.
  5. Verifique o Ownership do componente: RPCs de componentes exigem um dono válido (PlayerController).

Pare de lutar contra o Net Driver

O sistema de replicação da Unreal é poderoso, mas implacável. Quando os estados dos seus clientes não atualizarem, resista ao Multicast. Siga o fluxo da autoridade: o cliente solicita, o servidor dita, a propriedade replica.

Pronto para o próximo nível? Esqueça a gestão de infraestrutura. Teste horizOn gratuitamente para progressão persistente e matchmaking fluido.