Server Replication Question (Spawn a Class)

Hello everyone!
I am not new to programming but to UE4 and currently have some issues understanding how the interactions between clients and the server proceed.

For my current project, as an example, I have multiple players. It is kind of an RTS game, so every player can own different types of building structures. First question: Should these buildings be pawns or actors? A player should own them but never really “possess” the building, eg look through them or move with them. In fact, they are static, but belong to a specific player can do stuff with it (other players also have to have the possibility to see it and attack it, but I think you know what I mean).
So, the main question(s) I have:

To realise building a base I thought about the following process:

1.) The player triggers an event, eg a click on the landscape, telling the game that he would like to build a base here
2.) A server request is send to the server, to validate all conditions for building a base for Player P at Position XYZ.
3.) If the conditions are valid, spawn a base-class-instance on the server and replicate it to all the other players.
4.) Later on, if a Player clicks on that base, send server request to validate if the base belongs to this player
5.) If yes, open building menue, if no, open attack menue

But here I have multiple questions.

If I send a server request, this request goes through the network to the server. Now he handles it and may spawn a base. But doesnt make that him being the owner of that base?
And when it replicates this base to all the other players, does that mean, that he triggers all other players to also spawn a base at this position?

Also, this Get Player Controller function confuses me. What does it mean? Do the player controller 0 to X represent all X-1 players on the server? If yes, how can I, the local player, determine which player I am? I know that I have a unique ID in the player State, but to obtain the player state I have just to extract it from the get player controller output…
When I spawn a class on server, I also have to give him an input, which player controller it should spawn on. But how can I determine inside a server function who send this request? I has to be possible to just create that thingy on the server with player identifier integer and duplicate that thing to every other player, 1:1…

Soo many ambiguities… I would virtually hug you to death if you could make things clearer for me a little bit. I think, I just have a big misconception about server replication and the interaction between clients and servers…

Best greetings!

/**
* Owner of this Actor, used primarily for replication (bNetUseOwnerRelevancy & bOnlyRelevantToOwner) and visibility (PrimitiveComponent bOwnerNoSee and bOnlyOwnerSee)
* @see SetOwner(), GetOwner()
/
UPROPERTY(replicated)
AActor
Owner;

The Actor class has an Owner that you can set which will be replicated. The owner can be anything, like an Actor or Pawn that represents your player. However, I don’t know if I would recommend the owner being the PlayerController (since the PlayerController is only replicated between the server and the owning player) or even the Pawn. Instead, you should have some replicated Actor represent your player and their info. A PlayerState may actually work well for this. Everyone has a replicated copy of every player’s PlayerState, and since the Owner is replicated they will be able to tell which PlayerState specifically owns the building.

GetPlayerController would only provide all the PlayerControllers to the server. For the client, as I mentioned above, they only have a copy of their own PlayerController, which is typically at the 0th index. However, I would not really rely on this method. The PlayerState’s Owner is conveniently the PlayerController.

You can get the PlayerState from your PlayerController also. You shouldn’t really need to “find” your PlayerController as it’s logic should be to just manipulate input.

As for letting the Server know who sent the request, well the server knows “who” sent it based on who owns the object the request is being made on. The Owner of a PlayerController is a UPlayer by the way. I’m not sure how to use that class though. But anyway, if the PlayerController makes a Server RPC (message), then that RPC is called on the replicated version of the PlayerController on the Server, and so the Server knows inherently which player or actor that is (and can also access that PlayerController’s PlayerState).

So basically, for what you want, the PlayerController should send a server message, which is to its replicated version on the server. The Server can then check the PlayerController’s PlayerState for things like money to build (which, by the way, you should probably use a DOREPLIFETIME_COND and COND_OwnerOnly for money in an RTS), and also use that PlayerState as the Owner of the newly built building. The building, an AActor, just has to have SetOwner called on it after creation and presumably in this case we SetOwner to PlayerState.

By the way, I didn’t really know most of this till you asked your question, so thank you for asking. You can find out a lot by searching the source code.

I hope this helps!

Thanks man, I think things are getting a bit clearer.

Actually I didnt got any notification that you answered my question and I only found it in google by clicking on my own asked question :smiley:

I think you also resolved another big question of mine: Private data. So, I can store all player data like his diplomatic relationship to other players and other important stuff “enemys” shouldnt know about (because may they are hackers) in the playerState and just mark them as COND_OwnerOnly?

Yup, that would make cheating more difficult and it would reduce bandwidth a bit since that data is only relevant to one client.

I hope I am not too outrageous if I ask you one more question. Hard to get some good answers these days :smiley:

I have a class named IngameRessources. It is only a Data-Management class handling virtual ressources of a Base like Iron, Wood, and Stone. The class is not derived of any Unreal Class, so I can’t maunally replicate it (I dont know why, but my compiler says it’s not possible, sadly enough). But what happens when I put an instance inside a PlayerState? I can’t add a Replication Condition to it, same reasons as mentioned above. Is this variable then just not replicated at all or what happens?

Aaand one last question, I promise :smiley:
If I have an actor spawned in the world and a client tells the server that he thinks that he owns that actor (on a gameplay basis). How can I implement to verify that? At the moment, I save a copy of the playerState PlayerID inside that actor-object. But couldn’t a client just manipulate his own PlayerID and pretend to be another player? Do you consider this a good/safe method or are there better, maybe even inbuilt possibilities?

Thanks for your answers :slight_smile:

You can replicate entire structs if their properties are marked as UPROPERTY().

Simplified example from ShooterGame:

/** replicated information on a hit we've taken */
USTRUCT()
struct FTakeHitInfo
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	float ActualDamage;

	/** The damage type we were hit with. */
	UPROPERTY()
	UClass* DamageTypeClass;

	UPROPERTY()
	TWeakObjectPtr<class AShipPawn> PawnInstigator;

	/** Specifies which DamageEvent below describes the damage received. */
	UPROPERTY()
	int32 DamageEventClassID;

	UPROPERTY()
	uint32 bKilled : 1;

private:

	/** A rolling counter used to ensure the struct is dirty and will replicate. */
	UPROPERTY()
	uint8 EnsureReplicationByte;

	/** Describes general damage. */
	UPROPERTY()
	FDamageEvent GeneralDamageEvent;
public:
	FTakeHitInfo()
		: ActualDamage(0)
		, DamageTypeClass(NULL)
		, PawnInstigator(NULL)
		, DamageEventClassID(0)
		, bKilled(false)
		, EnsureReplicationByte(0)
	{}

	FDamageEvent& GetDamageEvent()
	{
		if (GeneralDamageEvent.DamageTypeClass == NULL)
		{
			GeneralDamageEvent.DamageTypeClass = DamageTypeClass ? DamageTypeClass : UDamageType::StaticClass();
		}
		return GeneralDamageEvent;
	}

	void EnsureReplication()
	{
		EnsureReplicationByte++;
	}
};

class AMyPawn : public APawn
{
    ...
    UPROPERTY(Transient, ReplicatedUsing = OnRep_LastTakeHitInfo)
    struct FTakeHitInfo LastTakeHitInfo;
    ...
}


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

	DOREPLIFETIME_CONDITION(AMyPawn, LastTakeHitInfo, COND_OwnerOnly);
}

I’m replying to my comment because it was getting impossible to edit the last one. Anyway, only Actor data is replicated, so the struct must be on an actor. The PlayerState may very well be the best place to put a struct with all your OwnerOnly.