NetworkManager.gd
extends Node
const TICK_RATE = 60 # Server ticks per second
const MS_PER_TICK = 1000 / TICK_RATE
const INTERPOLATION_OFFSET = 100 # ms
var world_state_buffer =
var last_world_state = {}
var server_clock = 0.0
class WorldState:
var timestamp: float
var player_states: Dictionary
func _init(ts: float):
timestamp = ts
player_states = {}
func _physics_process(delta: float):
if multiplayer.is_server():
server_clock += delta
process_server_tick()
func process_server_tick():
var world_state = WorldState.new(server_clock)
# Collect all player states
for player in get_tree().get_nodes_in_group("players"):
world_state.player_states[player.name] = {
"position": player.global_position,
"velocity": player.velocity,
# Add other relevant state
}
# Store state and broadcast to clients
world_state_buffer.append(world_state)
broadcast_world_state.rpc(world_state.timestamp, world_state.player_states)
# Cleanup old states
while world_state_buffer.size() > 1000: # Keep last ~16 seconds
world_state_buffer.pop_front()
@rpc(“authority”, “call_remote”, “reliable”)
func broadcast_world_state(timestamp: float, state: Dictionary):
last_world_state = state
# Interpolate positions
for player_id in state:
var player = get_node_or_null(str(player_id))
if player and player.name != str(multiplayer.get_unique_id()):
var target_pos = state[player_id]["position"]
# Implement interpolation logic here
player.global_position = lerp(player.global_position, target_pos, 0.3)
Client-side input handling
func send_player_input(input_vector: Vector2, actions: Dictionary):
if !multiplayer.is_server():
var timestamp = Time.get_ticks_msec()
process_input.rpc_id(1, timestamp, input_vector, actions)
@rpc(“any_peer”, “call_local”, “reliable”)
func process_input(timestamp: int, input_vector: Vector2, actions: Dictionary):
if !multiplayer.is_server(): return
var player = get_node_or_null(str(multiplayer.get_remote_sender_id()))
if !player: return
# Validate input
if !validate_input(input_vector, actions):
return
# Process movement with server-side validation
player.process_movement(input_vector)
# Handle actions
for action in actions:
if actions[action]:
player.handle_action(action)
func validate_input(input_vector: Vector2, actions: Dictionary) → bool:
# Implement validation logic
if input_vector.length() > 1.0:
return false
return true