Godot Version
4.3
Question and Background
Hello again. I’m still working on the MVP (minimum viable product) of the third-person shooter I’m developing.
I’m having an issue with my jump function. At high ping/packet loss the jump will trigger multiple times as the player touches the ground.
For example, at optimal network conditions: (0-50 ping/<1% packet loss)
- The player pressed the jump input.
- If the player is on the ground, jump.
At suboptimal network conditions: (75-150 ping/>1% packet loss)
- The player pressed the jump input.
- If the player is on the ground, jump.
- Once the player model touches the ground again, they will jump without the player pressing the input. This can happen 1-2 times before the player stops automatically jumping.
While I will need to provide background on how my game’s networking system currently works, I’ll only include the functions that are relevant to the question.
Working Example Code
My game uses a dedicated client-server model. Which means the server is the definitive source of truth.
The player code and the player input code are separated. With the server having authority over the player and the client having authority over the player’s input.
Let’s looks at some code that works at high ping: The movement code.
First, the client sends their movement input and sends it to the server if authorized to.
@rpc("any_peer","call_local","reliable",0)
func client_move() -> void:
var Server_Authorized = false
if Player_ID == str(Client_Input.get_multiplayer_authority()):
server_move.rpc(Input.get_vector("move_right", "move_left", "move_back", "move_forward"), true)
Then, the server converts that input into game logic.
@rpc("any_peer","call_local","unreliable",0)
func server_move(input : Vector2, Server_Authorized : bool) -> void:
if Server_Authorized == false: return
if Client_Input.is_multiplayer_authority() == false: return
var Movement_Direction = Camera_Rotation.basis * Vector3(input.x, 0, input.y).normalized()
velocity.x = Movement_Direction.x * Movement_Speed
velocity.z = Movement_Direction.z * Movement_Speed
sync_player_position.rpc(self.global_position)
Server authorization IS NOT done via the @RPC
arguments. It is done via server sided checks that aren’t currently implemented. Plus, since the server has authority over the main player script at all times, setting the @RPC
to authority
or call_remote
is redundant and prints an error.
After the movement input is sent from the client and processed by the server RPC, it is then triggered by another RPC function that moves the player instead of a Multiplayer Sync node.
@rpc("any_peer","call_remote","unreliable_ordered",0)
func sync_player_position(pos : Vector3) -> void:
self.global_position = pos
The reason I don’t use a multiplayer sync node is because I need fine tuned control over what RPCs get sent and how. Plus, the sync node is made for Peer-to-peer, which my game is not.
My client_move
function is also called every physics frame. (This has a good chance of being changed later)
func _physics_process(delta) -> void:
client_move.rpc()
apply_gravity(delta)
move_and_slide()
Now here’s the problem code for my jump function(s)
Client script jump signal emission:
func _process(delta : float) -> void:
#Client Jump Input
if Input.is_action_just_pressed("jump") \
and is_multiplayer_authority():
Client_Jump.emit()
Player script client jump function:
@rpc("any_peer","call_local","reliable",0)
func client_jump() -> void:
var Server_Authorized = false
if Player_ID == str(Client_Input.get_multiplayer_authority()):
Server_Authorized = true
server_jump.rpc(Server_Authorized)
else:
Server_Authorized = false
return
Player script server jump function:
@rpc("any_peer","call_local","reliable",0)
func server_jump(Server_Authorized) -> void:
if Server_Authorized == false: return
if is_on_floor():
velocity.y = Jump_Force
I’m not sure why this jump issue is occurring. I’d like to know your theories and potential solutions.