test implement webrtc

This commit is contained in:
2026-01-14 18:25:07 +01:00
parent 0c0998cd05
commit f71b510cfc
9 changed files with 1091 additions and 56 deletions

View File

@@ -2,21 +2,40 @@ extends Node
# Network Manager - Handles multiplayer connections and player spawning
# Supports both hosting and joining games
# Auto-detects platform and uses appropriate networking:
# - ENet for native builds (Windows, Linux, Mac)
# - WebRTC for web builds (requires signaling server)
signal player_connected(peer_id, player_info)
signal player_disconnected(peer_id, player_info)
signal connection_failed()
signal connection_succeeded()
const DEFAULT_PORT = 7777
const DEFAULT_PORT = 21212
const MAX_PLAYERS = 8
const MATCHBOX_SERVER = "wss://matchbox.thefirstboss.com"
const STUN_SERVER = "stun:ruinborn.thefirstboss.com:3578"
var players_info = {} # Dictionary of peer_id -> {local_player_count: int, player_names: []}
var local_player_count = 1 # How many local players on this machine
var is_hosting = false
var show_room_labels = false # Show room labels when in debug mode
var network_mode: int = 0 # 0 = ENet, 1 = WebRTC, 2 = WebSocket
var room_id = "" # Room ID for Matchbox (WebRTC) or WebSocket server URL
var matchbox_client: Node = null # Matchbox client instance
const WEBSOCKET_SERVER_URL = "ws://ruinborn.thefirstboss.com:21212" # WebSocket server URL
func _ready():
# Detect if running in browser - default to WebRTC on web, ENet on native
if OS.get_name() == "Web":
network_mode = 1 # WebRTC default for web
print("NetworkManager: Detected Web platform, defaulting to WebRTC")
print("NetworkManager: Matchbox server: ", MATCHBOX_SERVER)
else:
network_mode = 0 # ENet default for native
print("NetworkManager: Using ENet by default for native platform")
print("NetworkManager: You can switch network modes in the menu")
# Connect multiplayer signals
multiplayer.peer_connected.connect(_on_peer_connected)
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
@@ -24,47 +43,221 @@ func _ready():
multiplayer.connection_failed.connect(_on_connection_failed)
multiplayer.server_disconnected.connect(_on_server_disconnected)
func host_game(port: int = DEFAULT_PORT) -> bool:
var peer = ENetMultiplayerPeer.new()
var error = peer.create_server(port, MAX_PLAYERS)
func set_network_mode(mode: int):
# 0 = ENet, 1 = WebRTC, 2 = WebSocket
# WebRTC is only available on web builds
if mode == 1 and OS.get_name() != "Web":
push_error("NetworkManager: WebRTC is not available on native platforms. WebRTC only works in web builds. Falling back to ENet.")
network_mode = 0
print("NetworkManager: ENet mode enabled (WebRTC not available on native platforms)")
return
if error != OK:
push_error("Failed to create server: " + str(error))
return false
network_mode = mode
match mode:
0:
print("NetworkManager: ENet mode enabled")
1:
print("NetworkManager: WebRTC mode enabled")
print("NetworkManager: Matchbox server: ", MATCHBOX_SERVER)
2:
print("NetworkManager: WebSocket mode enabled")
print("NetworkManager: WebSocket server: ", WEBSOCKET_SERVER_URL)
func force_webrtc_mode(enable: bool):
# Legacy function for backwards compatibility
if enable:
set_network_mode(1)
else:
set_network_mode(0)
func host_game(port: int = DEFAULT_PORT, matchbox_room: String = "") -> bool:
var peer
var error
multiplayer.multiplayer_peer = peer
is_hosting = true
# Register the host as a player
var my_id = multiplayer.get_unique_id()
players_info[my_id] = {
"local_player_count": local_player_count,
"player_names": _generate_player_names(local_player_count, my_id)
}
print("Server started on port ", port)
return true
if network_mode == 1: # WebRTC
# WebRTC for browser builds using Matchbox signaling
# Generate room ID if not provided
if matchbox_room.is_empty():
room_id = _generate_room_id()
else:
room_id = matchbox_room
print("NetworkManager: Creating WebRTC host with room ID: ", room_id)
print("NetworkManager: Share this room code with players!")
print("NetworkManager: Matchbox URL: ", MATCHBOX_SERVER, "/", room_id)
# Create Matchbox client
var matchbox_script = load("res://scripts/matchbox_client.gd")
matchbox_client = Node.new()
matchbox_client.set_script(matchbox_script)
add_child(matchbox_client)
# Connect Matchbox signals
matchbox_client.peer_joined.connect(_on_matchbox_peer_joined)
matchbox_client.peer_left.connect(_on_matchbox_peer_left)
matchbox_client.peer_connected.connect(_on_matchbox_peer_connected)
matchbox_client.connection_succeeded.connect(_on_matchbox_connected)
matchbox_client.connection_failed.connect(_on_matchbox_connection_failed)
matchbox_client.webrtc_ready.connect(_on_matchbox_webrtc_ready)
# Setup WebRTC peer (will be initialized when Matchbox connects)
# For now, we'll set it up after Matchbox connection
is_hosting = true
# Connect to Matchbox room (pass is_hosting = true)
if not matchbox_client.connect_to_room(room_id, true):
push_error("Failed to connect to Matchbox room")
return false
# Register the host as a player (peer_id 1)
players_info[1] = {
"local_player_count": local_player_count,
"player_names": _generate_player_names(local_player_count, 1)
}
return true
elif network_mode == 2: # WebSocket
# WebSocket for both native and web builds
# Generate room ID if not provided
if matchbox_room.is_empty():
room_id = _generate_room_id()
else:
room_id = matchbox_room
print("NetworkManager: Creating WebSocket host with room ID: ", room_id)
print("NetworkManager: Share this room code with players!")
print("NetworkManager: WebSocket URL: ", WEBSOCKET_SERVER_URL, "/", room_id)
peer = WebSocketMultiplayerPeer.new()
var url = WEBSOCKET_SERVER_URL + "/" + room_id
error = peer.create_server(port)
if error != OK:
push_error("Failed to create WebSocket server: " + str(error))
return false
multiplayer.multiplayer_peer = peer
is_hosting = true
print("WebSocket server started on port ", port)
print("Players can join at: ", url)
# Register the host as a player
var my_id = multiplayer.get_unique_id()
players_info[my_id] = {
"local_player_count": local_player_count,
"player_names": _generate_player_names(local_player_count, my_id)
}
return true
else: # ENet (mode 0)
# ENet for native builds
peer = ENetMultiplayerPeer.new()
error = peer.create_server(port, MAX_PLAYERS)
if error != OK:
push_error("Failed to create ENet server: " + str(error))
return false
multiplayer.multiplayer_peer = peer
is_hosting = true
print("ENet server started on port ", port)
print("Players can join at: ", get_local_ip(), ":", port)
# Register the host as a player
var my_id = multiplayer.get_unique_id()
players_info[my_id] = {
"local_player_count": local_player_count,
"player_names": _generate_player_names(local_player_count, my_id)
}
return true
func join_game(address: String, port: int = DEFAULT_PORT) -> bool:
var peer = ENetMultiplayerPeer.new()
var error = peer.create_client(address, port)
var peer
var error
if error != OK:
push_error("Failed to create client: " + str(error))
return false
multiplayer.multiplayer_peer = peer
is_hosting = false
print("Attempting to connect to ", address, ":", port)
return true
if network_mode == 1: # WebRTC
# WebRTC for browser builds using Matchbox signaling
# 'address' is the room ID for WebRTC
room_id = address
print("NetworkManager: Joining WebRTC game with room ID: ", room_id)
print("NetworkManager: Matchbox URL: ", MATCHBOX_SERVER, "/", room_id)
# Create Matchbox client
var matchbox_script = load("res://scripts/matchbox_client.gd")
matchbox_client = Node.new()
matchbox_client.set_script(matchbox_script)
add_child(matchbox_client)
# Connect Matchbox signals
matchbox_client.peer_joined.connect(_on_matchbox_peer_joined)
matchbox_client.peer_left.connect(_on_matchbox_peer_left)
matchbox_client.peer_connected.connect(_on_matchbox_peer_connected)
matchbox_client.connection_succeeded.connect(_on_matchbox_connected)
matchbox_client.connection_failed.connect(_on_matchbox_connection_failed)
matchbox_client.webrtc_ready.connect(_on_matchbox_webrtc_ready)
is_hosting = false
# Connect to Matchbox room (pass is_hosting = false)
if not matchbox_client.connect_to_room(room_id, false):
push_error("Failed to connect to Matchbox room")
return false
return true
elif network_mode == 2: # WebSocket
# WebSocket for both native and web builds
# 'address' is the room ID for WebSocket
room_id = address
print("NetworkManager: Joining WebSocket game with room ID: ", room_id)
print("NetworkManager: WebSocket URL: ", WEBSOCKET_SERVER_URL, "/", room_id)
peer = WebSocketMultiplayerPeer.new()
var url = WEBSOCKET_SERVER_URL + "/" + room_id
error = peer.create_client(url)
if error != OK:
push_error("Failed to create WebSocket client: " + str(error))
return false
multiplayer.multiplayer_peer = peer
is_hosting = false
print("Attempting to connect to WebSocket server: ", url)
return true
else: # ENet (mode 0)
# ENet for native builds
peer = ENetMultiplayerPeer.new()
error = peer.create_client(address, port)
if error != OK:
push_error("Failed to create ENet client: " + str(error))
return false
multiplayer.multiplayer_peer = peer
is_hosting = false
print("Attempting to connect to ", address, ":", port)
return true
func disconnect_from_game():
if matchbox_client:
matchbox_client.disconnect_from_room()
matchbox_client.queue_free()
matchbox_client = null
if multiplayer.multiplayer_peer:
multiplayer.multiplayer_peer.close()
multiplayer.multiplayer_peer = null
players_info.clear()
is_hosting = false
room_id = ""
func set_local_player_count(count: int):
local_player_count = max(1, min(count, 4)) # Limit to 1-4 local players
@@ -159,3 +352,140 @@ func get_all_player_ids() -> Array:
func get_player_info(peer_id: int) -> Dictionary:
return players_info.get(peer_id, {})
# Matchbox callback handlers
func _on_matchbox_connected():
print("NetworkManager: Connected to Matchbox server")
# WebRTC peer will be set up when Welcome message is received with our peer ID
func _on_matchbox_webrtc_ready():
print("NetworkManager: WebRTC mesh is ready")
# Register ourselves if we're not hosting (clients need to register)
if not is_hosting and matchbox_client:
var my_peer_id = matchbox_client.get_my_peer_id()
if my_peer_id > 0 and not players_info.has(my_peer_id):
players_info[my_peer_id] = {
"local_player_count": local_player_count,
"player_names": _generate_player_names(local_player_count, my_peer_id)
}
print("NetworkManager: Registered joining player with peer ID: ", my_peer_id)
# Emit connection_succeeded signal so the game can start
connection_succeeded.emit()
func _on_matchbox_connection_failed():
print("NetworkManager: Failed to connect to Matchbox server")
connection_failed.emit()
func _on_matchbox_peer_joined(peer_id: int):
print("NetworkManager: Matchbox peer joined: ", peer_id)
# Peer connection will be created by Matchbox client
# Once connected, we'll add it to WebRTC mesh
func _on_matchbox_peer_left(peer_id: int):
print("NetworkManager: Matchbox peer left: ", peer_id)
# Player disconnect will be handled by multiplayer signals
func _on_matchbox_peer_connected(peer_id: int):
print("NetworkManager: Matchbox peer connected: ", peer_id)
# Add peer to WebRTC mesh
if matchbox_client:
matchbox_client.add_peer_to_mesh(peer_id)
# Register player info
if not players_info.has(peer_id):
players_info[peer_id] = {
"local_player_count": 1, # Default, will be updated via RPC
"player_names": _generate_player_names(1, peer_id)
}
# Emit player connected signal
player_connected.emit(peer_id, players_info[peer_id])
func get_room_id() -> String:
return room_id
func get_local_ip() -> String:
var addresses = IP.get_local_addresses()
for addr in addresses:
# Skip localhost and IPv6
if addr.begins_with("127.") or ":" in addr:
continue
return addr
return "127.0.0.1"
func _generate_room_id() -> String:
# Generate a random 6-character room code
var chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" # Avoid confusing chars like O/0, I/1
var code = ""
for i in range(6):
code += chars[randi() % chars.length()]
return code
func get_webrtc_peer() -> WebRTCMultiplayerPeer:
if network_mode == 1 and multiplayer.multiplayer_peer is WebRTCMultiplayerPeer:
return multiplayer.multiplayer_peer as WebRTCMultiplayerPeer
return null
# Create a WebRTC peer connection with STUN server configured
func create_peer_connection() -> WebRTCPeerConnection:
var peer_connection = WebRTCPeerConnection.new()
# Configure STUN server for NAT traversal
var config = {
"iceServers": [
{
"urls": [STUN_SERVER]
}
]
}
var error = peer_connection.initialize(config)
if error != OK:
push_error("Failed to initialize WebRTC peer connection: " + str(error))
return null
print("WebRTC peer connection initialized with STUN server: ", STUN_SERVER)
return peer_connection
# Add a peer connection for WebRTC mesh networking
func add_webrtc_peer(peer_id: int) -> bool:
var webrtc = get_webrtc_peer()
if not webrtc:
push_error("Not using WebRTC")
return false
var peer_connection = create_peer_connection()
if not peer_connection:
return false
var error = webrtc.add_peer(peer_connection, peer_id)
if error != OK:
push_error("Failed to add WebRTC peer: " + str(error))
return false
print("Added WebRTC peer: ", peer_id, " with STUN server configured")
return true
# Get the peer connection info for a specific peer
func get_peer_connection(peer_id: int) -> Dictionary:
var webrtc = get_webrtc_peer()
if not webrtc:
return {}
return webrtc.get_peer(peer_id)
# Get connection info to share with remote peer (for manual signaling)
# This would typically be sent via a signaling server
# Note: With matchbox, signaling is handled automatically
func get_connection_offer(peer_id: int) -> Dictionary:
var peer_conn = get_peer_connection(peer_id)
if peer_conn.is_empty():
return {}
# In a real implementation, you'd get the SDP offer from the peer connection
# With matchbox, this is handled automatically via the signaling server
print("To implement: Get SDP offer for peer ", peer_id)
return {}