Sending Data via an RPC on Timer, prevents Client changing the data locally?

In order to reduce the amount of RPC’s sent over the network in my game, I have tried to make one of my RPC’s that was previously called on Tick, called by a Timer instead. Very simply:



void ABZGame_HoverCraft::UpdateState()
{
	ServerUpdateState(InputStates);
}

bool ABZGame_HoverCraft::ServerUpdateState_Validate(FHoverMovementStates InInputStates)
{
	return true;
}

void ABZGame_HoverCraft::ServerUpdateState_Implementation(FHoverMovementStates InInputStates)
{
	ReplicatedInputStates = InInputStates;
}

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

	DOREPLIFETIME(ABZGame_HoverCraft, ReplicatedInputStates);
}


All this does, is send a struct of the Input from the Clients keyboard (InputStates), to the Server. On doing so, the Server sets the Replicated Version of that input and subsequently sends that out to ALL clients, so that all clients are on the same page. Unless the Client is Locally Controlled (I.e, it’s the client that send that data) - in which case it uses the original states from the Keyboard.

What I want to do is simulate movement both Locally and on the Server, so that clients get instant feedback and can have their position regularly checked and updated from the Server via ReplicatedMovement.

The problem I’m facing is that simply putting this RPC on a timer prevents the Client from updating that struct. While that RPC is queued up in the Timer, I can’t seem to change the Input at all - so I have to wait for the RPC timer to go to the server before I can make a change, which at the moment is every second or so. In reality, this will be about every 0.1 - 0.2 seconds, but even then, that’s not fast enough. Previously, the input RPC was sent on Tick so it wasn’t a problem, but naturally it’s eating up bandwidth and needs to change.

This is how I’m setting the Timer, this is currently run on Tick, but I will likely drop it into OnRep_Controller so that it only checks when it needs to:



void ABZGame_HoverCraft::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	/* If we're the client, we want the server to simulate our input too, and update our position / rotation via Replicated Movement based on that. */
	/* We simulate locally too, so the client gets instant feedback. 99% of the time these should be pretty much in sync - depending on latency. */
	if (IsLocallyControlled())
	{
		if (!GetWorldTimerManager().TimerExists(TH_InputRPC))
		{
			GetWorldTimerManager().SetTimer(TH_InputRPC, this, &ABZGame_HoverCraft::UpdateState, TH_InputRPCDelay, true);
		}
	}
	else if (GetWorldTimerManager().TimerExists(TH_InputRPC))
	{
		GetWorldTimerManager().ClearTimer(TH_InputRPC);
	}

	/* If we're not the local player, set the states from the replicated states so we see and hear engine stuff */
	if (!IsLocallyControlled())
	{
		InputStates = ReplicatedInputStates;
	}
}


So, any ideas why the input for the client is still so latent, even though the movement component uses InputStates and not ReplicatedInputStates?

It’s not really acceptable for me to have to send an RPC on tick so that the Client get’s instant feedback. I don’t see any reason why this wouldn’t work.

On this subject, I can’t see any RPC functions in CharacterMovementComponent nor Character that actually send the data from the Clients keyboard to the Server… How is it handled there?

EDIT: Cancel that, it looks like even in CMC they send an (unreliable) RPC on tick with the Client-side changes. That’s kind of a shame, was hoping there was a better way to do it.

I don’t see how setting the timer can prevent the client from updating the struct. What happens when you try to edit the struct’s data? Assigning new values doesn’t have an effect?

Essentially yeh, the struct is changed consistently via input from the Keyboard anyway, so when I press a key, the feedback should be instantaneous.

Unfortunately however, I have to wait for the round-trip for that input to actually kick in. That’s fine in LAN games and stuff where it’s barely noticeable, but then for net-based games anything over 10-20ms is going to start being noticeable.