Add Brogether menu modal
This commit is contained in:
29
README.md
29
README.md
@@ -13,6 +13,7 @@ Der aktuelle Stand ist bewusst ein technischer Prototyp. Es ist noch keine ferti
|
||||
- Ein lokaler Loopback-Test simuliert Paketverkehr ohne zweiten Steam-Client.
|
||||
- Brotatos Run-Szene wird per Script-Extension beobachtet.
|
||||
- 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.
|
||||
|
||||
## Was noch nicht fertig ist
|
||||
|
||||
@@ -25,16 +26,17 @@ Der aktuelle Stand ist bewusst ein technischer Prototyp. Es ist noch keine ferti
|
||||
|
||||
```text
|
||||
src/mods-unpacked/Brogether-Brogether/
|
||||
manifest.json Mod-Metadaten fuer Godot Mod Loader
|
||||
mod_main.gd Steam-Lobby, P2P-Pakete, Debug-Overlay, Logging
|
||||
extensions/main_ext.gd Hook in Brotatos Run-Szene
|
||||
manifest.json Mod-Metadaten fuer Godot Mod Loader
|
||||
mod_main.gd Steam-Lobby, P2P-Pakete, Brogether-Modal, Logging
|
||||
extensions/main_ext.gd Hook in Brotatos Run-Szene
|
||||
extensions/main_menu_ext.gd Brogether-Menuepunkt im Hauptmenue
|
||||
|
||||
scripts/
|
||||
package.ps1 Baut das Mod-ZIP
|
||||
install-local.ps1 Installiert das ZIP fuer lokale Brotato-Tests
|
||||
package.ps1 Baut das Mod-ZIP
|
||||
install-local.ps1 Installiert das ZIP fuer lokale Brotato-Tests
|
||||
|
||||
docs/
|
||||
setup.md Setup- und Testnotizen
|
||||
setup.md Setup- und Testnotizen
|
||||
brotato-multiplayer-modding-research.md
|
||||
```
|
||||
|
||||
@@ -50,10 +52,12 @@ Brotato laedt in der Steam-Version nur abonnierte Workshop-Ordner. Das Install-S
|
||||
|
||||
Danach Brotato komplett neu starten.
|
||||
|
||||
Brogether ist im Spiel standardmaessig unsichtbar. Im Hauptmenue gibt es einen neuen `BROGETHER`-Button; alternativ oeffnet `F5` das Brogether-Modal. Das Modal pausiert das Spiel wie Brotatos eigenes F8-Feedback-Fenster und kann mit `Escape`, `F5` oder dem Close-Button wieder geschlossen werden.
|
||||
|
||||
## Debug-Tasten
|
||||
|
||||
```text
|
||||
F5 Debug-Panel ein-/ausblenden
|
||||
F5 Brogether-Modal ein-/ausblenden
|
||||
F6 Steam-Lobby erstellen
|
||||
F7 Lobby suchen / joinen
|
||||
F9 Lobby verlassen
|
||||
@@ -61,7 +65,7 @@ F10 Ping senden
|
||||
F11 Lokalen Loopback-Test starten
|
||||
```
|
||||
|
||||
Zusätzlich zeigt Brogether ein kleines Debug-Panel oben rechts mit Buttons fuer Host, Join, Leave, Ping und Loopback. Das Panel ist fuer die naechsten Zwei-Client-Tests gedacht und ersetzt die meisten Hotkey-Aktionen.
|
||||
Das Brogether-Modal enthaelt Buttons fuer Host, Join, Leave, Ping und Loopback. Es ist fuer die naechsten Zwei-Client-Tests gedacht und ersetzt die meisten Hotkey-Aktionen.
|
||||
|
||||
Brogether schreibt ein eigenes Log nach:
|
||||
|
||||
@@ -74,9 +78,10 @@ C:\Users\tommi\AppData\Roaming\Brotato\brogether.log
|
||||
Sobald ein zweiter Steam-Client verfuegbar ist:
|
||||
|
||||
1. Beide Clients mit derselben Brogether-Version starten.
|
||||
2. Host drueckt `F6`.
|
||||
3. Client drueckt `F7`.
|
||||
4. Mit `F10` Ping/Pong testen.
|
||||
5. Einen Run starten und pruefen, ob `run_started`, `players_spawned` und Snapshot-Pakete auf beiden Seiten im Log erscheinen.
|
||||
2. Host oeffnet `BROGETHER` im Hauptmenue oder drueckt `F5`.
|
||||
3. Host klickt `Host` oder drueckt `F6`.
|
||||
4. Client klickt `Join` oder drueckt `F7`.
|
||||
5. Mit `Ping` oder `F10` Ping/Pong testen.
|
||||
6. Einen Run starten und pruefen, ob `run_started`, `players_spawned` und Snapshot-Pakete auf beiden Seiten im Log erscheinen.
|
||||
|
||||
Wenn das stabil laeuft, ist der naechste Entwicklungsschritt die echte Remote-Spielersteuerung auf Basis der Snapshot- und Input-Pakete.
|
||||
|
||||
@@ -58,7 +58,7 @@ Warum nicht sofort voll deterministisch/lockstep: Brotato hat viele zufalls- und
|
||||
|
||||
## MVP-Vorschlag
|
||||
|
||||
1. Mod-Skeleton laedt in Brotato und zeigt einen Multiplayer-Button oder Debug-Hotkeys.
|
||||
1. Mod-Skeleton laedt in Brotato und zeigt einen versteckten Multiplayer-Dialog ueber Hauptmenue-Button oder Hotkey.
|
||||
2. Steam-Lobby erstellen/joinen.
|
||||
3. Text-/Ping-Nachrichten zwischen zwei Clients senden.
|
||||
4. Run-Start zwischen zwei Clients synchronisieren.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Brogether Setup
|
||||
|
||||
Stand: 2026-05-22
|
||||
Stand: 2026-05-23
|
||||
|
||||
## Aktueller Zustand
|
||||
|
||||
@@ -27,9 +27,11 @@ Brotato laedt in der Steam-Version nur Workshop-Ordner, deren ID Steam als abonn
|
||||
|
||||
Danach Brotato ueber Steam starten. Die Mod sollte im Mod-Menue auftauchen.
|
||||
|
||||
Brogether selbst bleibt danach unsichtbar, bis es geoeffnet wird. Im Hauptmenue fuegt die Mod einen `BROGETHER`-Button hinzu. Alternativ oeffnet `F5` dasselbe Modal. Das Modal verhaelt sich bewusst wie Brotatos F8-Feedback-Fenster: es pausiert, legt sich ueber das Spiel und kann mit `Escape`, `F5` oder `Close` geschlossen werden.
|
||||
|
||||
## Debug-Hotkeys im Spiel
|
||||
|
||||
- `F5`: Debug-Panel ein-/ausblenden
|
||||
- `F5`: Brogether-Modal ein-/ausblenden
|
||||
- `F6`: oeffentliche Dev-Steam-Lobby erstellen
|
||||
- `F7`: Brogether-Lobby suchen und beitreten
|
||||
- `F9`: Lobby verlassen
|
||||
|
||||
@@ -38,4 +38,4 @@ Get-ChildItem -Path $legacyModsDir -Filter "PotatoConnect-PotatoConnect-*.zip" -
|
||||
Copy-Item -LiteralPath $zipPath -Destination (Join-Path $workshopItem.FullName (Split-Path -Leaf $zipPath)) -Force
|
||||
|
||||
Write-Host "Installed to workshop item folder $($workshopItem.FullName)"
|
||||
Write-Host "Start Brotato from Steam, then use F6 on host and F7 on client."
|
||||
Write-Host "Start Brotato from Steam, then open BROGETHER in the main menu or press F5."
|
||||
|
||||
@@ -35,10 +35,10 @@ func _exit_tree() -> void:
|
||||
|
||||
|
||||
func _get_brogether():
|
||||
var root := get_tree().get_root()
|
||||
var root: Viewport = get_tree().get_root()
|
||||
if root == null:
|
||||
return null
|
||||
var mod_loader := root.get_node_or_null("ModLoader")
|
||||
var mod_loader: Node = root.get_node_or_null("ModLoader")
|
||||
if mod_loader == null:
|
||||
return null
|
||||
return mod_loader.get_node_or_null("Brogether-Brogether")
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
extends "res://ui/menus/pages/main_menu.gd"
|
||||
|
||||
var _brogether_button: Button = null
|
||||
|
||||
|
||||
func setup_menu() -> void:
|
||||
.setup_menu()
|
||||
_add_brogether_button()
|
||||
|
||||
|
||||
func _add_brogether_button() -> void:
|
||||
if _brogether_button != null and is_instance_valid(_brogether_button):
|
||||
return
|
||||
if right_container == null:
|
||||
return
|
||||
_brogether_button = Button.new()
|
||||
_brogether_button.name = "BrogetherButton"
|
||||
_brogether_button.text = "BROGETHER"
|
||||
_brogether_button.rect_min_size = mods_button.rect_min_size
|
||||
_brogether_button.size_flags_horizontal = mods_button.size_flags_horizontal
|
||||
_brogether_button.size_flags_vertical = mods_button.size_flags_vertical
|
||||
_brogether_button.theme = mods_button.theme
|
||||
_brogether_button.connect("pressed", self, "_on_BrogetherButton_pressed")
|
||||
right_container.add_child(_brogether_button)
|
||||
right_container.move_child(_brogether_button, right_container.get_child_count() - 1)
|
||||
|
||||
|
||||
func _on_BrogetherButton_pressed() -> void:
|
||||
var brogether = _get_brogether()
|
||||
if brogether != null:
|
||||
brogether.open_debug_panel()
|
||||
|
||||
|
||||
func _get_brogether():
|
||||
var root: Viewport = get_tree().get_root()
|
||||
if root == null:
|
||||
return null
|
||||
var mod_loader: Node = root.get_node_or_null("ModLoader")
|
||||
if mod_loader == null:
|
||||
return null
|
||||
return mod_loader.get_node_or_null("Brogether-Brogether")
|
||||
@@ -11,10 +11,14 @@ var is_host := false
|
||||
var packet_sequence := 0
|
||||
var status_layer: CanvasLayer = null
|
||||
var status_label: Label = null
|
||||
var dim_background: ColorRect = null
|
||||
var panel: PanelContainer = null
|
||||
var panel_status_label: Label = null
|
||||
var panel_log_label: Label = null
|
||||
var panel_default_focus: Control = null
|
||||
var status_time_left := 0.0
|
||||
var restore_paused_value := false
|
||||
var restore_focus_control = null
|
||||
var current_run_id := ""
|
||||
var current_run_active := false
|
||||
var current_players := []
|
||||
@@ -23,6 +27,7 @@ var snapshot_sequence := 0
|
||||
|
||||
func _init() -> void:
|
||||
ModLoaderMod.install_script_extension("res://mods-unpacked/Brogether-Brogether/extensions/main_ext.gd")
|
||||
ModLoaderMod.install_script_extension("res://mods-unpacked/Brogether-Brogether/extensions/main_menu_ext.gd")
|
||||
|
||||
func _ready() -> void:
|
||||
name = MOD_ID
|
||||
@@ -33,7 +38,7 @@ func _ready() -> void:
|
||||
_create_status_overlay()
|
||||
_create_debug_panel()
|
||||
_init_steam()
|
||||
_log("loaded. F5=toggle panel, F6=create lobby, F7=find/join lobby, F9=leave, F10=ping, F11=loopback test")
|
||||
_log("loaded. F5=toggle modal, F6=create lobby, F7=find/join lobby, F9=leave, F10=ping, F11=loopback test")
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
@@ -52,6 +57,10 @@ func _process(_delta: float) -> void:
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not event is InputEventKey or not event.pressed or event.echo:
|
||||
return
|
||||
if panel != null and panel.visible and event.scancode == KEY_ESCAPE:
|
||||
close_debug_panel()
|
||||
get_tree().set_input_as_handled()
|
||||
return
|
||||
match event.scancode:
|
||||
KEY_F5:
|
||||
toggle_debug_panel()
|
||||
@@ -428,37 +437,48 @@ func _create_status_overlay() -> void:
|
||||
|
||||
|
||||
func _create_debug_panel() -> void:
|
||||
dim_background = ColorRect.new()
|
||||
dim_background.name = "BrogetherDimBackground"
|
||||
dim_background.anchor_right = 1.0
|
||||
dim_background.anchor_bottom = 1.0
|
||||
dim_background.color = Color(0, 0, 0, 0.62)
|
||||
dim_background.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
dim_background.visible = false
|
||||
status_layer.add_child(dim_background)
|
||||
|
||||
panel = PanelContainer.new()
|
||||
panel.name = "BrogetherPanel"
|
||||
panel.anchor_left = 1.0
|
||||
panel.anchor_right = 1.0
|
||||
panel.anchor_top = 0.0
|
||||
panel.anchor_bottom = 0.0
|
||||
panel.margin_left = -280
|
||||
panel.margin_right = -16
|
||||
panel.margin_top = 54
|
||||
panel.margin_bottom = 286
|
||||
panel.anchor_left = 0.5
|
||||
panel.anchor_right = 0.5
|
||||
panel.anchor_top = 0.5
|
||||
panel.anchor_bottom = 0.5
|
||||
panel.margin_left = -260
|
||||
panel.margin_right = 260
|
||||
panel.margin_top = -170
|
||||
panel.margin_bottom = 170
|
||||
panel.pause_mode = Node.PAUSE_MODE_PROCESS
|
||||
panel.visible = false
|
||||
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.05, 0.07, 0.08, 0.88)
|
||||
style.border_color = Color(0.25, 1.0, 0.65, 0.9)
|
||||
var style: StyleBoxFlat = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.08, 0.09, 0.1, 0.96)
|
||||
style.border_color = Color(0.55, 0.58, 0.62, 1.0)
|
||||
style.set_border_width_all(1)
|
||||
style.set_corner_radius_all(4)
|
||||
style.content_margin_left = 10
|
||||
style.content_margin_right = 10
|
||||
style.content_margin_top = 10
|
||||
style.content_margin_bottom = 10
|
||||
style.set_corner_radius_all(3)
|
||||
style.content_margin_left = 18
|
||||
style.content_margin_right = 18
|
||||
style.content_margin_top = 16
|
||||
style.content_margin_bottom = 16
|
||||
panel.add_stylebox_override("panel", style)
|
||||
|
||||
var box := VBoxContainer.new()
|
||||
var box: VBoxContainer = VBoxContainer.new()
|
||||
box.name = "Content"
|
||||
box.add_constant_override("separation", 6)
|
||||
box.add_constant_override("separation", 10)
|
||||
panel.add_child(box)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "Brogether"
|
||||
title.add_color_override("font_color", Color(0.25, 1.0, 0.65, 1.0))
|
||||
var title: Label = Label.new()
|
||||
title.text = "BROGETHER"
|
||||
title.align = Label.ALIGN_CENTER
|
||||
title.add_color_override("font_color", Color(1, 1, 1, 1))
|
||||
box.add_child(title)
|
||||
|
||||
panel_status_label = Label.new()
|
||||
@@ -466,50 +486,76 @@ func _create_debug_panel() -> void:
|
||||
panel_status_label.autowrap = true
|
||||
box.add_child(panel_status_label)
|
||||
|
||||
var grid := GridContainer.new()
|
||||
var grid: GridContainer = GridContainer.new()
|
||||
grid.columns = 2
|
||||
grid.add_constant_override("hseparation", 6)
|
||||
grid.add_constant_override("vseparation", 6)
|
||||
box.add_child(grid)
|
||||
|
||||
_add_panel_button(grid, "Host", "create_lobby")
|
||||
panel_default_focus = _add_panel_button(grid, "Host", "create_lobby")
|
||||
_add_panel_button(grid, "Join", "find_lobbies")
|
||||
_add_panel_button(grid, "Leave", "leave_lobby")
|
||||
_add_panel_button(grid, "Ping", "send_ping")
|
||||
_add_panel_button(grid, "Loopback", "run_loopback_test")
|
||||
_add_panel_button(grid, "Hide F5", "toggle_debug_panel")
|
||||
_add_panel_button(grid, "Close", "close_debug_panel")
|
||||
|
||||
panel_log_label = Label.new()
|
||||
panel_log_label.text = "Ready"
|
||||
panel_log_label.autowrap = true
|
||||
panel_log_label.rect_min_size = Vector2(240, 46)
|
||||
panel_log_label.rect_min_size = Vector2(480, 78)
|
||||
box.add_child(panel_log_label)
|
||||
|
||||
status_layer.add_child(panel)
|
||||
_update_debug_panel()
|
||||
|
||||
|
||||
func _add_panel_button(parent: Control, label: String, method_name: String) -> void:
|
||||
var button := Button.new()
|
||||
func _add_panel_button(parent: Control, label: String, method_name: String) -> Button:
|
||||
var button: Button = Button.new()
|
||||
button.text = label
|
||||
button.rect_min_size = Vector2(118, 30)
|
||||
button.focus_mode = Control.FOCUS_NONE
|
||||
button.rect_min_size = Vector2(236, 34)
|
||||
button.connect("pressed", self, method_name)
|
||||
parent.add_child(button)
|
||||
return button
|
||||
|
||||
|
||||
func toggle_debug_panel() -> void:
|
||||
if panel != null and panel.visible:
|
||||
close_debug_panel()
|
||||
else:
|
||||
open_debug_panel()
|
||||
|
||||
|
||||
func open_debug_panel() -> void:
|
||||
if panel == null:
|
||||
return
|
||||
panel.visible = not panel.visible
|
||||
_show_status("debug panel %s" % ("shown" if panel.visible else "hidden"))
|
||||
restore_paused_value = get_tree().paused
|
||||
restore_focus_control = _find_focus_owner()
|
||||
get_tree().paused = true
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
dim_background.visible = true
|
||||
panel.visible = true
|
||||
set_process_input(true)
|
||||
if panel_default_focus != null and is_instance_valid(panel_default_focus):
|
||||
panel_default_focus.call_deferred("grab_focus")
|
||||
_update_debug_panel("Panel opened")
|
||||
|
||||
|
||||
func close_debug_panel() -> void:
|
||||
if panel == null:
|
||||
return
|
||||
panel.visible = false
|
||||
if dim_background != null:
|
||||
dim_background.visible = false
|
||||
get_tree().paused = restore_paused_value
|
||||
if restore_focus_control != null and is_instance_valid(restore_focus_control):
|
||||
restore_focus_control.call_deferred("grab_focus")
|
||||
|
||||
|
||||
func _update_debug_panel(last_message: String = "") -> void:
|
||||
if panel_status_label == null:
|
||||
return
|
||||
var lobby_text := "none" if lobby_id == 0 else str(lobby_id)
|
||||
var role := "host" if is_host else "client"
|
||||
var lobby_text: String = "none" if lobby_id == 0 else str(lobby_id)
|
||||
var role: String = "host" if is_host else "client"
|
||||
panel_status_label.text = "Steam %s\nLobby %s\nRole %s | Members %s" % [steam_id, lobby_text, role, lobby_members.size()]
|
||||
if panel_log_label != null and not last_message.empty():
|
||||
panel_log_label.text = last_message
|
||||
@@ -518,11 +564,20 @@ func _update_debug_panel(last_message: String = "") -> void:
|
||||
func _show_status(message: String) -> void:
|
||||
if status_label == null:
|
||||
return
|
||||
if panel == null or not panel.visible:
|
||||
return
|
||||
status_label.text = "Brogether: " + message
|
||||
status_label.visible = true
|
||||
status_time_left = 5.0
|
||||
|
||||
|
||||
func _find_focus_owner():
|
||||
var scene: Node = get_tree().current_scene
|
||||
if scene != null and scene.has_method("get_focus_owner"):
|
||||
return scene.get_focus_owner()
|
||||
return null
|
||||
|
||||
|
||||
func _build_run_state() -> Dictionary:
|
||||
return {
|
||||
"run_id": current_run_id,
|
||||
|
||||
Reference in New Issue
Block a user