Steam Integration - RoyasDev/EzNet GitHub Wiki

In this tutorial we will learn how to make a Steam Networker to allow EzNet and Steam to work together!

The repo for this networker can be found here

This godot project needs to be opened with the GodotSteam MultiplayerPeer version of Godot you can download that here

Now on to the tutorial! The first thing you will need to do is to go to the GodotSteam website

image

Click download and choose the MultiplayerPeer option

Screenshot 2025-06-18 110753

Then choose the latest version of GodotSteam to download

Screenshot 2025-06-18 111003

Now open the version of Godot you just downloaded and create a new project

image

In the new project click on the AssetLib tab at the top

image

Now search for EzNet

image

Once EzNet is installed IF it didn't place it in the addons folder move it to the addons folder

image

Go to res://addons/EzNet/example/scenes/levels/ and open example_level.tscn

image

Delete the ConnectionMenu Node

image

Create a new node under the Node3D and call it Steam. This node will be used to initialize steam when the game starts up

image

Attach a new script to the Steam Node

image

In this scripts _ready function set the SteamAppID and the SteamGameID. I am setting them to 480, but if you have your own steam app id you should set it to that.

func _ready() -> void:
	OS.set_environment("SteamAppID", str(480))
	OS.set_environment("SteamGameID", str(480))

Now at the end of your ready function init steam by calling Steam.steamInitEx()

func _ready() -> void:
	OS.set_environment("SteamAppID", str(480))
	OS.set_environment("SteamGameID", str(480))
	Steam.steamInitEx()

To finish this script in the _process function call Steam.run_callbacks() so that the steam call back system updates.

func _process(delta: float) -> void:
	Steam.run_callbacks()

The steam.gd script is now completed and now if steam is running our game should initialize with steam!

extends Node

func _ready() -> void:
	OS.set_environment("SteamAppID", str(480))
	OS.set_environment("SteamGameID", str(480))
	Steam.steamInitEx()


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	Steam.run_callbacks()

Go back to your scene and under the Menus node Make a control node and call it SteamConnectionMenu

image

Add two buttons underneath our newly created control node and call one Host and the other Refresh

image

Set the position of the Host button to x:0.0 y:616.0 and its size to x:1152.0 y:32.0 and the Refresh buttons position to x:256.0 y:384.0 and its size to x:640.0 y:32.0

image

Now add a Scroll Container Node under the SteamConnectionMenu and call it LobbyContainer

image

Then child a VBoxContainer to the LobbyContainer and call it LobbiesList

image

Set the position of the LobbyContainer to x:256.0 y:192.0 and its size to x:640.0 y:192.0

image

open the res://addons/EzNet/scripts/network/ folder and create a new script called steam_networker.gd

image

open the steam_networker.gd script and make it extend the Networker class

extends Networker

Now we will export a variable of type int for the max_players that we will allow into our games and define 3 other variables for the lobby information. The lobby information variables will be for the lobby name of type String, the lobby type which will be used to determine if the lobby is public or private and that will be of type SteamMultiplayerPeer.LobbyType and the last one will be the lobby id which will be of type int

@export var max_players : int = 4

var lobby_name : String
var lobby_type : SteamMultiplayerPeer.LobbyType = SteamMultiplayerPeer.LobbyType.LOBBY_TYPE_PUBLIC
var lobby_id : int

We will then also define a variable called peer that will be of type SteamMultiplayerPeer and we will also make a signal called on_lobby_created

var peer : SteamMultiplayerPeer

signal on_lobby_created

Since we are extending the Networker class we need to have a host() -> MultiplayerPeer and a connect_client() -> MultiplayerPeer functions defined. Lets start wtih the host() function. In the host() function we will start by creating a new SteamMultiplayerPeer and assigning it to our peer variable. Then we will connect to the peer's lobby_created signal with the function _on_lobby_created. Don't worry we will create that function later.

func host() -> MultiplayerPeer:
	peer = SteamMultiplayerPeer.new()
	peer.lobby_created.connect(_on_lobby_created)

Now that we have our peer we will create a variable called err of type Error and set that equal to peer.create_lobby and that function will take in our lobby_type and max_players variables

var err : Error = peer.create_lobby(lobby_type, max_players)

Now we will need to check if we created our lobby successfully. We do that by checking if our err variable doesn't equal OK. If it doesn't we will print an error and return null

	if err != OK:
		printerr("Could not initialize network host")
		return null

Finally for our host function we will return our peer

return peer

Now lets create that _on_lobby_created function we connected to the peers lobby_created signal. This gets called by our peer when we create a lobby. It has two arguments connect which is an int and an id which is also an int

func _on_lobby_created(connect : int, id : int):

Then we will check to see if the lobby was created successfully by checking if connect is equal to team.Result.RESULT_OK. If it was created successfully we will then set our lobby_id to be the id. Then we will use that lobby_id to set the name of the lobby and set the lobby to be joinable. Finally we will emit the on_lobby_created signal we made.

func _on_lobby_created(connect : int, id : int):
	if connect == Steam.Result.RESULT_OK:
		lobby_id = id
		Steam.setLobbyData(lobby_id, "name", lobby_name)
		Steam.setLobbyJoinable(lobby_id, true)
		on_lobby_created.emit()

Now lets make the connect_client function it will be mostly like the host one, but with a couple of tweaks. The first tweak is that on the peer we will call connect_lobby passing in the lobby_id and the second one is we won't connect to any signals.

func connect_client() -> MultiplayerPeer:
	peer = SteamMultiplayerPeer.new()
	var err : Error = peer.connect_lobby(lobby_id)
	
	if err != OK:
		printerr("Could not connect to lobby")
		return null
	
	return peer

When this script is all done it should look like this

extends Networker

@export var max_players : int = 4

var lobby_name : String
var lobby_type : SteamMultiplayerPeer.LobbyType = SteamMultiplayerPeer.LobbyType.LOBBY_TYPE_PUBLIC
var lobby_id : int

var peer : SteamMultiplayerPeer

signal on_lobby_created

func host() -> MultiplayerPeer:
	peer = SteamMultiplayerPeer.new()
	peer.lobby_created.connect(_on_lobby_created)
	var err : Error = peer.create_lobby(lobby_type, max_players)
	
	if err != OK:
		printerr("Could not initialize network host")
		return null
	
	return peer
	

func connect_client() -> MultiplayerPeer:
	peer = SteamMultiplayerPeer.new()
	var err : Error = peer.connect_lobby(lobby_id)
	
	if err != OK:
		printerr("Could not connect to lobby")
		return null
	
	return peer

func _on_lobby_created(connect : int, id : int):
	if connect == Steam.Result.RESULT_OK:
		lobby_id = id
		Steam.setLobbyData(lobby_id, "name", lobby_name)
		Steam.setLobbyJoinable(lobby_id, true)
		on_lobby_created.emit()

image

Now click on the ExampleNetworkManager Node and then click the create resource in memory and edit it button in the inspector

image

Make a default Resource and drag the steam_networker.gd script into the script part of the resource

image

Now hit the save button on the Resource

image

And save the Resource as steam_networker.tres in the res://addons/EzNet/resources folder

image

Click on the ExampleNetworkManager again and drag in the steam_networker.tres into the Networker slot

image

Now all we have to do is wire up the connection menu and we are all set! To do that click on the SteamConnectionMenu and add a new script that we will call steam_connection_menu.gd

image

Open the script and at the top we will get the network_manage onready

extends Control

@onready var network_manager : NetworkManager = get_node("/root/Node3D/ExampleNetworkManager")

Now lets make some functions that we will connect to some signals later! Lets start at joining a lobby, so lets make a function called _join_lobby that takes int a lobby id of type int. It then uses that lobby id to set the id of the lobby we will try to connect to on the networker and then we will call the network_manager to connect the client

func _join_lobby(lobby : int):
	network_manager.networker.lobby_id = lobby
	network_manager._connect_client()

How will we call this magical _join_lobby function you might ask? Well we will create another function called _on_lobby_match_list that takes in an array of lobbies.

func _on_lobby_match_list(lobbies : Array):

We will then loop through those lobbies and get their names and the number of members that they have and store those in variables

for lobby in lobbies:
	var lobby_name = Steam.getLobbyData(lobby, "name")
	var memb_count = Steam.getNumLobbyMembers(lobby)

Once we have gotten the lobby's information we will create a button and set the text of that button to the information we collected from that lobby

var btn : Button = Button.new()
btn.set_text("%s | Player Count: %s" % [lobby_name, memb_count])

Then we will create a callable out of our _join_lobby function and bind the lobby's number to it and then connect that callable to our new buttons pressed signal

btn.pressed.connect(Callable(_join_lobby).bind(lobby))

and we will add that button to be a child of our LobbyContainer/LobbiesList

$LobbyContainer/LobbiesList.add_child(btn)

That handles our joining lobbies logic. Now we have to handle our hosting logic. To do that we will create a function called _host and set the lobby_name on our networker to be the screen name of the current steam client and then we will call the network_manager._create_server function to start hosting! Its as easy as that!

func _host():
	network_manager.networker.lobby_name = Steam.getPersonaName()
	network_manager._create_server()

Now we will want our connection menu to disappear and our color select menu to appear when we connect to a server either through hosting or joining. So to do that we will create a function called _on_connected_to_server and get our ColorSelectMenu node and show it then we will hide our SteamConnectionMenu node

func _on_connected_to_server():
	get_node("/root/Node3D/Menus/ColorSelectMenu").show()
	hide()

Finally all we gotta do is connect our functions to the corresponding signals in _ready

func _ready() -> void:
	network_manager.on_server_started.connect(_on_connected_to_server)
	$Host.pressed.connect(_host)
	$Refresh.pressed.connect(_open_lobby_list)

The final script should look like this

extends Control

@onready var network_manager : NetworkManager = get_node("/root/Node3D/ExampleNetworkManager")

func _ready() -> void:
	network_manager.on_server_started.connect(_on_connected_to_server)
	$Host.pressed.connect(_host)
	$Refresh.pressed.connect(_open_lobby_list)

func _host():
	network_manager.networker.lobby_name = Steam.getPersonaName()
	network_manager._create_server()

func _on_connected_to_server():
	get_node("/root/Node3D/Menus/ColorSelectMenu").show()
	hide()

func _open_lobby_list():
	Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE)
	Steam.lobby_match_list.connect(_on_lobby_match_list)
	Steam.requestLobbyList()
	

func _on_lobby_match_list(lobbies : Array):
	
	for lobby in lobbies:
		var lobby_name = Steam.getLobbyData(lobby, "name")
		var memb_count = Steam.getNumLobbyMembers(lobby)
		
		var btn : Button = Button.new()
		btn.set_text("%s | Player Count: %s" % [lobby_name, memb_count])
		
		btn.pressed.connect(Callable(_join_lobby).bind(lobby))
		
		$LobbyContainer/LobbiesList.add_child(btn)

func _join_lobby(lobby : int):
	network_manager.networker.lobby_id = lobby
	network_manager._connect_client()

Steam_Menu

Note: You will have to use 2 different computers and 2 different steam accounts to test this. You can't test on the same computer unless you have virtual machines setup.