Download

How do I make a function replicate from client to server?

I have asked this more in depth but I have finally gotten close but just not all the way there yet so I am back to see if cough rama cough someone has some advice or can just say this is how to fix it.

So here’s where I am at
in my header file I have this function


/** shutdown mesh when picked up */
	UFUNCTION(reliable, netmulticast)
	virtual void CullAndDestroy();

and in the constructor this


/** shutdown pickup mesh */
void ACH_Pickup::CullAndDestroy_Implementation()
{
	OnPickedUpEvent();
	Destroy();
	SetLifeSpan(0.1);
	SetActorHiddenInGame(true);

	CollisionComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	CollisionComp->SetCollisionResponseToAllChannels(ECR_Ignore);
}

now if you pickup the object on the server side all clients of course see it go away but if you pick it up on the client side nothing happens EXCEPT on the client side.
feel free to offer any suggestions. also if there is a better way I am open to that as the object is a staticmeshcomponent and a boxcomponent.

Have you looked at the replication tutorial ?

yes I did so does this mean I need to make the call in the character?

Where are you calling the CullAndDestroy function from? NetMulticast functions are intended to be called on the server, wherein they will execute and then broadcast to the clients so that they also call it. Some of the code in that implementation you probably only want executing on the server/network authority (like the call to Destroy). When you call Destroy, it should try to kill that actor immediately and you shouldn’t need to also set the lifespan, hide it, etc.

Well good to know I figured that but as I was just trying to get the client to do something at all I was trying different ways to see what might actually work.’

CullAndDestroy is being called in the pickup. I did look at the wiki but that confused me more especially with


void AYourPlayerController::ServerSetSomeBool_Implementation(bool bNewSomeBool)
{
    SetRunning(bNewSomeBool, bToggle);
}

because it did not even discuss what setrunning was doing or why that was there instead of what I think it meant maybe was “setsomebool”?

I’m not familiar with that particular wiki page, though I agree it probably didn’t mean SetRunning there. That example is using a Server replicated function, which has slightly different requirements than your NetMulticast one does.

For your particular example, what are you trying to do? Have the pickup call OnPickedUpEvent() for every machine and then destroy the pickup? How are you handling the pickup part? When someone collides with the pickup? You should be able to detect the collision on the server, call your multicast to trigger visual effects, etc. (whatever you want on each machine), and then destroy the pickup on the server. You do not want the clients executing the Destroy() code. If you can specify a bit more context, I might be able to help a bit more.

For your particular example, what are you trying to do?
have the pickup add its self to the player who picked it up and then destroy it. I would like to make it respawn at a later time but I haven’t gotten that far.

Have the pickup call OnPickedUpEvent() for every machine and then destroy the pickup?
yes this is what I thought I was trying to do but I don’t think the client is reaching OnPickedUpEvent()

How are you handling the pickup part?
Right now I have an interface that traces to a boxcomponent set as root which when hit will display a canvas USE message. and also will allow you to pick it up with a key

When someone collides with the pickup? no. I used existing pickup code that was in shootergame and changed it from a particlefx to a mesh. Although now I can see that the particlefx is easier to deal with since you can just activate and deactivate it. if this is the better route I will revert back to it.

I will post the code here you probably will be able to analyze it better and see my errors

.h


UCLASS(abstract)
class ACH_Pickup : public AActor, public ICH_UseInterface
{
	GENERATED_UCLASS_BODY()

	/** check if pawn can use this pickup */
	virtual bool CanBePickedUp(class AShooterCharacter* TestPawn) const;

	/** initial setup */
	virtual void BeginPlay() OVERRIDE;

protected:

	/** collisions */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Pickup)
	TSubobjectPtr<class UBoxComponent> CollisionComp;

	/** Pickupmesh component */
	UPROPERTY(VisibleDefaultsOnly, Category = Pickup)
	TSubobjectPtr<UStaticMeshComponent> CHPickupMesh;

	/** FX of active pickup */
	UPROPERTY(EditDefaultsOnly, Category = Effects)
	UParticleSystem* ActiveFX;

	/** FX of pickup on respawn timer */
	UPROPERTY(EditDefaultsOnly, Category = Effects)
	UParticleSystem* RespawningFX;

	/** sound played when player picks it up */
	UPROPERTY(EditDefaultsOnly, Category = Effects)
	USoundCue* PickupSound;

	/** sound played on respawn */
	UPROPERTY(EditDefaultsOnly, Category = Effects)
	USoundCue* RespawnSound;

	/** how long it takes to respawn? */
	UPROPERTY(EditDefaultsOnly, Category = Pickup)
	float RespawnTime;

	/** is it ready for interactions? */
	UPROPERTY(Transient, ReplicatedUsing = OnRep_IsActive)
	uint32 bIsActive : 1;

	/* The character who has picked up this pickup */
	UPROPERTY(Transient, Replicated)
	AShooterCharacter* PickedUpBy;

	UFUNCTION()
	void OnRep_IsActive();

	/** give pickup */
	virtual void GivePickupTo(class AShooterCharacter* Pawn);

	/** handle touches */
	void PickupOnTouch(class AShooterCharacter* Pawn);

	/** show and enable pickup */
	virtual void RespawnPickup();

	/** show effects when pickup disappears */
	virtual void OnPickedUp();

	/** show effects when pickup appears */
	virtual void OnRespawned();

	/** blueprint event: pickup disappears */
	UFUNCTION(BlueprintImplementableEvent)
	virtual void OnPickedUpEvent();

	/** blueprint event: pickup appears */
	UFUNCTION(BlueprintImplementableEvent)
	virtual void OnRespawnEvent();

	// USE - DJMK
	virtual void DisplayPrompt(UCanvas* Canvas, AController * user) OVERRIDE;
	
	// USE - DJMK
	virtual void OnUsed(class AController* user);

	/** shutdown mesh when picked up - DJMK */
	UFUNCTION(reliable, netmulticast)
	virtual void CullAndDestroy();

	UPROPERTY(EditDefaultsOnly, Category = Debug)
	bool DrawDebug;
};

.cpp


#include "CH_Pickup.h"


ACH_Pickup::ACH_Pickup(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	CollisionComp = PCIP.CreateDefaultSubobject<UBoxComponent>(this, TEXT("PickupComp"));
	CollisionComp->InitBoxExtent(FVector(20, 20, 20));
	CollisionComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	CollisionComp->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
	CollisionComp->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
	RootComponent = CollisionComp;

	CHPickupMesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("PickupMesh"));
	CHPickupMesh->bAutoActivate = false;
	CHPickupMesh->AttachParent = RootComponent;

	RespawnTime = 10.0f;
	bIsActive = false;
	PickedUpBy = NULL;

	SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy);
	bReplicates = true;

	bReplicateInstigator = true;      //for projectiles, see actor description
	bReplicateMovement = true;

	DrawDebug = false;
}

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

	RespawnPickup();

	// register on pickup list (server only), don't care about unregistering (in FinishDestroy) - no streaming
	AShooterGameMode* GameMode = GetWorld()->GetAuthGameMode<AShooterGameMode>();
	if (GameMode)
	{
		GameMode->CHLevelPickups.Add(this);
	}
}

void ACH_Pickup::OnUsed(class AController* user)
{
	AShooterCharacter* picker = Cast<AShooterCharacter>(user->GetPawn());
	PickupOnTouch(Cast<AShooterCharacter>(picker));
}

bool ACH_Pickup::CanBePickedUp(class AShooterCharacter* TestPawn) const
{
	return TestPawn && TestPawn->IsAlive();
}

void ACH_Pickup::GivePickupTo(class AShooterCharacter* Pawn)
{
}

void ACH_Pickup::PickupOnTouch(class AShooterCharacter* Pawn)
{
	if (bIsActive && Pawn && Pawn->IsAlive() && !IsPendingKill())
	{
		if (CanBePickedUp(Pawn))
		{
			GivePickupTo(Pawn);
			PickedUpBy = Pawn;

			if (!IsPendingKill())
			{
				bIsActive = false;
				OnPickedUp();

				if (RespawnTime > 0.0f)
				{
					GetWorldTimerManager().SetTimer(this, &ACH_Pickup::RespawnPickup, RespawnTime, false);
				}
			}
		}
	}
}

void ACH_Pickup::RespawnPickup()
{
	bIsActive = true;
	PickedUpBy = NULL;
	OnRespawned();
}

void ACH_Pickup::OnPickedUp()
{
	if (RespawningFX)
	{
		//PickupPSC->SetTemplate(RespawningFX);
		//PickupPSC->ActivateSystem();
	}
	else
	{
		//PickupPSC->DeactivateSystem();
	}

	if (PickupSound && PickedUpBy)
	{
		UGameplayStatics::PlaySoundAttached(PickupSound, PickedUpBy->GetRootComponent());
	}
              OnPickedUpEvent();
	CullAndDestroy();
}

void ACH_Pickup::OnRespawned()
{
	const bool bJustSpawned = CreationTime <= (GetWorld()->GetTimeSeconds() + 5.0f);
	if (!bJustSpawned)
	{
		
	}

	OnRespawnEvent();
}

void ACH_Pickup::OnRep_IsActive()
{
	if (bIsActive)
	{
		OnRespawned();
	}
	else
	{
		OnPickedUp();
	}
}

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

	DOREPLIFETIME(ACH_Pickup, bIsActive);
	DOREPLIFETIME(ACH_Pickup, PickedUpBy);
}

/** shutdown pickup mesh */
void ACH_Pickup::CullAndDestroy_Implementation()
{
	Destroy();
	SetLifeSpan(0.1);
	SetActorHiddenInGame(true);

	CollisionComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	CollisionComp->SetCollisionResponseToAllChannels(ECR_Ignore);
}

/** Display Use Helper - DJMK */
void ACH_Pickup::DisplayPrompt(UCanvas* Canvas, AController* user)
{
	float x, y;
	Canvas->GetCenter(x, y);

	FVector ScreenPos = Canvas->Project(GetActorLocation());
	FString UseText = FString("<USE>");
	float SizeX, SizeY;
	Canvas->StrLen(GEngine->GetLargeFont(), UseText, SizeX, SizeY);
	Canvas->DrawText(GEngine->GetLargeFont(), UseText, ScreenPos.X - SizeX * 2, ScreenPos.Y, 4, 4);
}

Ah ok, there’s a lot going on here, so you’ll have to bear with me, because this will likely be a long response.

I’d not personally looked through the ShooterGame example’s code yet, so I had to go take a peek real quick. From what I can see, the ShooterGame pickup never actually destroys itself at all. It exists and “respawns” on a timer by hiding or showing particle effects, as you mentioned. It is definitely possible to do something similar with a mesh instead of a particle system, if you wanted. In the example, you’ll see a boolean named bIsActive, which is marked in the UPROPERTY as “ReplicatedUsing,” with a function specified. What that basically means is that anytime that variable changes value, that function specified will be automatically called on the clients when they get the updated value. If you go look at the function it’s calling, OnRep_IsActive, it’s making sure OnRespawned() and OnPickedUp() are getting called for the clients. Those functions are being called on the server manually from the functions that are directly setting the value of bIsActive. In your case, if you wanted to use a mesh instead, then inside OnPickedUp(), you could just turn collision off on your mesh component, as well as hide the component. Likewise, in OnRespawned(), you could turn on collision for the component, as well as make sure it’s visible. Minor side-point, it appears that this particular example is also allowing clients to execute some of that code that should be server only, so I definitely want to audit this file when I get into the office tomorrow…seems fishy!

Now, part of your problem is that you’re attempting to add a new interaction that is potentially triggered client-side, whereas the “touch” code for doing pickups automatically also already runs on the server. Basically your “use” action being bound to a key is slightly different and needs to be handled slightly differently. I assume you have code somewhere that is basically like “when the user presses the use key, call OnUsed() on the pickup.” The tricky bit here is that the key press could be instigated by a client player and not happen on the server at all. However, you really want the server being the one that determines that the pickup got “used.” So you do actually need a function to replicate from client to server, as you originally requested. The thing here is that NetMulticast is likely not what you want. It is available to use on actors, but it expects to be called on the server (so already not what you want), and then it will execute on the server, before being broadcast to each client. In this example, you really don’t need all of the clients to know that action happened directly, they’ll get the update from the bIsActive value changing, so the multicast is probably not the right choice. There is a type of replicated function for sending from clients to servers directly, which was in that wiki page linked earlier in the thread. That type of function is slightly more complicated in that it has some restrictions on when it can work. Simple explanation, it usually requires that it’s on a player controller or an actor owned by a player contoller. In your case, what you probably want is to put a server function on your player controller that is something like ServerUsePickup, that takes a pickup as a parameter. Then inside that function, you’d probably want to call the code that’s inside PickupOnTouch on the pickup, and I think you’ll be good to go. If you do this strategy, you shouldn’t need the CullAndDestroy() code at all, as you’d also not be destroying the pickup, just hiding/showing it as the ShooterGame example does.

Thanks for your help Billy. I’ve been banging my head on this all day and kind of came to the conclusion of what you are talking about here.

Although I understand the idea, implementing it has had me stumped as I have tried to see if I could grasp some implementation that was coded already that I could use for a roadmap to follow. Unfortunately everything in the shootergame has to deal with either running, jumping or firing the weapon and I am unsure how to go about coding it for my situation using any ideas from that.

I will attempt to get from A to B with what you have described.

again thanks

No problem! Let me know how it goes and if you get stuck somewhere and I can try to help you along. I’d try to get you some simple code snippets, but I’m home and don’t have VS2013 installed on this PC yet, so I don’t want to send you potentially non-compiling/faulty code :).

You are absolutely correct - should be SetSomeBool(bNewSomeBool);
That’s what you get for cut’n’pasting stuff at 3am.
Fixed.