test implement webrtc
This commit is contained in:
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user