Add lobby ready fake peer test mode
This commit is contained in:
@@ -15,6 +15,7 @@ Der aktuelle Stand ist bewusst ein technischer Prototyp. Es ist noch keine ferti
|
||||
- Run-Start, Spieler-Spawn und einfache Player-Snapshots werden geloggt bzw. fuer P2P-Versand vorbereitet.
|
||||
- Das Brogether-Modal ist standardmaessig versteckt und kann ueber das Hauptmenue oder `F5` geoeffnet werden.
|
||||
- Die UI enthaelt einen ersten Lobby-Browser, Lobby-Erstellung mit optionalem Passwort und einen Wartebereich mit Spielerkacheln.
|
||||
- Der Wartebereich hat Ready-/Start-Status und einen Fake-Peer-Modus fuer lokale Tests ohne zweiten Client.
|
||||
|
||||
## Was noch nicht fertig ist
|
||||
|
||||
@@ -58,7 +59,7 @@ Brogether ist im Spiel standardmaessig unsichtbar. Im Hauptmenue gibt es einen n
|
||||
Im Modal gibt es jetzt zwei Bereiche:
|
||||
|
||||
- Lobby-Browser: zeigt gefundene Brogether-Lobbys mit Name, Host, Spielerzahl und Passwortstatus.
|
||||
- Wartebereich: zeigt die Spieler in der aktuellen Lobby als Kacheln mit Steam-Namen und statischem Initialen-Avatar.
|
||||
- Wartebereich: zeigt die Spieler in der aktuellen Lobby als Kacheln mit Steam-Namen, statischem Initialen-Avatar und Ready-Status.
|
||||
|
||||
## Debug-Tasten
|
||||
|
||||
@@ -73,6 +74,8 @@ F11 Lokalen Loopback-Test starten
|
||||
|
||||
Das Brogether-Modal enthaelt Buttons fuer Host, Join, Leave und Ping. Es ist fuer die naechsten Zwei-Client-Tests gedacht und ersetzt die meisten Hotkey-Aktionen. Passwort-Lobbys sind aktuell eine Mod-seitige Zugangskontrolle: die Lobby bleibt sichtbar, der Join-Button prueft aber das Passwort, bevor Brogether beitritt.
|
||||
|
||||
Fuer Tests ohne zweiten Steam-Client kann im Wartebereich `Add Fake` genutzt werden. Das fuegt einen lokalen Test-Peer hinzu, dessen Ready-Status mit `Fake Ready` umgeschaltet werden kann. `Start` wird nur fuer den Host aktiv, wenn alle sichtbaren Lobby-Mitglieder ready sind; aktuell schreibt Start nur ein Lobby-Protokollereignis ins Log und startet noch keinen echten Run.
|
||||
|
||||
Brogether schreibt ein eigenes Log nach:
|
||||
|
||||
```text
|
||||
|
||||
@@ -31,6 +31,8 @@ Brogether selbst bleibt danach unsichtbar, bis es geoeffnet wird. Im Hauptmenue
|
||||
|
||||
Die UI hat jetzt einen ersten Lobby-Browser. Dort kann man Lobbys suchen, eine Lobby mit optionalem Passwort erstellen und einer ausgewaehlten Lobby beitreten. Nach Host/Join wechselt Brogether in einen Wartebereich mit Steam-Namen und statischen Initialen-Avataren fuer die Lobby-Spieler.
|
||||
|
||||
Ohne zweiten Client kann im Wartebereich `Add Fake` gedrueckt werden. Dadurch erscheint ein lokaler Test-Peer. Mit `Ready` und `Fake Ready` lassen sich beide Ready-Zustaende pruefen; `Start` wird beim Host erst aktiv, wenn alle sichtbaren Mitglieder ready sind.
|
||||
|
||||
## Debug-Hotkeys im Spiel
|
||||
|
||||
- `F5`: Brogether-Modal ein-/ausblenden
|
||||
|
||||
@@ -4,6 +4,7 @@ const MOD_ID := "Brogether-Brogether"
|
||||
const LOG_PATH := "user://brogether.log"
|
||||
const BROGETHER_VERSION := "0.1.0"
|
||||
const MAX_LOBBY_MEMBERS := 2
|
||||
const FAKE_PEER_ID := 48000000000000001
|
||||
|
||||
var steam = null
|
||||
var lobby_id := 0
|
||||
@@ -12,6 +13,8 @@ var available_lobbies := []
|
||||
var selected_lobby_id := 0
|
||||
var pending_lobby_name := ""
|
||||
var pending_lobby_password_hash := ""
|
||||
var lobby_ready_states := {}
|
||||
var fake_peer_enabled := false
|
||||
var steam_id := 0
|
||||
var is_host := false
|
||||
var packet_sequence := 0
|
||||
@@ -34,6 +37,10 @@ var selected_lobby_label: Label = null
|
||||
var lobby_members_container: GridContainer = null
|
||||
var lobby_title_label: Label = null
|
||||
var lobby_detail_label: Label = null
|
||||
var ready_button: Button = null
|
||||
var start_button: Button = null
|
||||
var fake_peer_button: Button = null
|
||||
var fake_ready_button: Button = null
|
||||
var status_time_left := 0.0
|
||||
var restore_paused_value := false
|
||||
var restore_focus_control = null
|
||||
@@ -69,6 +76,8 @@ func _exit_tree() -> void:
|
||||
is_host = false
|
||||
lobby_members.clear()
|
||||
available_lobbies.clear()
|
||||
lobby_ready_states.clear()
|
||||
fake_peer_enabled = false
|
||||
current_players.clear()
|
||||
_free_ui()
|
||||
steam = null
|
||||
@@ -164,6 +173,8 @@ func leave_lobby() -> void:
|
||||
steam.call("leaveLobby", lobby_id)
|
||||
lobby_id = 0
|
||||
lobby_members.clear()
|
||||
lobby_ready_states.clear()
|
||||
fake_peer_enabled = false
|
||||
is_host = false
|
||||
_show_browser_view()
|
||||
_update_debug_panel()
|
||||
@@ -316,6 +327,8 @@ func _on_lobby_created(connect: int, created_lobby_id: int) -> void:
|
||||
steam.call("setLobbyData", lobby_id, "max_players", str(MAX_LOBBY_MEMBERS))
|
||||
steam.call("setLobbyData", lobby_id, "has_password", "1" if not pending_lobby_password_hash.empty() else "0")
|
||||
steam.call("setLobbyData", lobby_id, "password_hash", pending_lobby_password_hash)
|
||||
lobby_ready_states.clear()
|
||||
_set_member_ready(steam_id, false)
|
||||
_refresh_lobby_members()
|
||||
_show_lobby_view()
|
||||
_update_debug_panel()
|
||||
@@ -327,6 +340,8 @@ func _on_lobby_joined(joined_lobby_id: int, _permissions: int, _locked: bool, re
|
||||
_log("join failed for lobby %s: %s" % [joined_lobby_id, response])
|
||||
return
|
||||
lobby_id = joined_lobby_id
|
||||
lobby_ready_states.clear()
|
||||
_set_member_ready(steam_id, false)
|
||||
_refresh_lobby_members()
|
||||
is_host = steam.has_method("getLobbyOwner") and int(steam.call("getLobbyOwner", lobby_id)) == steam_id
|
||||
_log("joined lobby %s as %s with %s member(s)" % [lobby_id, "host" if is_host else "client", lobby_members.size()])
|
||||
@@ -419,6 +434,9 @@ func _refresh_lobby_members() -> void:
|
||||
for index in range(member_count):
|
||||
var member_id := int(steam.call("getLobbyMemberByIndex", lobby_id, index))
|
||||
lobby_members.append({"steam_id": member_id, "name": _get_persona_name(member_id)})
|
||||
if not lobby_ready_states.has(member_id):
|
||||
_set_member_ready(member_id, false)
|
||||
_append_fake_peer_if_needed()
|
||||
_log("members: %s" % lobby_members)
|
||||
_update_lobby_room()
|
||||
_update_debug_panel()
|
||||
@@ -471,6 +489,13 @@ func _handle_packet(sender: int, data, is_loopback: bool = false) -> void:
|
||||
})
|
||||
"pong":
|
||||
_log("pong received from %s for sequence %s" % [sender, data.get("sequence", -1)])
|
||||
"ready_state":
|
||||
var ready_sender := int(data.get("from", sender))
|
||||
_set_member_ready(ready_sender, bool(data.get("ready", false)))
|
||||
_log("ready state from %s: %s" % [ready_sender, _ready_text(ready_sender)])
|
||||
_update_lobby_room()
|
||||
"lobby_start":
|
||||
_log("lobby start requested by %s: %s" % [sender, data])
|
||||
"run_started":
|
||||
_log("remote run started from %s: %s" % [sender, data.get("state", {})])
|
||||
"players_spawned":
|
||||
@@ -494,9 +519,12 @@ func _send_p2p_packet(target: int, packet_data: Dictionary) -> void:
|
||||
if target == 0:
|
||||
for member in lobby_members:
|
||||
var member_id := int(member["steam_id"])
|
||||
if member_id != steam_id:
|
||||
if member_id != steam_id and not _is_fake_peer(member_id):
|
||||
steam.call("sendP2PPacket", member_id, payload, send_type, 0)
|
||||
else:
|
||||
if _is_fake_peer(target):
|
||||
_handle_packet(target, packet_data, true)
|
||||
return
|
||||
steam.call("sendP2PPacket", target, payload, send_type, 0)
|
||||
|
||||
|
||||
@@ -739,6 +767,10 @@ func _create_debug_panel() -> void:
|
||||
lobby_actions.add_constant_override("separation", 8)
|
||||
lobby_view.add_child(lobby_actions)
|
||||
|
||||
ready_button = _add_panel_button(lobby_actions, "Ready", "toggle_local_ready")
|
||||
start_button = _add_panel_button(lobby_actions, "Start", "request_lobby_start")
|
||||
fake_peer_button = _add_panel_button(lobby_actions, "Fake Peer", "toggle_fake_peer")
|
||||
fake_ready_button = _add_panel_button(lobby_actions, "Fake Ready", "toggle_fake_peer_ready")
|
||||
_add_panel_button(lobby_actions, "Ping", "send_ping")
|
||||
_add_panel_button(lobby_actions, "Leave", "leave_lobby")
|
||||
_add_panel_button(lobby_actions, "Browser", "_show_browser_view")
|
||||
@@ -801,6 +833,70 @@ func _join_selected_lobby_from_ui() -> void:
|
||||
join_lobby(selected_lobby_id)
|
||||
|
||||
|
||||
func toggle_local_ready() -> void:
|
||||
if lobby_id == 0:
|
||||
_log("join or create a lobby before readying up")
|
||||
return
|
||||
var next_ready := not _is_member_ready(steam_id)
|
||||
_set_member_ready(steam_id, next_ready)
|
||||
_send_p2p_packet(0, {
|
||||
"type": "ready_state",
|
||||
"from": steam_id,
|
||||
"ready": next_ready
|
||||
})
|
||||
_log("local ready state: %s" % _ready_text(steam_id))
|
||||
_update_lobby_room()
|
||||
|
||||
|
||||
func request_lobby_start() -> void:
|
||||
if lobby_id == 0:
|
||||
_log("create or join a lobby before starting")
|
||||
return
|
||||
if not is_host:
|
||||
_log("only the lobby host can start")
|
||||
return
|
||||
if not _all_members_ready():
|
||||
_log("cannot start: not all lobby members are ready")
|
||||
return
|
||||
var start_packet := {
|
||||
"type": "lobby_start",
|
||||
"from": steam_id,
|
||||
"lobby_id": str(lobby_id),
|
||||
"member_count": lobby_members.size(),
|
||||
"fake_peer_enabled": fake_peer_enabled
|
||||
}
|
||||
_log("lobby start ready: %s" % start_packet)
|
||||
_send_p2p_packet(0, start_packet)
|
||||
|
||||
|
||||
func toggle_fake_peer() -> void:
|
||||
if lobby_id == 0:
|
||||
_log("create a lobby before adding a fake peer")
|
||||
return
|
||||
fake_peer_enabled = not fake_peer_enabled
|
||||
if fake_peer_enabled:
|
||||
_set_member_ready(FAKE_PEER_ID, false)
|
||||
_append_fake_peer_if_needed()
|
||||
_handle_packet(FAKE_PEER_ID, {"type": "handshake", "from": FAKE_PEER_ID}, true)
|
||||
_log("fake peer joined the lobby")
|
||||
else:
|
||||
_remove_fake_peer()
|
||||
_log("fake peer left the lobby")
|
||||
_update_lobby_room()
|
||||
|
||||
|
||||
func toggle_fake_peer_ready() -> void:
|
||||
if not fake_peer_enabled:
|
||||
_log("add a fake peer before toggling fake ready")
|
||||
return
|
||||
var next_ready := not _is_member_ready(FAKE_PEER_ID)
|
||||
_handle_packet(FAKE_PEER_ID, {
|
||||
"type": "ready_state",
|
||||
"from": FAKE_PEER_ID,
|
||||
"ready": next_ready
|
||||
}, true)
|
||||
|
||||
|
||||
func _show_browser_view() -> void:
|
||||
if browser_view != null:
|
||||
browser_view.visible = true
|
||||
@@ -893,12 +989,13 @@ func _update_lobby_room() -> void:
|
||||
if lobby_title_label != null:
|
||||
lobby_title_label.text = lobby_name.to_upper()
|
||||
if lobby_detail_label != null:
|
||||
lobby_detail_label.text = "Wartebereich %s | %s/%s Spieler | %s" % [str(lobby_id), lobby_members.size(), MAX_LOBBY_MEMBERS, password_text]
|
||||
lobby_detail_label.text = "Wartebereich %s | %s/%s Spieler | %s | %s" % [str(lobby_id), lobby_members.size(), MAX_LOBBY_MEMBERS, password_text, _lobby_ready_summary()]
|
||||
for member in lobby_members:
|
||||
_add_member_card(member)
|
||||
var empty_slot_count: int = max(0, MAX_LOBBY_MEMBERS - lobby_members.size())
|
||||
for empty_slot in range(empty_slot_count):
|
||||
_add_empty_member_slot(empty_slot)
|
||||
_update_lobby_controls()
|
||||
|
||||
|
||||
func _add_member_card(member: Dictionary) -> void:
|
||||
@@ -941,6 +1038,11 @@ func _add_member_card(member: Dictionary) -> void:
|
||||
role_label.text = "Host" if int(member.get("steam_id", 0)) == _current_lobby_owner() else "Player"
|
||||
text_box.add_child(role_label)
|
||||
|
||||
var ready_label: Label = Label.new()
|
||||
ready_label.text = _ready_text(int(member.get("steam_id", 0)))
|
||||
ready_label.add_color_override("font_color", Color(0.45, 0.9, 0.56, 1.0) if _is_member_ready(int(member.get("steam_id", 0))) else Color(0.9, 0.62, 0.4, 1.0))
|
||||
text_box.add_child(ready_label)
|
||||
|
||||
|
||||
func _add_empty_member_slot(_index: int) -> void:
|
||||
var card: PanelContainer = PanelContainer.new()
|
||||
@@ -962,6 +1064,70 @@ func _current_lobby_owner() -> int:
|
||||
return steam_id if is_host else 0
|
||||
|
||||
|
||||
func _append_fake_peer_if_needed() -> void:
|
||||
if not fake_peer_enabled:
|
||||
return
|
||||
for member in lobby_members:
|
||||
if int(member.get("steam_id", 0)) == FAKE_PEER_ID:
|
||||
return
|
||||
lobby_members.append({"steam_id": FAKE_PEER_ID, "name": "Fake Test Peer", "fake": true})
|
||||
if not lobby_ready_states.has(FAKE_PEER_ID):
|
||||
_set_member_ready(FAKE_PEER_ID, false)
|
||||
|
||||
|
||||
func _remove_fake_peer() -> void:
|
||||
for index in range(lobby_members.size() - 1, -1, -1):
|
||||
var member: Dictionary = lobby_members[index]
|
||||
if int(member.get("steam_id", 0)) == FAKE_PEER_ID:
|
||||
lobby_members.remove(index)
|
||||
lobby_ready_states.erase(FAKE_PEER_ID)
|
||||
|
||||
|
||||
func _is_fake_peer(member_id: int) -> bool:
|
||||
return member_id == FAKE_PEER_ID
|
||||
|
||||
|
||||
func _set_member_ready(member_id: int, ready: bool) -> void:
|
||||
lobby_ready_states[member_id] = ready
|
||||
|
||||
|
||||
func _is_member_ready(member_id: int) -> bool:
|
||||
return bool(lobby_ready_states.get(member_id, false))
|
||||
|
||||
|
||||
func _ready_text(member_id: int) -> String:
|
||||
return "Ready" if _is_member_ready(member_id) else "Waiting"
|
||||
|
||||
|
||||
func _all_members_ready() -> bool:
|
||||
if lobby_members.empty():
|
||||
return false
|
||||
for member in lobby_members:
|
||||
if not _is_member_ready(int(member.get("steam_id", 0))):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _lobby_ready_summary() -> String:
|
||||
var ready_count := 0
|
||||
for member in lobby_members:
|
||||
if _is_member_ready(int(member.get("steam_id", 0))):
|
||||
ready_count += 1
|
||||
return "%s/%s ready" % [ready_count, lobby_members.size()]
|
||||
|
||||
|
||||
func _update_lobby_controls() -> void:
|
||||
if ready_button != null:
|
||||
ready_button.text = "Unready" if _is_member_ready(steam_id) else "Ready"
|
||||
if start_button != null:
|
||||
start_button.disabled = not is_host or not _all_members_ready()
|
||||
if fake_peer_button != null:
|
||||
fake_peer_button.text = "Remove Fake" if fake_peer_enabled else "Add Fake"
|
||||
if fake_ready_button != null:
|
||||
fake_ready_button.disabled = not fake_peer_enabled
|
||||
fake_ready_button.text = "Fake Unready" if fake_peer_enabled and _is_member_ready(FAKE_PEER_ID) else "Fake Ready"
|
||||
|
||||
|
||||
func _initials(display_name: String) -> String:
|
||||
var clean := display_name.strip_edges()
|
||||
if clean.empty():
|
||||
@@ -1022,6 +1188,10 @@ func _free_ui() -> void:
|
||||
lobby_members_container = null
|
||||
lobby_title_label = null
|
||||
lobby_detail_label = null
|
||||
ready_button = null
|
||||
start_button = null
|
||||
fake_peer_button = null
|
||||
fake_ready_button = null
|
||||
|
||||
|
||||
func toggle_debug_panel() -> void:
|
||||
|
||||
Reference in New Issue
Block a user