Files
splunk/splunk/networking/steam-manager.gd
2025-08-07 22:52:49 -05:00

233 lines
8.2 KiB
GDScript

extends Node
@onready var player_scene: PackedScene = preload("res://player/Player.tscn")
# -- Game State Variables --
var players: Dictionary = {} # Key: peer_id, Value: player_node
var peer_to_steam_id_map: Dictionary = {} # Key: peer_id, Value: steam_id
# -- Steam Variables --
var steam_id: int = 0
var steam_username: String = ""
var lobby_id: int = 0
var steam_initialized: bool = false
var steam_app_id: int = 480 # Spacewar App ID for testing
# -- Godot Variables --
var peer: SteamMultiplayerPeer
func _ready() -> void:
OS.set_environment("SteamAppId", str(steam_app_id))
OS.set_environment("SteamGameId", str(steam_app_id))
steam_initialized = init_steam()
if not steam_initialized:
print("!!! Steam did not initialize. Multiplayer will be disabled.")
return
# Connect Steam lobby signals
Steam.lobby_created.connect(_on_lobby_created)
Steam.lobby_joined.connect(_on_lobby_joined)
Steam.lobby_chat_update.connect(_on_lobby_chat_update)
Steam.p2p_session_request.connect(_on_p2p_session_request)
# Connect Godot multiplayer signals
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
multiplayer.connected_to_server.connect(_on_connected_to_server)
multiplayer.connection_failed.connect(_on_connection_failed)
check_command_line()
multiplayer.server_relay = true
func _process(delta):
if not steam_initialized:
return
Steam.run_callbacks()
# -----------------------------------------------------------------------------
# CORE LOGIC: STEP 1 - HOST CREATION
# -----------------------------------------------------------------------------
func create_lobby():
print("[HOST] 1. Attempting to create lobby...")
Steam.createLobby(Steam.LOBBY_TYPE_FRIENDS_ONLY, 4)
func _on_lobby_created(connect: int, this_lobby_id: int):
if connect != 1:
print("[HOST] !!! Lobby creation failed.")
return
lobby_id = this_lobby_id
print("[HOST] 2. Lobby created successfully (ID: %s)." % lobby_id)
Steam.setLobbyJoinable(lobby_id, true)
Steam.setLobbyData(lobby_id, "name", steam_username + "'s Lobby")
setup_multiplayer_peer(true)
print("[HOST] 3. Multiplayer host started (My Peer ID is always 1).")
# Register self
peer_to_steam_id_map[1] = steam_id
print("[HOST] 4. Host (Peer 1) registered in the ID map.")
# Spawn self
spawn_player.rpc(1)
# -----------------------------------------------------------------------------
# CORE LOGIC: STEP 2 - CLIENT CONNECTION
# -----------------------------------------------------------------------------
func join_lobby(this_lobby_id: int):
print("[CLIENT] 1. Attempting to join lobby (ID: %s)..." % this_lobby_id)
Steam.joinLobby(this_lobby_id)
func _on_lobby_joined(this_lobby_id: int, _p, _l, response: int):
if response != Steam.CHAT_ROOM_ENTER_RESPONSE_SUCCESS:
print("[CLIENT] !!! Failed to join lobby: %s" % get_join_fail_reason(response))
return
lobby_id = this_lobby_id
print("[CLIENT] 2. Successfully joined Steam lobby.")
if Steam.getLobbyOwner(lobby_id) != steam_id:
print("[CLIENT] 3. I am a client, creating multiplayer peer to connect to host.")
setup_multiplayer_peer(false)
func _on_connected_to_server():
print("[CLIENT] 4. Successfully connected to host's multiplayer peer.")
print("[CLIENT] 5. Sending my info to the server for registration...")
register_player_on_server.rpc_id(1, steam_id)
# -----------------------------------------------------------------------------
# CORE LOGIC: STEP 3 - SERVER REGISTRATION & SPAWNING
# -----------------------------------------------------------------------------
@rpc("any_peer")
func register_player_on_server(new_player_steam_id: int):
if not multiplayer.is_server(): return
var new_player_peer_id = multiplayer.get_remote_sender_id()
print("[SERVER] 6. Received registration request from Peer %s." % new_player_peer_id)
peer_to_steam_id_map[new_player_peer_id] = new_player_steam_id
print("[SERVER] 7. Peer %s (Steam ID: %s) added to ID map." % [new_player_peer_id, new_player_steam_id])
# --- NEW STEP ---
# Sync the completed map to the new client BEFORE spawning players for them
print("[SERVER] 8. Sending complete ID map to new client (Peer %s)." % new_player_peer_id)
sync_id_map.rpc_id(new_player_peer_id, peer_to_steam_id_map)
# Tell the new client about all players already in the game
print("[SERVER] 9. Telling Peer %s to spawn existing players..." % new_player_peer_id)
for existing_peer_id in players:
spawn_player.rpc_id(new_player_peer_id, existing_peer_id)
# Tell EVERYONE to spawn the new player
print("[SERVER] 10. Telling ALL peers to spawn the new player (Peer %s)." % new_player_peer_id)
spawn_player.rpc(new_player_peer_id)
# --- NEW FUNCTION ---
# This RPC is sent by the server to a new client to give them the ID map
@rpc("authority")
func sync_id_map(map_from_server: Dictionary):
print("-> [%s] Received ID map from server." % multiplayer.get_unique_id())
peer_to_steam_id_map = map_from_server
@rpc("any_peer", "call_local")
func spawn_player(peer_id: int):
var steam_id_of_player = peer_to_steam_id_map.get(peer_id)
if steam_id_of_player == null:
print("!!! [%s] CRITICAL: Cannot spawn player for Peer %s, not in map." % [multiplayer.get_unique_id(), peer_id])
return
if players.has(peer_id): return
var player_name = Steam.getFriendPersonaName(steam_id_of_player)
print("-> [%s] Spawning character for Peer %s (Name: %s)." % [multiplayer.get_unique_id(), peer_id, player_name])
var new_player = player_scene.instantiate() as Player
new_player.name = str(peer_id)
players[peer_id] = new_player
add_child(new_player)
new_player.position = Vector3.UP
new_player.set_player_name(player_name)
new_player.set_multiplayer_authority(peer_id, true)
new_player.setup_player()
# -----------------------------------------------------------------------------
# UTILITY AND CALLBACKS (Unchanged)
# -----------------------------------------------------------------------------
func _on_p2p_session_request(steam_id_remote: int) -> void:
print("[P2P] ==> Session request from: %s. Accepting." % steam_id_remote)
Steam.acceptP2PSessionWithUser(steam_id_remote)
func init_steam() -> bool:
var response: Dictionary = Steam.steamInitEx()
if response['status'] > 0:
print("!!! Failed to init steam! Code: %s " % response)
return false
steam_id = Steam.getSteamID()
steam_username = Steam.getPersonaName()
print("Steam initialized successfully for %s (ID: %s)." % [steam_username, steam_id])
return true
func setup_multiplayer_peer(is_host: bool = false) -> void:
if multiplayer.multiplayer_peer and multiplayer.multiplayer_peer.get_connection_status() != MultiplayerPeer.CONNECTION_DISCONNECTED:
multiplayer.multiplayer_peer.close()
peer = SteamMultiplayerPeer.new()
if is_host:
peer.create_host(0)
else:
var host_id = Steam.getLobbyOwner(lobby_id)
peer.create_client(host_id, 0)
multiplayer.multiplayer_peer = peer
func _on_peer_connected(id: int):
print("[INFO] Peer %s has established a multiplayer session." % id)
func _on_peer_disconnected(id: int):
print("[INFO] Peer %s has disconnected." % id)
if players.has(id):
players[id].queue_free()
players.erase(id)
if peer_to_steam_id_map.has(id):
peer_to_steam_id_map.erase(id)
func _on_connection_failed() -> void:
print("[CLIENT] !!! Connection to the host failed.")
func _on_lobby_chat_update(_l_id, user_changed_id: int, _u_m_c_id, chat_state: int):
var state_string = "UNKNOWN"
match chat_state:
Steam.CHAT_MEMBER_STATE_CHANGE_ENTERED: state_string = "ENTERED"
Steam.CHAT_MEMBER_STATE_CHANGE_LEFT: state_string = "LEFT"
Steam.CHAT_MEMBER_STATE_CHANGE_DISCONNECTED: state_string = "DISCONNECTED"
print("[LOBBY INFO] User %s has %s the lobby." % [user_changed_id, state_string])
func check_command_line() -> void:
var args: Array = OS.get_cmdline_args()
if args.size() > 1 and args[0] == "+connect_lobby":
if int(args[1]) > 0:
print("[CMD] Command line join request for lobby ID: %s" % args[1])
join_lobby(int(args[1]))
func get_join_fail_reason(response: int) -> String:
match response:
1: return "Doesnt Exist"
2: return "Not Allowed"
3: return "Full"
4: return "Error"
5: return "Banned"
6: return "Limited"
7: return "Clan Disabled"
8: return "Community Ban"
9: return "Member Blocked You"
10: return "You Blocked Member"
_: return "Unknown Reason"