Download

Networking question about spawning actors

I can’t seem to understand how to properly spawn actors in a network game.

Basically, I have a Pawn for each player; all the players have the same kind of pawn, with the same exact properties. Each pawn should spawn at BeginPlay() an actor, and they should be owner of that actor in order to be able to call Server functions from within the actor itself.

This is the idea, but I can’t manage to make it work. In code it looks something like this:

AUserPawn class:



 void AUserPawn::BeginPlay()
{    
    Super::BeginPlay();      

     FActorSpawnParameters spawnParams;        
     spawnParams.Owner = pOwningUser;          
    mMySecondActor = (AMySecondActor*) GetWorld()->SpawnActor<AMySecondActor>(AAMySecondActor::StaticClass, spawnParams);    
}    

 void AUserPawn::Tick(float dt)
{    
   Super::Tick(dt);          
​​​​​​​    mMySecondActor->SomeFunction();
}


AMySecondActor class




// header
UFUNCTION(Server, WithValidation, Reliable)
void ServerTest();

void SomeFunction();

// Cpp
void AMySecondActor::ServerTest()
{
​​​​​​​ UE_LOG(LogTemp, Warning, TEXT("Hello from the server"));
}

void AMySecondActor::SomeFunction()
{
​​​​​​​ AUserPawn *ownerPawn = Cast<AUserPawn>(GetOwner());
​​​​​​​}

if(ownerPawn->IsLocallyControlled() && ownerPawn->GetNetMode() == ENetMode::NM_Client)
{
​​​​​​​ ​​​​​​​ ServerTest();
}


​​​​​​​This code should print “Hello from the server” in the server at each tick, for each online client. The result is that the code is print ONLY in each client, never in the server. What am I doing wrong?

Actors are only created on the server if they are Replicated, in which case your spawn actor call should automatically be routed to the server and spawned.

  • In AUserPawn::BeginPlay(), you have to make sure you only spawn the new actor on the server side, and that both the second actor and the variable you store it in are set to replicate. Replication only works from server to clients, so if you create an actor on the client side in BeginPlay the server won’t know about it.

  • You should be implementing AMySecondActor::ServerTest_Implementation and AMySecondActor::ServerTest_Validate, AMySecondActor::ServerTest will be generated by UHT. I’m surprised that actually compiled as is.

If this worked, it would open a huge security hole for hacked clients to spawn arbitrary actors on your server. I highly doubt that is true, you have to take care of spawning it on the server yourself.

Ah, correct. Sorry, hadn’t had my coffee yet.

As Zeblote said, you can just add a “GetRole()” check in your BeginPlay and if it’s Authoritative (i.e. the server), then spawn the second actor which will replicate to all clients (assuming you have someone holding on to it and it’s not set to replicate just to owner).

Thanks guys, I’ll try that on monday!

So the code shoud look kind of like this, right?

AUserPawn class (I’m omitting the GetLifetimeReplicatedProps() override)



// In the Header
UFUNCTION(Replicated)
AMySecondActor *mMySecondActor;

// In the Cpp file
void AUserPawn::BeginPlay()
{
     Super::BeginPlay();

     if(GetRole() == ROLE_Authority)
     {
          FActorSpawnParameters spawnParams;
          spawnParams.Owner = pOwningUser;
          mMySecondActor = (AMySecondActor*) GetWorld()->SpawnActor<AMySecondActor>(AAMySecondActor::StaticClass, spawnParams);
     }
}


With this the clients will automatically have the correct reference to their own copy of mMySecondActor, to which they will be the owner of, right?

@Zeblote regarding the thing about _Implementation and _Validate, it was actually my mistake. The code I was using was a bit different, so I simplified it and I just wrote it here from memory, so I forgot to add those.