I saw another question on this part of the course and I applied another solution to prevent the host for starting the game before the other player joins in… I simply show the start button after the client joins. Don’t know if it is correct for later parts of the course, but here is what I did:
# levels/menu/menu.gd
extends Node
# reference to UI controls
@export var ui: Control
@export var level_container: Node
@export var level_scene: PackedScene
@export var ip_line_edit: LineEdit
@export var status_label: Label
# reference to the 2 HBoxContainers
@export var host_hbox: HBoxContainer
@export var not_connected_hbox: HBoxContainer
func _ready():
multiplayer.connection_failed.connect(_on_connection_failed)
multiplayer.connected_to_server.connect(_on_connected_to_server)
func _on_host_button_pressed():
# hide the not connected HBoxContainer
not_connected_hbox.hide()
Lobby.create_game()
# change the status label to "Waiting for players..."
status_label.text = "Waiting for players..."
func _on_join_button_pressed():
# hide the not connected HBoxContainer
not_connected_hbox.hide()
Lobby.join_game(ip_line_edit.text)
status_label.text = "Connecting..."
func _on_start_button_pressed():
# hide the UI on game start. Call on every client (no id specified)
hide_menu.rpc()
change_level.call_deferred(level_scene)
func change_level(scene: PackedScene):
# remove the current level if there is one. using for loop
for child in level_container.get_children():
level_container.remove_child(child)
child.queue_free()
level_container.add_child(scene.instantiate(), true)
func _on_connection_failed():
status_label.text = "Connection failed"
# better to re-enable the buttons if the connection failed
not_connected_hbox.show()
func _on_connected_to_server():
status_label.text = "Connected!"
# tell the server to change the status label when a player joins
player_joined.rpc()
# make an rpc to tell everyone that game has started and hide the UI
# The server must be the only one to tell everyone that the game has started,
# and also himself has to hide the UI
@rpc("call_local","authority","reliable")
func hide_menu():
ui.hide()
# make an rpc to tell the server to change status label when a player joins
@rpc("call_remote","any_peer","reliable")
func player_joined():
status_label.text = "Player joined!"
# show the host HBoxContainer
host_hbox.show()
Another thing I felt like was pretty to be added was the ability to close the Host if the host is tired of waiting for a player to join. I mapped this behaviour to the Esc button pression and added a destroy game function in the lobby’s
# lobby.gd
extends Node
signal player_connected(peer_id, player_info)
signal player_disconnected(peer_id)
signal server_disconnected()
const PORT = 7000
const MAX_CONNECTIONS = 2
var players = {}
var player_info = {"name": "Name Here"}
func _ready():
multiplayer.peer_connected.connect(_on_player_connected)
multiplayer.peer_disconnected.connect(_on_player_disconnected)
multiplayer.connected_to_server.connect(_on_server_connected)
multiplayer.connection_failed.connect(_on_connection_failed)
multiplayer.server_disconnected.connect(_on_server_disconnected)
func create_game():
var peer = ENetMultiplayerPeer.new()
var error = peer.create_server(PORT, MAX_CONNECTIONS)
if error != OK:
return error
multiplayer.multiplayer_peer = peer
players[1] = player_info
player_connected.emit(1, player_info)
# function to destroy the game
func destroy_game():
# refuese new connections
multiplayer.multiplayer_peer.set_refuse_new_connections(true)
# disconnect all players
multiplayer.multiplayer_peer.close()
# clear the multiplayer peer
multiplayer.multiplayer_peer = null
# clear the players dictionary
players.clear()
# emit the server_disconnected signal
server_disconnected.emit()
func join_game(ip_address):
var peer = ENetMultiplayerPeer.new()
var error = peer.create_client(ip_address, PORT)
if error != OK:
return error
multiplayer.multiplayer_peer = peer
func _on_player_disconnected(id):
players.erase(id)
player_disconnected.emit(id)
func _on_server_connected():
var peer_id = multiplayer.get_unique_id()
players[peer_id] = player_info
player_connected.emit(peer_id, player_info)
func _on_connection_failed():
multiplayer.multiplayer_peer = null
func _on_server_disconnected():
multiplayer.multiplayer_peer = null
players.clear()
server_disconnected.emit()
func _on_player_connected(id):
_register_player.rpc_id(id, player_info)
@rpc("any_peer", "reliable")
func _register_player(new_player_info):
var new_player_id = multiplayer.get_remote_sender_id()
players[new_player_id] = new_player_info
player_connected.emit(new_player_id, new_player_info)
and then the listener in the menu
# levels/menu/menu.gd
extends Node
@export var ui: Control
@export var level_container: Node
@export var level_scene: PackedScene
@export var ip_line_edit: LineEdit
@export var status_label: Label
# reference to the 2 HBoxContainers
@export var host_hbox: HBoxContainer
@export var not_connected_hbox: HBoxContainer
func _ready():
multiplayer.connection_failed.connect(_on_connection_failed)
multiplayer.connected_to_server.connect(_on_connected_to_server)
func _process(_delta):
# if the Host is waiting for players, but pressed Esc, then stop waiting
if Input.is_action_just_pressed("back_to_not_connected") and status_label.text == "Waiting for players...":
# lobby destroy the game
Lobby.destroy_game()
# show the not connected HBoxContainer
not_connected_hbox.show()
# change the status label to "Not connected"
status_label.text = "Session closed"
func _on_host_button_pressed():
not_connected_hbox.hide()
Lobby.create_game()
status_label.text = "Waiting for players..."
func _on_join_button_pressed():
not_connected_hbox.hide()
Lobby.join_game(ip_line_edit.text)
status_label.text = "Connecting..."
func _on_start_button_pressed():
hide_menu.rpc()
change_level.call_deferred(level_scene)
func change_level(scene: PackedScene):
for child in level_container.get_children():
level_container.remove_child(child)
child.queue_free()
level_container.add_child(scene.instantiate(), true)
func _on_connection_failed():
status_label.text = "Connection failed"
not_connected_hbox.show()
func _on_connected_to_server():
status_label.text = "Connected!"
player_joined.rpc()
@rpc("call_local","authority","reliable")
func hide_menu():
ui.hide()
@rpc("call_remote","any_peer","reliable")
func player_joined():
status_label.text = "Player joined!"
host_hbox.show()
Hope it doesn’t get in the way with next steps in the course