feat: Add lobby and multiplayer room management with Nakama and ENet LAN support.

This commit is contained in:
Yogi Wiguna
2026-03-17 12:45:16 +08:00
parent 6eb6dfa20d
commit 1b8e411657
5 changed files with 23 additions and 38 deletions
+4 -6
View File
@@ -726,12 +726,8 @@ func _on_ready_state_changed(_player_id: int, _is_ready: bool) -> void:
func _on_all_players_ready() -> void: func _on_all_players_ready() -> void:
if LobbyManager.is_host: if LobbyManager.is_host:
if LobbyManager.is_lan_mode and LobbyManager.players_in_room.size() == 1: start_game_btn.disabled = false
# Auto-start for solo LAN testing status_label.text = "All ready! Start the match!"
LobbyManager.start_game()
else:
start_game_btn.disabled = false
status_label.text = "All ready! Start the match!"
else: else:
status_label.text = "All ready! Waiting for host..." status_label.text = "All ready! Waiting for host..."
@@ -877,6 +873,8 @@ func _update_player_slots() -> void:
ready_label.text = "WAITING..." ready_label.text = "WAITING..."
ready_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.7)) ready_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.7))
_update_status()
func _update_status() -> void: func _update_status() -> void:
var players = LobbyManager.get_players() var players = LobbyManager.get_players()
var ready_count = 0 var ready_count = 0
+14 -14
View File
@@ -1,24 +1,24 @@
[gd_scene format=3 uid="uid://b8yqx5v3n8u1p"] [gd_scene format=3 uid="uid://b8yqx5v3n8u1p"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_block"]
albedo_color = Color(0.46, 0.46, 0.46, 1)
metallic = 0.5
roughness = 0.8
uv1_scale = Vector3(3, 2, 1)
[sub_resource type="BoxMesh" id="BoxMesh_wall"]
material = SubResource("StandardMaterial3D_block")
size = Vector3(1, 2, 0.1)
[sub_resource type="BoxShape3D" id="BoxShape3D_wall"] [sub_resource type="BoxShape3D" id="BoxShape3D_wall"]
size = Vector3(1, 2, 0.1) size = Vector3(1, 2, 0.1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wall"] [node name="SafeZoneWall" type="StaticBody3D" unique_id=393858776]
albedo_color = Color(0.8, 0.2, 0.2, 0.8)
emission_enabled = true
emission = Color(1, 0, 0, 1)
emission_energy_multiplier = 0.5
[sub_resource type="BoxMesh" id="BoxMesh_wall"]
material = SubResource("StandardMaterial3D_wall")
size = Vector3(1, 2, 0.1)
[node name="SafeZoneWall" type="StaticBody3D"]
collision_layer = 1
collision_mask = 0 collision_mask = 0
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=110057234]
visible = false
mesh = SubResource("BoxMesh_wall") mesh = SubResource("BoxMesh_wall")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1293049960]
shape = SubResource("BoxShape3D_wall") shape = SubResource("BoxShape3D_wall")
+1 -4
View File
@@ -1,4 +1,4 @@
[gd_scene format=3 uid="uid://cggmcgvdj6wxt"] [gd_scene format=3 uid="uid://sb04b5pkw2g6"]
[sub_resource type="BoxShape3D" id="BoxShape3D_wall"] [sub_resource type="BoxShape3D" id="BoxShape3D_wall"]
size = Vector3(1.68, 1.5, 0.05) size = Vector3(1.68, 1.5, 0.05)
@@ -6,9 +6,6 @@ size = Vector3(1.68, 1.5, 0.05)
[node name="Wall3D" type="StaticBody3D" unique_id=992511920] [node name="Wall3D" type="StaticBody3D" unique_id=992511920]
collision_mask = 0 collision_mask = 0
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1405008923]
transform = Transform3D(1.68, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1446599023] [node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1446599023]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.35764623, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.35764623, 0)
shape = SubResource("BoxShape3D_wall") shape = SubResource("BoxShape3D_wall")
+3 -13
View File
@@ -359,8 +359,8 @@ func sync_ready_state(player_id: int, is_ready: bool) -> void:
func _check_all_ready() -> void: func _check_all_ready() -> void:
"""Check if all players are ready.""" """Check if all players are ready."""
# In LAN mode allow solo play - only 1 player needed # Allow solo play in both LAN and Nakama modes
var min_players = 1 if is_lan_mode else 2 var min_players = 1
if players_in_room.size() < min_players: if players_in_room.size() < min_players:
_all_ready = false _all_ready = false
return return
@@ -373,17 +373,6 @@ func _check_all_ready() -> void:
_all_ready = true _all_ready = true
emit_signal("all_players_ready") emit_signal("all_players_ready")
func force_solo_ready() -> void:
"""Mark the local player as ready immediately (for solo LAN play)."""
if not multiplayer.has_multiplayer_peer():
return
var my_id = multiplayer.get_unique_id()
for player in players_in_room:
if player["id"] == my_id:
player["is_ready"] = true
break
_check_all_ready()
func is_all_ready() -> bool: func is_all_ready() -> bool:
return _all_ready return _all_ready
@@ -814,6 +803,7 @@ func _on_peer_connected(peer_id: int) -> void:
# Sync player list to all clients # Sync player list to all clients
rpc("sync_player_list", players_in_room) rpc("sync_player_list", players_in_room)
emit_signal("player_joined", new_player) emit_signal("player_joined", new_player)
_check_all_ready()
else: else:
# Client: if we connected to the host (peer_id 1), request room info # Client: if we connected to the host (peer_id 1), request room info
if peer_id == 1 and not is_host: if peer_id == 1 and not is_host:
+1 -1
View File
@@ -21,7 +21,7 @@ const POWERUP_SPAWN_COUNT: int = 5 # Number of power-up tiles to spawn
var powerups_spawned: bool = false var powerups_spawned: bool = false
var stop_phase_occurred: bool = false var stop_phase_occurred: bool = false
var safe_zone_wall_scene = preload("res://scenes/wall_3d.tscn") var safe_zone_wall_scene = preload("res://scenes/safe_zone_wall.tscn")
const PERMANENT_POWERUP_LOCATIONS: Array[Vector2i] = [ const PERMANENT_POWERUP_LOCATIONS: Array[Vector2i] = [
Vector2i(4, 1), # Power up 1 Vector2i(4, 1), # Power up 1