add multiplayer lobby system
This commit is contained in:
		
							parent
							
								
									3eb8b2a718
								
							
						
					
					
						commit
						ef8553fe45
					
				
					 21 changed files with 322 additions and 15 deletions
				
			
		
							
								
								
									
										15
									
								
								level/PlayerSpawner.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								level/PlayerSpawner.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| extends MultiplayerSpawner | ||||
| 
 | ||||
| func _ready(): | ||||
| 	set_multiplayer_authority(1) | ||||
| 	spawn_function = spawn_player | ||||
| 
 | ||||
| func spawn_player(owner_id: int): | ||||
| 	var player := preload("res://player/player.tscn").instantiate() | ||||
| 	player.set_multiplayer_authority(owner_id) | ||||
| 	if multiplayer.get_unique_id() == owner_id: | ||||
| 		var camera := preload("res://camera/camera.tscn").instantiate() | ||||
| 		player.add_child(camera) | ||||
| 	player.position.y = 5.0 | ||||
| 	return player | ||||
| 
 | ||||
							
								
								
									
										5
									
								
								level/level.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								level/level.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| extends Node3D | ||||
| 
 | ||||
| # Called from the server | ||||
| func server_add_player(id: int): | ||||
| 	%PlayerSpawner.spawn(id) | ||||
|  | @ -1,10 +1,10 @@ | |||
| [gd_scene load_steps=8 format=3 uid="uid://b00brfkibo5cj"] | ||||
| 
 | ||||
| [ext_resource type="PackedScene" uid="uid://bq654gwim6col" path="res://level/level.glb" id="1_s37in"] | ||||
| [ext_resource type="PackedScene" uid="uid://do25xvpy80iio" path="res://player/player.tscn" id="2_7ct70"] | ||||
| [ext_resource type="Environment" uid="uid://covjrwmk4rplw" path="res://level/world_environment.tres" id="2_ptkl6"] | ||||
| [ext_resource type="PackedScene" uid="uid://brgqf2ebuyhuy" path="res://camera/camera.tscn" id="3_yev7j"] | ||||
| [ext_resource type="Script" path="res://level/level.gd" id="2_s1bx6"] | ||||
| [ext_resource type="Script" path="res://addons/smoother/smoother.gd" id="5_2tyle"] | ||||
| [ext_resource type="Script" path="res://level/PlayerSpawner.gd" id="6_7ww0m"] | ||||
| 
 | ||||
| [sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ujmev"] | ||||
| margin = 2.067 | ||||
|  | @ -13,6 +13,7 @@ margin = 2.067 | |||
| size = Vector3(2, 0.1, 2) | ||||
| 
 | ||||
| [node name="level" instance=ExtResource("1_s37in")] | ||||
| script = ExtResource("2_s1bx6") | ||||
| 
 | ||||
| [node name="WorldEnvironment" type="WorldEnvironment" parent="." index="0"] | ||||
| environment = ExtResource("2_ptkl6") | ||||
|  | @ -30,17 +31,14 @@ shape = SubResource("WorldBoundaryShape3D_ujmev") | |||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0) | ||||
| shape = SubResource("BoxShape3D_qp06x") | ||||
| 
 | ||||
| [node name="Player" parent="." index="3" instance=ExtResource("2_7ct70")] | ||||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.36667, 2.66437, 0) | ||||
| floor_max_angle = 1.309 | ||||
| JUMP_VELOCITY = 5.0 | ||||
| [node name="Players" type="Node3D" parent="." index="3"] | ||||
| unique_name_in_owner = true | ||||
| 
 | ||||
| [node name="Camera" parent="Player" index="2" instance=ExtResource("3_yev7j")] | ||||
| 
 | ||||
| [node name="Players" type="Node3D" parent="." index="4"] | ||||
| [node name="PlayerSpawner" type="MultiplayerSpawner" parent="." index="4"] | ||||
| unique_name_in_owner = true | ||||
| _spawnable_scenes = PackedStringArray("res://player/player.tscn") | ||||
| spawn_path = NodePath("../Players") | ||||
| script = ExtResource("6_7ww0m") | ||||
| 
 | ||||
| [node name="Smoother" type="Node" parent="." index="5"] | ||||
| script = ExtResource("5_2tyle") | ||||
| 
 | ||||
| [node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." index="6"] | ||||
| spawn_path = NodePath("../Players") | ||||
|  |  | |||
							
								
								
									
										16
									
								
								menu/main_menu.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								menu/main_menu.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| extends Control | ||||
| 
 | ||||
| 
 | ||||
| func _on_create_server_pressed(): | ||||
| 	get_tree().change_scene_to_file("res://server/server.tscn") | ||||
| 
 | ||||
| 
 | ||||
| func _on_connect_to_server_pressed(): | ||||
| 	var address: String = %ConnectIP.text | ||||
| 	var port := int(%ConnectPort.text) | ||||
| 	var parent := get_parent() | ||||
| 	queue_free() | ||||
| 	var scene := preload("res://server/client_lobbies.tscn").instantiate() | ||||
| 	scene.address = address | ||||
| 	scene.port = port | ||||
| 	parent.add_child(scene) | ||||
							
								
								
									
										51
									
								
								menu/main_menu.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								menu/main_menu.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| [gd_scene load_steps=2 format=3 uid="uid://b4tgnq0dwf13"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://menu/main_menu.gd" id="1_q1k1j"] | ||||
| 
 | ||||
| [node name="MainMenu" type="Control"] | ||||
| layout_mode = 3 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| script = ExtResource("1_q1k1j") | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="."] | ||||
| layout_mode = 1 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| 
 | ||||
| [node name="CreateServer" type="Button" parent="VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| text = "Start Server" | ||||
| 
 | ||||
| [node name="Label" type="Label" parent="VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| text = "Server IP" | ||||
| 
 | ||||
| [node name="ConnectIP" type="TextEdit" parent="VBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| custom_minimum_size = Vector2(0, 61.195) | ||||
| layout_mode = 2 | ||||
| text = "127.0.0.1" | ||||
| 
 | ||||
| [node name="Label2" type="Label" parent="VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| text = "Server Port" | ||||
| 
 | ||||
| [node name="ConnectPort" type="TextEdit" parent="VBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| custom_minimum_size = Vector2(0, 61.195) | ||||
| layout_mode = 2 | ||||
| text = "3000" | ||||
| 
 | ||||
| [node name="ConnectToServer" type="Button" parent="VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| text = "Connect To Server" | ||||
| 
 | ||||
| [connection signal="pressed" from="VBoxContainer/CreateServer" to="." method="_on_create_server_pressed"] | ||||
| [connection signal="pressed" from="VBoxContainer/ConnectToServer" to="." method="_on_connect_to_server_pressed"] | ||||
|  | @ -7,15 +7,14 @@ | |||
| properties/0/path = NodePath(".:position") | ||||
| properties/0/spawn = true | ||||
| properties/0/replication_mode = 1 | ||||
| properties/1/path = NodePath("player:velocity") | ||||
| properties/1/spawn = true | ||||
| properties/1/replication_mode = 1 | ||||
| 
 | ||||
| [sub_resource type="SphereShape3D" id="SphereShape3D_t1htn"] | ||||
| radius = 0.986757 | ||||
| 
 | ||||
| [node name="player" instance=ExtResource("1_0u2un")] | ||||
| floor_max_angle = 1.32121 | ||||
| script = ExtResource("1_gh340") | ||||
| JUMP_VELOCITY = 8.0 | ||||
| 
 | ||||
| [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." index="0"] | ||||
| replication_config = SubResource("SceneReplicationConfig_u4bmc") | ||||
|  |  | |||
|  | @ -29,9 +29,14 @@ DSP/dsp_buffer_count=4 | |||
| [application] | ||||
| 
 | ||||
| config/name="Grounders" | ||||
| run/main_scene="res://menu/main_menu.tscn" | ||||
| config/features=PackedStringArray("4.2", "Forward Plus") | ||||
| config/icon="res://icon.svg" | ||||
| 
 | ||||
| [autoload] | ||||
| 
 | ||||
| MultiplayerEvents="*res://server/MultiplayerEvents.gd" | ||||
| 
 | ||||
| [input] | ||||
| 
 | ||||
| move_left={ | ||||
|  |  | |||
							
								
								
									
										7
									
								
								server/LobbyListEntry.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/LobbyListEntry.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| extends Button | ||||
| 
 | ||||
| @export var address: String | ||||
| @export var port: int | ||||
| 
 | ||||
| func _ready(): | ||||
| 	text = "Connect to " + address + ":" + str(port) | ||||
							
								
								
									
										2
									
								
								server/MultiplayerEvents.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								server/MultiplayerEvents.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| extends Node | ||||
| 
 | ||||
							
								
								
									
										15
									
								
								server/client.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								server/client.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| extends Node | ||||
| 
 | ||||
| @export var address: String | ||||
| @export var port: int | ||||
| 
 | ||||
| func _ready(): | ||||
| 	var m = SceneMultiplayer.new() | ||||
| 	get_tree().set_multiplayer(m, get_path()) | ||||
| 	var peer := ENetMultiplayerPeer.new() | ||||
| 	peer.create_client(address, port) | ||||
| 	m.multiplayer_peer = peer	 | ||||
| 	m.connected_to_server.connect(connected_to_server) | ||||
| 	 | ||||
| func connected_to_server(): | ||||
| 	print("Client connected to server") | ||||
							
								
								
									
										9
									
								
								server/client.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								server/client.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://cb5t0uw0nr5bg"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://server/client.gd" id="1_frcvp"] | ||||
| [ext_resource type="PackedScene" uid="uid://b00brfkibo5cj" path="res://level/level.tscn" id="2_eigcb"] | ||||
| 
 | ||||
| [node name="Client" type="Node"] | ||||
| script = ExtResource("1_frcvp") | ||||
| 
 | ||||
| [node name="level" parent="." instance=ExtResource("2_eigcb")] | ||||
							
								
								
									
										25
									
								
								server/client_lobbies.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								server/client_lobbies.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| extends Control | ||||
| 
 | ||||
| var address: String | ||||
| var port: int | ||||
| 
 | ||||
| func _ready(): | ||||
| 	var m = SceneMultiplayer.new() | ||||
| 	get_tree().set_multiplayer(m, %LobbyList.get_path()) | ||||
| 	var peer := ENetMultiplayerPeer.new() | ||||
| 	var error := peer.create_client(address, port) | ||||
| 	if error: | ||||
| 		push_error(error) | ||||
| 	m.multiplayer_peer = peer | ||||
| 	m.connected_to_server.connect(connected_to_server) | ||||
| 	%LobbyList.set_multiplayer_authority(1) | ||||
| 
 | ||||
| func connected_to_server(): | ||||
| 	print("Connected to master server @ " + address + ":" + str(port)) | ||||
| 
 | ||||
| func _on_lobby_list_join_lobby(join_addr, join_port): | ||||
| 	queue_free() | ||||
| 	var client := preload("res://server/client.tscn").instantiate() | ||||
| 	client.address = join_addr | ||||
| 	client.port = join_port | ||||
| 	get_parent().add_child(client) | ||||
							
								
								
									
										27
									
								
								server/client_lobbies.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server/client_lobbies.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://d3i1pstq13h2p"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://server/client_lobbies.gd" id="1_2hyqo"] | ||||
| [ext_resource type="PackedScene" uid="uid://bdfsbuxqrkq68" path="res://server/lobby_list.tscn" id="2_rktrq"] | ||||
| 
 | ||||
| [node name="Server" type="Control"] | ||||
| layout_mode = 3 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| script = ExtResource("1_2hyqo") | ||||
| 
 | ||||
| [node name="Label" type="Label" parent="."] | ||||
| layout_mode = 0 | ||||
| offset_right = 40.0 | ||||
| offset_bottom = 23.0 | ||||
| text = "Lobby List:" | ||||
| 
 | ||||
| [node name="LobbyList" parent="." instance=ExtResource("2_rktrq")] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 1 | ||||
| 
 | ||||
| [connection signal="join_lobby" from="LobbyList" to="." method="_on_lobby_list_join_lobby"] | ||||
| 
 | ||||
| [editable path="LobbyList"] | ||||
							
								
								
									
										16
									
								
								server/lobby.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								server/lobby.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| extends Node | ||||
| 
 | ||||
| var port: int | ||||
| var MAX_CLIENTS := 32 | ||||
| 
 | ||||
| func _ready(): | ||||
| 	var m = SceneMultiplayer.new() | ||||
| 	get_tree().set_multiplayer(m, get_path()) | ||||
| 	var peer := ENetMultiplayerPeer.new() | ||||
| 	peer.create_server(port, MAX_CLIENTS) | ||||
| 	m.multiplayer_peer = peer | ||||
| 	m.peer_connected.connect(peer_connected) | ||||
| 
 | ||||
| func peer_connected(id: int): | ||||
| 	print("Client " + str(id) + "connected to lobby " + str(get_path())) | ||||
| 	$level.server_add_player(id) | ||||
							
								
								
									
										9
									
								
								server/lobby.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								server/lobby.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://tilcdm56kb10"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://server/lobby.gd" id="1_cpwsy"] | ||||
| [ext_resource type="PackedScene" uid="uid://b00brfkibo5cj" path="res://level/level.tscn" id="2_mulf5"] | ||||
| 
 | ||||
| [node name="Lobby" type="Node"] | ||||
| script = ExtResource("1_cpwsy") | ||||
| 
 | ||||
| [node name="level" parent="." instance=ExtResource("2_mulf5")] | ||||
							
								
								
									
										17
									
								
								server/lobby_list.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								server/lobby_list.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| extends Control | ||||
| 
 | ||||
| signal join_lobby(addr: String, port: int) | ||||
| 
 | ||||
| @rpc("reliable", "authority", "call_local") | ||||
| func add(addr: String, port: int): | ||||
| 	var lobby_button := preload("res://server/lobby_list_entry.tscn").instantiate() | ||||
| 	lobby_button.address = addr | ||||
| 	lobby_button.port = port | ||||
| 	$Lobbies.add_child(lobby_button, true) | ||||
| 	lobby_button.pressed.connect(emit_join_lobby.bind(addr, port)) | ||||
| 
 | ||||
| func emit_join_lobby(addr: String, port: int): | ||||
| 	join_lobby.emit(addr, port) | ||||
| 
 | ||||
| func lobbies(): | ||||
| 	return $Lobbies.get_children() | ||||
							
								
								
									
										21
									
								
								server/lobby_list.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								server/lobby_list.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| [gd_scene load_steps=2 format=3 uid="uid://bdfsbuxqrkq68"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://server/lobby_list.gd" id="1_tktrj"] | ||||
| 
 | ||||
| [node name="LobbyList" type="Control"] | ||||
| layout_mode = 3 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| offset_top = 67.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| script = ExtResource("1_tktrj") | ||||
| 
 | ||||
| [node name="Lobbies" type="VBoxContainer" parent="."] | ||||
| layout_mode = 1 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
							
								
								
									
										6
									
								
								server/lobby_list_entry.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								server/lobby_list_entry.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| [gd_scene load_steps=2 format=3 uid="uid://mhnsc0w6xt85"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://server/LobbyListEntry.gd" id="1_su4dr"] | ||||
| 
 | ||||
| [node name="LobbyListEntry" type="Button"] | ||||
| script = ExtResource("1_su4dr") | ||||
							
								
								
									
										4
									
								
								server/overview.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								server/overview.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| Two types of servers / clients | ||||
| One for lobby management and one for actual game | ||||
| server.gd and client_lobbies.gd handle the former | ||||
| and lobby.gd and client.gd handle the latter | ||||
							
								
								
									
										31
									
								
								server/server.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/server.gd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| extends Node | ||||
| 
 | ||||
| @export var external_address := "127.0.0.1" | ||||
| var master_server_port := 3000 | ||||
| var next_port_num := master_server_port + 1 | ||||
| const MAX_CLIENTS = 4095 | ||||
| 
 | ||||
| func _ready(): | ||||
| 	var m = SceneMultiplayer.new() | ||||
| 	get_tree().set_multiplayer(m, %LobbyList.get_path()) | ||||
| 	var peer := ENetMultiplayerPeer.new() | ||||
| 	var error := peer.create_server(master_server_port, MAX_CLIENTS) | ||||
| 	if error: | ||||
| 		push_error(error) | ||||
| 	m.multiplayer_peer = peer | ||||
| 	m.peer_connected.connect(peer_connected) | ||||
| 	 | ||||
| func peer_connected(id: int): | ||||
| 	print("Client " + str(id) + " connected to master server") | ||||
| 	for lobby in %LobbyList.lobbies(): | ||||
| 		%LobbyList.add.rpc_id(id, lobby.address, lobby.port) | ||||
| 
 | ||||
| func create_lobby(): | ||||
| 	var lobby := preload("res://server/lobby.tscn").instantiate() | ||||
| 	lobby.port = next_port_num | ||||
| 	next_port_num += 1 | ||||
| 	%Lobbies.add_child(lobby) | ||||
| 	%LobbyList.add.rpc(external_address, lobby.port) | ||||
| 	 | ||||
| func _on_create_lobby_pressed(): | ||||
| 	create_lobby() | ||||
							
								
								
									
										29
									
								
								server/server.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								server/server.tscn
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://cb6qjcj72sfe2"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://server/server.gd" id="1_0oac5"] | ||||
| [ext_resource type="PackedScene" uid="uid://bdfsbuxqrkq68" path="res://server/lobby_list.tscn" id="2_mxbok"] | ||||
| 
 | ||||
| [node name="Server" type="Node"] | ||||
| script = ExtResource("1_0oac5") | ||||
| 
 | ||||
| [node name="DebugControls" type="Control" parent="."] | ||||
| layout_mode = 3 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| 
 | ||||
| [node name="CreateLobby" type="Button" parent="DebugControls"] | ||||
| layout_mode = 0 | ||||
| offset_right = 8.0 | ||||
| offset_bottom = 8.0 | ||||
| text = "Create Lobby" | ||||
| 
 | ||||
| [node name="LobbyList" parent="." instance=ExtResource("2_mxbok")] | ||||
| unique_name_in_owner = true | ||||
| 
 | ||||
| [node name="Lobbies" type="Node" parent="."] | ||||
| unique_name_in_owner = true | ||||
| 
 | ||||
| [connection signal="pressed" from="DebugControls/CreateLobby" to="." method="_on_create_lobby_pressed"] | ||||
		Loading…
	
	Add table
		
		Reference in a new issue