"Handoff" from authoritative pregame lobby to client-relayed main gameplay? How?

I’m using Godot 4.2.1-stable Linux, Nakama 3.21.1, and the nakama-webrtc godot addon.

The game I’m trying to make is real-time competitive multiplayer with 2, 4, 6, or 8 players (2 teams).
I have followed along with the tutorials I could find but am not satisfied with the “click a button and automagically get put in a game” user experience of the matchmaking. Or the “copy paste match ID” thing. So I definitely want to go the “match listing” route.

I envisioned a panel on the right that keeps polling the API for matches and displays a custom control for each showing some basic data. Most importantly, the number of players joined over the maximum/ desired number of players- but also the username of the hosting player, how many seconds ago it was created, and a couple of lines of player-entered descriptive text.

Then you click one, and it loads more info about it on the left side- all the players’ names and avatars, maybe map choices and game rule options, the latest chat messages, and a “join” button. When you join, then you can chat with other players, and when the player count is full, you get a “ready” button where the join button was.

Then once everyone clicks “ready”, the lobby match goes away and everyone who was in it is now in a new match that is peer-to-peer over WebRTC.

So I got to trying to populate the UI listing these MatchListItem controls I just made

func set_content( _match_id : String, _players_joined: int, _players_max: int, 
	_created_by: String, _tick_started: int, _notes ) -> void:

	created_by = _created_by
	players_max = _players_max
	players_joined = _players_joined
	seconds_old = round((Time.get_ticks_msec() - _tick_started) / 1000)
	match_id = _match_id
	notes = _notes
	player_count_label.text = str( players_joined ) + "/" + str( players_max )
	host_name_label.text = created_by
	elapsed_time_label.text = str( seconds_old ) + " s"
	match_description.text = notes

Each one has its own match id as a property on the instance so clicking it joins that match by ID, as if you’d pasted it in manually.

func _update_match_list() -> void:
	#p_session : NakamaSession, p_min : int, p_max : int, p_limit : int, p_authoritative : bool, p_label : String, p_query : String):		
	var _latest_match_list = await Online.nakama_client.list_matches_async( Online.nakama_session, 1, 7, 50, false, "","" )
	for child in open_matches_list.get_children():
		child.queue_free()
	for _each_match in _latest_match_list.matches:
		var _list_item = list_item.instantiate()
		open_matches_list.add_child( _list_item )
		_list_item.set_content(
			"4815162342",
			3,
			6,
			"SomePlayer",
			5318008,
			"This is a test."
		)

And immediately realized I couldn’t get any of the data I wanted to feed to _list_item.set_content. I originally thought I could add a JSON ‘label’ to each match as it was created, but that only works for authoritative server matches- which isn’t what I want. The client-relayed matches have no information on them to list and so are basically only good for doing the blind automatic matchmaking.

So it looks like what I have to do is have the match list list lobbies which are authoritative but are not the actual game matches, which no one needs to find because they aren’t joinable once they’ve started. And the gameplay matches need to be client-relayed, taking advantage of WebRTC, so that the ‘server’ can just be one of the players’ Godot client; not the whole gameplay logic written in Typescript on the backend. So I just need to put all the players from the lobby into a new match.

Am I confused here? I feel like this must be either basically impossible or far easier to do than I’m presuming it to be and I can’t tell which.

Hello @pobqod,

We have a guide on creating lobbies with server authoritative runtime code. You could use a hybrid approach and create the lobbies authoritatively but then notify the clients so that one of them starts a relayed match and communicates the matchID through the lobby, then terminate the lobby match either by custom logic or by having everyone leave and setting max_empty_sec.

Another way of doing this would be to use Storage Search, you could create a storage object that the clients update with the lobby metadata and use the listing function to list matches that are not full, but you’d need to ensure the objects are deleted once the lobby is not needed anymore - the authoritative approach is best as it’ll allow to ensure there are no “dangling lobbies” with stale data that haven’t been deleted or updated correctly.

Hope this helps.

1 Like

Thanks for the quick reply! It will still take me a while to implement and test this stuff but with you saying it’s workable, at least now I know I’m not following a dead end.

I did already read through that lobby guide and the Sagi-Shi game example but got discouraged finding that neither one existed as a functioning project / downloadable repo, and the two examples that I could download, build and run (tutorial app and fish game) both had basically the same two match joining flows.
But I’ll go back and take a closer look.