Ideas for Optimizing this RPC?

I have created a custom Movement system for myHovering Pawns, which uses both the engines default ‘Replicated Movement’ struct to ensure position information is the same on all clients, and it also replicates the Input from the keyboard given to the Pawn as well. I need the Input given to the pawn, to drive various visual and audible effects such as Engine sounds, thruster visualization and animation.

Pawns not controlled by the client receive their input data from the server via ‘ReplicatedInputStates’ - No different to how WheeledMovementComponent does it in the engine. If the Pawn is Locally Controlled, it performs a Server RPC (On Tick) which sends the Input from the keyboard to the server. The input is then replicated to everybody from the server. This is however, pretty expensive and the amount of RPC’s is far too high. Input is simulated locally first, and updated when it receives the ReplicatedInput from the Server.

The RPC sends a struct of the following data:

float ThrustValue
float StrafeValue
float SteerValue
float PitchValue
bool bIsJumping

Both ThrustValue and StrafeValue can only ever be in the range of -1.f to 1.f, and they only really need one decimal place of precision. Steer Value comes from the MouseX input and can therefore range quite significantly, whereas PitchValue can be any arbitrary value. Again though, both only require maybe 1-2 decimal places of precision.

I can quite easily setup a Timer that reduces the amount of RPC’s sent over the network. 10-Per Second would be more than enough. However, I feel as though there is more than enough potential to heavily optimized the replicated input states, since I don’t need the full floating-point precision.

How can I pack or Quantize these floats for more optimized RPC’s? Alternatively, is there a way I can handle this without using RPC’s at all, since RPC calls are expensive and the players WILL spend the majority of their time in possession of one of these craft.

Here’s some code, if it helps:

void ABZGame_HoverCraft::UpdateState(float DeltaTime) // Called On Tick. // TODO: Remove DeltaTime Pass-In
	/* If we're the local object, we need to update the state of the input on the server. */
	if (IsLocallyControlled())
		/* Send Input States to Server. */
		// TODO - This is currently an RPC every tick :O - need to find a more optimized way of doing this. (e.g., timer)
		/* Use Replicated Values For Remote Pawns */
		InputStates = ReplicatedInputStates;

void ABZGame_HoverCraft::ServerUpdateState_Implementation(FHoverMovementStates InInputStates)
	InputStates = InInputStates;
	ReplicatedInputStates = InInputStates;

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

	DOREPLIFETIME(ABZGame_HoverCraft, ReplicatedInputStates);

If you’re only using 3 - 4 digits for each float could you pack them into one int32 with each set of four digits in the int representing one value? Packing and unpacking it would be fairly trivial and only need to be done on the client end. I’ve admittedly never done anything like that myself but it sounds like it might work :slight_smile:

I don’t know whether client to server communication without RPCs is possible, but some ideas come to mind that could improve your network traffic.

  • One idea is to only send a client-to-server RPC when input changes. So you would store a second struct containing the last frame’s values and compare them with the latest values to determine whether sending an RPC is needed.
  • Maybe experiment with sending the server only the key presses (= boolean values) and letting the server do the calculations? Meanwhile, let the client simulate the local player’s input as well, but any incoming server info is seen as the true state. In my experience this approach works quite well with acceleration based movement, but when working with instant velocity changes its hard to mask the lag from having the info go from client->server->client.

You could try combining the ideas. If the user is pressing A to turn left and keeps holding that button, theoretically the server just needs one notification when he starts pressing it and one when he releases it.

Yeah I thought about doing Key-Presses, the only problem is that if the user ever uses a Joystick or a Gamepad the movement would suffer since they wouldn’t have much control over their speeds. Checking for changed data wouldn’t be too difficult, since the Replicated Struct is used to update the second non-replicated local copy of the struct anyway.

Only sending the data when the movement changes isn’t a bad idea too. I think a combination of packing the data, sending only when data has changed and basing the sending off of a timer will help a lot.

Packing into a uInt32 might work well, I’m going to check out how FVector_NetQuantize works and report back on that… In theory none of the values will ever exceed double-digits before the decimal place (unless that person has an insanely sensitive mouse…), and one DP of precision should do just fine.

I could probably make the RPC unreliable as well, since it’s only used to update visual effects, and occasionally check that the Client isn’t cheating by giving their craft insane amounts of input.