Other way to wait for other player before starting the game

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

Thanks for this. I also wanted to enable the start button after the players were connected.

2 Likes

Privacy & Terms