feat(multiplayer): add Steam transport foundation

This commit is contained in:
Josh Creek
2026-08-21 18:52:07 +01:00
parent f2b72394de
commit 6bafdc7794
16 changed files with 402 additions and 31 deletions
+48 -15
View File
@@ -1,7 +1,7 @@
extends Node
# Autoload (project.godot [autoload] NetworkManager). Owns the ENet
# transport: hosting, joining, shutdown, and connection-state signals. Lives
# Autoload (project.godot [autoload] NetworkManager). Owns transport-neutral
# hosting, joining, shutdown, and connection-state signals. Lives
# at a fixed autoload path so RPC NodePaths never depend on which scene is
# loaded (§1.3 of multiplayer-todo.md's derived decisions).
#
@@ -52,6 +52,11 @@ signal shutting_down()
const DEFAULT_PORT := 7777
const MAX_CLIENTS := 32
const TRANSPORT_ENET := "enet"
const TRANSPORT_STEAM := "steam"
const EnetTransportScript = preload("res://scripts/enet_transport.gd")
const SteamTransportScript = preload("res://scripts/steam_transport.gd")
# Clock (task 1.8, §4.7): client pings the server once a second on the
# reliable control channel; clock_offset_ms is the min-RTT sample in a
@@ -64,7 +69,8 @@ const CLOCK_WINDOW_SEC := 5.0
var is_server := false
var is_client := false
var _peer: ENetMultiplayerPeer # keep a strong ref alongside multiplayer.multiplayer_peer
var _peer: MultiplayerPeer # keep a strong ref alongside multiplayer.multiplayer_peer
var active_transport := ""
var rtt_ms := -1.0 # min-RTT sample currently in the window; -1 = no sample yet
var clock_offset_ms := 0.0 # add to a local Time.get_ticks_msec() reading to estimate the server's clock
@@ -121,31 +127,46 @@ func poll() -> void:
multiplayer.poll()
func host(port: int = DEFAULT_PORT, max_clients: int = MAX_CLIENTS) -> Error:
func available_transports() -> PackedStringArray:
var transports := PackedStringArray([TRANSPORT_ENET])
if SteamTransportScript.new().is_available():
transports.append(TRANSPORT_STEAM)
return transports
func host(port: int = DEFAULT_PORT, max_clients: int = MAX_CLIENTS, transport: String = TRANSPORT_ENET) -> Error:
shutdown()
var peer := ENetMultiplayerPeer.new()
var err := peer.create_server(port, max_clients)
var implementation := _make_transport(transport)
if implementation == null:
return ERR_INVALID_PARAMETER
var result: Dictionary = implementation.create_server(port, max_clients)
var err := int(result.error)
if err != OK:
push_error("NetworkManager.host: create_server failed (%s)" % error_string(err))
push_error("NetworkManager.host(%s): create_server failed (%s): %s" % [transport, error_string(err), String(result.get("reason", ""))])
return err
_peer = peer
multiplayer.multiplayer_peer = peer
_peer = result.peer as MultiplayerPeer
multiplayer.multiplayer_peer = _peer
multiplayer.server_relay = false
active_transport = transport
is_server = true
is_client = false
return OK
func join(address: String, port: int = DEFAULT_PORT) -> Error:
func join(address: String, port: int = DEFAULT_PORT, transport: String = TRANSPORT_ENET) -> Error:
shutdown()
var peer := ENetMultiplayerPeer.new()
var err := peer.create_client(address, port)
var implementation := _make_transport(transport)
if implementation == null:
return ERR_INVALID_PARAMETER
var result: Dictionary = implementation.create_client(address, port)
var err := int(result.error)
if err != OK:
push_error("NetworkManager.join: create_client failed (%s)" % error_string(err))
push_error("NetworkManager.join(%s): create_client failed (%s): %s" % [transport, error_string(err), String(result.get("reason", ""))])
return err
_peer = peer
multiplayer.multiplayer_peer = peer
_peer = result.peer as MultiplayerPeer
multiplayer.multiplayer_peer = _peer
multiplayer.server_relay = false
active_transport = transport
is_server = false
is_client = true
return OK
@@ -164,6 +185,7 @@ func shutdown() -> void:
peer.close()
multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()
_peer = null
active_transport = ""
is_server = false
is_client = false
rtt_ms = -1.0
@@ -174,6 +196,17 @@ func shutdown() -> void:
_last_raw_rtt_ms = -1.0
func _make_transport(transport: String) -> NetTransport:
match transport:
TRANSPORT_ENET:
return EnetTransportScript.new()
TRANSPORT_STEAM:
return SteamTransportScript.new()
_:
push_error("NetworkManager: unknown transport '%s'" % transport)
return null
@rpc("any_peer", "call_remote", "reliable")
func _ping(client_send_ms: int) -> void:
if not multiplayer.is_server():