Calling TryActivateAbility from Player Controller (GAS)

Hi guys,

I’m having a bit of trouble with a networked RTS game; the ASC in question is on the PlayerState and I am trying to locally activate an ability used to bring up targeting and then command selected pawns (with their own ASC’s) .

When I try to call TryActivateAbility from the client’s Player Controller it fails due to:

const ENetRole NetMode = ActorInfo->AvatarActor->GetLocalRole(); // This should only come from button presses/local instigation (AI, etc). if (NetMode == ROLE_SimulatedProxy) {

I can call it using an RPC so it executes on the server, but then I can’t see the Target Actor on the client, and I think it makes more sense to keep this part of the code local (and only go to server when the pawn has actually been commanded to make the relevant checks).

I’m not sure what the standard practice is here, in short I want to:

  • Trigger a local command ability that brings up any required targeting (i.e. for buildings, spells etc)
  • Send the target data to a ‘command component’ on the player state which then triggers abilities on the pawns themselves (this part works already)

Thanks!

Hey, hello @shadyfox

I think the issue is related to having the AbilitySystemComponent on the PlayerState and not properly setting up your avatar actor. It’s totally fine to have the AbilityComponent in the PlayerState, especially if your game frequently changes pawns — but your avatar actor should always be your active pawn.

Usually, we move the AbilityComponent to the PlayerState when the pawn changes often. If the pawn stays the same throughout the game, it’s perfectly fine to keep the AbilitySystemComponent on the pawn itself.

Make sure you’re using your pawn as the AvatarActor when initializing.

Here’s a GitHub repo with a lot of useful GAS information:

Hi Demian,

Thanks for the info; in this case it is an RTS so there is no singular pawn for the PlayerState. I have managed to get it working by using the PlayerController as the AvatarActor.

Though I am now having a somewhat unrelated problem where abilities themselves are not replicating to the owning client so I can’t get info about them (whether they can be activated etc)

Thanks for the help,
Adam

Hey @shadyfox!

There is always a pawn, even if it’s a flying camera (like in an RTS) — you always have a pawn.

Is it failing to replicate the entire AbilitySystemComponent, or just the ability list?

Did you try this?

Hi Demian,

Thanks for your reply,

Well there is a pseudo pawn like the one you described yeah, but for now it works with teh avatar as the player controller itself.

I do have code like that yeah:

ATDPlayerState::ATDPlayerState() {
	// Create and initialize the Ability System Component
	AbilitySystemComponent = CreateDefaultSubobject<UTDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
	AbilitySystemComponent->SetIsReplicated(true);
	AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);

The ASC itself does replicate, and the abilities are triggerable via the server, they are just empty on the client. I’ve tried making the ASC owner the player controller and its avatar the pawn (the pawns owner is also the player controller, though its controller is an aicontroller)

Thanks!

@shadyfox, sorry im not following.

What do you mean by the abilities being just empty?

So I made this tester function on the player controller, it runs and outputs the abilities on the server/server client, but it is empty on the network client

Thanks!

@shadyfox, if you’re adding the abilities from the server side and the AbilitySystemComponent is replicating, it should work.

Are you sure that ‘GetPawnsWithAbility’ is returning the correct pawn on the client side? Maybe it’s not printing anything because it’s not returning any pawns on the client side.

Yeah I thought so too…

It is yeah, if I print string the pointer it shows Client: BP_Pawn0 etc.

In my c++ it only gets as far as “UTDPlayerCommandData: GetMostSuitablePawn CanActivateAbility 2” (i.e. it finds the valid ASC):

	for (APawn* CommandedPawn : CommandedPawns) {
		UE_LOG(LogTemp, Error, TEXT("UTDPlayerCommandData: GetMostSuitablePawn CanActivateAbility 1"));
		if (!CommandedPawn) continue;

		UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(CommandedPawn);
		if (!ASC) continue;
		UE_LOG(LogTemp, Error, TEXT("UTDPlayerCommandData: GetMostSuitablePawn CanActivateAbility 2"));

		FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromClass(GameplayAbilityClass);
		if (!Spec) continue;
		UE_LOG(LogTemp, Error, TEXT("UTDPlayerCommandData: GetMostSuitablePawn CanActivateAbility 3"));

Within Unreals GAS source code it says :

	Params.Condition = COND_ReplayOrOwner;
	DOREPLIFETIME_WITH_PARAMS_FAST(UAbilitySystemComponent, ActivatableAbilities, Params);

Which is the variable GetAllAbilities uses, so I’ve tried ensuring the pawn owner is the player controller when it spawns

I’m a bit stumped!

Thanks for your help!

@Shadyfox, I think I’m missing something, help me recap.

You have the Ability System Component on the APlayerState.
The commanded pawns are your units that you are trying to move, and those are pawns because you need an AI Controller to move them.

So far, so good.

What I’m missing is why you are trying to get the Ability System Component from the commanded pawn. Do those pawns also have their own Ability System Component? Are you requesting a PlayerState for each unit? That might be problematic.

If those units have their own Ability System Component, it makes sense why you don’t have the abilities of those units on the client side. The client is not the owner of those units, and based on the code you shared from the Ability System Component, it’s not going to replicate.

You should use your own (the client’s PlayerState) Ability System Component to perform the selection, not each individual ability of each unit. In other words, the one who has the ability to select units is the player, not each individual pawn, if that makes sense.

So the pawns do have their own ASC yeah. The player controller controls pawn selection, when the player sends a command it runs a local ability that gathers target data and then calls on the server to issue a command (Ability) to each selected pawn.

The player controller should be the owner of those pawns as they spawned like this:

bool ATDPlayerState::SpawnPlayerPawnAtLocation(APawn*& SpawnedPawn, TSubclassOf<class APawn> PawnClass, const FTransform& TransformIn, enum ESpawnActorCollisionHandlingMethod CollisionHandlingMethod) {
	if (!HasAuthority()) return false;

	if (!PawnClass) {
		UE_LOG(LogTemp, Error, TEXT("SpawnPlayerPawn: Invalid PawnClass."));
		return false;
	}

	UWorld* World = GetWorld();
	if (!World) {
		UE_LOG(LogTemp, Error, TEXT("SpawnPlayerPawn: World is null."));
		return false;
	}

	FActorSpawnParameters SpawnParameters;
	SpawnParameters.SpawnCollisionHandlingOverride = CollisionHandlingMethod;
	SpawnParameters.Owner = GetPlayerController();
	SpawnedPawn = World->SpawnActor<APawn>(PawnClass, TransformIn.GetLocation(), TransformIn.Rotator(), SpawnParameters);
	if (!SpawnedPawn) {
		UE_LOG(LogTemp, Error, TEXT("SpawnPlayerPawn: Spawned actor is not a pawn."));
		return false;
	}

	AssignPawnToPlayer(SpawnedPawn);

	return true;
}

What I need from the pawns is to see which of them can actually use the selected ability at the time of the command being issued, i.e. its not on cooldown etc.

It does seem the pawn owner might be breaking somewhere though as if I output its owner I get :

LogBlueprintUserMessages: [BP_Unit_C_0] Client 1: 
LogBlueprintUserMessages: [BP_Unit_C_1] Client 1: BP_PlayerController_C_0
LogBlueprintUserMessages: [BP_Unit_C_0] Server: BP_PlayerController_C_0
LogBlueprintUserMessages: [BP_Unit_C_1] Server: BP_PlayerController_C_1

(there’s 2 players, 1 pawn per player)

Thanks for all your help, I appreciate its a pretty complex problem!

@shadyfox

To be on same page:

The Player/Client has his OwnPawn and his Own APlayerController
Each unit is a Pawn and they also have their own AIController.

On the code you provided you are spawning the pawn for that player and setting the owner.
But if that pawn is configured as AutoPosses (because you need an AI Controller) its going to create a controller and posses.

Then is going to call Possess
And somewhere in the process is going to call PossessedBy

And then

The thing is, its ok for the posses to change the ownership and you should not change it.

Can you explain me a bit more what you are trying to do with the abilities on the CommandedPawns?. do you have a SelectAbility on each CommandedPawn? We can found another way to handle it.

Yeah sounds like we’re on the same page now, I found it changes the owner when possessed by the AIController and I’ve been trying to override the owner again but there might be a better way. It seems to be quite difficult to actually get a reference to the local player controller

Basically I need some information about the pawn’s abilities to be known locally so I can choose the most suitable pawn from the players selection when using an ability, i.e. the closest pawn to the target that can activate the ability.

So for example, the player selects a group of pawns, and selects a spell ability, I want it to choose the best pawn and then send the command to the server to actually be executed. I have build a ‘command component’ which basically allows for queuing of these abilities etc.

@shadyfox
All right, now I understand and it makes sense to me.

Unfortunately, you can’t access Ability Handles from AbilitySystemComponents that don’t belong to you, so that approach isn’t possible.

However, what you can do is use GameplayTags and configure the abilities or effects to apply those tags to the owner.

For example, if you’re using a GameplayEffect for the cooldown, you can configure it to apply a GameplayTag while it’s active. GameplayTags are replicated to all clients, so on the client side you can use that tag to check for cooldowns.

Thanks @BRGDemianLopez

Hmm its not a bad approach but I feel like tags might be too simplistic for what I’m trying to acheive, I would need to know which pawns have the ability and ultimately there will be hundreds of abilities in the game to test for.

I’m thinking of adding my own replicated structure parameter to my ASC subclass that has basic info about the abilities such as a bool on whether it can be activated or not, its current cooldown etc., and then updating it as necessary using delegate events. Do you think that’s a viable approach? I can then add some net security stuff later on (though frankly as the game is co-op I’m not sure I should worry too much about cheating).

@shadyfox

What you’re proposing should work. I’m not sure how it would behave with tons of units and abilities in terms of network performance, so try to keep it light and replicate only the necessary data.

If it becomes a problem in the future, you can always optimize or switch to using Tags or something similar. My philosophy is: first, make a game—then worry about optimization and security (not saying to do things poorly, of course).

The most important thing is to make a fun game.

Have a good day!

1 Like

@BRGDemianLopez

That’s awesome, thanks for all your help!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.