From 235d7f38109256cc2460ba643404e4695c310416 Mon Sep 17 00:00:00 2001 From: Dominic Date: Sun, 4 Oct 2026 14:35:50 +0200 Subject: [PATCH] Prepare tester feedback and reproducible alpha builds --- ALPHA_TEST_CHECKLIST.md | 46 +++++++++++++++++++++++++++++++++++++ README.md | 7 ++++-- export_presets.cfg | 2 +- main.gd | 19 +++++++++++++++ online_manager.gd | 2 +- packaging/windows/build.ps1 | 44 +++++++++++++++++++++++++++++++++++ tests/smoke_test.gd | 4 ++++ 7 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 ALPHA_TEST_CHECKLIST.md create mode 100644 packaging/windows/build.ps1 diff --git a/ALPHA_TEST_CHECKLIST.md b/ALPHA_TEST_CHECKLIST.md new file mode 100644 index 0000000..965589a --- /dev/null +++ b/ALPHA_TEST_CHECKLIST.md @@ -0,0 +1,46 @@ +# Void Bastion – Alpha-Testcheckliste + +Diese Liste wird vor jeder öffentlichen Windows-Alpha vollständig abgearbeitet. Automatische Prüfungen sind mit **A**, manuelle Sicht- und Bedienprüfungen mit **M** gekennzeichnet. + +## Build und Installation + +- [ ] **A** `tests/smoke_test.gd` läuft ohne Fehler. +- [ ] **A** `tests/online_integration_test.gd` läuft gegen `api.void-bastion.gergames.de` ohne Fehler. +- [ ] **A** Windows-Export und Setup werden mit `packaging/windows/build.ps1` reproduzierbar erstellt. +- [ ] **A** Entwicklerwerkzeuge, Tests, Serverdateien und interne MCP-Konfigurationen sind nicht im Windows-Paket enthalten. +- [ ] **M** Setup installiert ohne Administratorrechte, Startmenü- und Desktop-Verknüpfung funktionieren. +- [ ] **M** Deinstallation entfernt Programmdateien und Verknüpfungen vollständig. +- [ ] **M** Windows-SmartScreen-Hinweis ist in den Testhinweisen genannt, solange der Installer noch nicht signiert ist. + +## Darstellung und Bedienung + +- [ ] **M** 1920 × 1080, 2560 × 1440 und eine kleinere 16:9-Auflösung prüfen: Karte und UI bleiben scharf und vollständig sichtbar. +- [ ] **M** Alle drei Karten prüfen: Straße, Gegnerlaufweg, Raster und Bausperren stimmen exakt überein. +- [ ] **M** Reaktoren und freie Weltraumbereiche sind auf allen Karten unbebaubar. +- [ ] **M** Reichweitenvorschau erscheint vor dem Bau; Turmwerte und Upgrade-Mehrwert sind lesbar. +- [ ] **M** Zurück-Schaltfläche bricht eine laufende Runde verständlich ab. + +## Spielablauf + +- [ ] **M** Jede Karte einmal auf Leicht, Mittel und Schwer starten; Startressourcen und Gegnerdruck unterscheiden sich sichtbar. +- [ ] **M** Eine Mission bis Welle 15 spielen: Sieg kann beendet oder freiwillig als Endloslauf fortgesetzt werden. +- [ ] **M** Eine Niederlage im Endlosmodus wird nicht als gescheiterte Mission gewertet. +- [ ] **M** Kernkristall-Belohnung wird nach erfolgreicher Karte gespeichert. +- [ ] **M** Spielstand, Einstellungen und Bestwerte bleiben nach einem Neustart erhalten. + +## Konto und Onlinefunktionen + +- [ ] **M** Gastkonto wird beim ersten Start erzeugt; Spielername lässt sich ändern. +- [ ] **M** Profil zeigt Version, gekürzte Spieler-ID, Datenschutz und Support/Feedback. +- [ ] **M** Bestenliste lädt; ein neuer Bestwert wird korrekt übertragen und dem sichtbaren Spielernamen zugeordnet. +- [ ] **M** Kontolöschung verlangt eine zweite Bestätigung und erzeugt anschließend ein neues Gastprofil. +- [ ] **M** Privater Raum: Erstellen, Code eingeben, Bereit-Lobby, Partiestart und Abbruch mit zwei Geräten prüfen. +- [ ] **M** Kurzen Netzabbruch während einer Onlinepartie und die Wiederverbindung prüfen. +- [ ] **M** Lokaler Koop mit KI: getrennte Ressourcen, zwei Gegnerwege, Seitenwechsel und gemeinsames Reaktorleben prüfen. + +## Veröffentlichung + +- [ ] **M** Setup-Datei und SHA-256-Prüfsumme stimmen mit der öffentlichen Release-Datei überein. +- [ ] **M** Download-Schaltfläche auf `https://void-bastion.gergames.de/` lädt die aktuelle Setup-Datei. +- [ ] **M** Datenschutz, Impressum, Supportadresse und Kontolöschseite sind öffentlich erreichbar. +- [ ] **M** Änderungen, bekannte Fehler und Testhinweise stehen in den Release-Notizen. diff --git a/README.md b/README.md index 5566cd8..78beae6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Void Bastion – spielbarer Prototyp 0.23 +# Void Bastion - Alpha 0.1.0 Ein plattformübergreifender 2D-Tower-Defense-Prototyp für Godot 4. Der aktuelle Stand ist für Windows, Android und später iOS ausgelegt. @@ -41,6 +41,7 @@ Ein plattformübergreifender 2D-Tower-Defense-Prototyp für Godot 4. Der aktuell - frei wählbarer Spielername mit identischer Prüfung im Spiel und auf dem Server - zweistufige Kontolöschung direkt im Pilotenprofil; danach wird ein neues lokales Gastprofil erstellt - direkter Datenschutz-Link im Pilotenprofil +- sichtbare App-Version und vorbereitete Support-Mail an `support@gergames.de` im Pilotenprofil; Version, Plattform und gekürzte Spieler-ID werden für eine schnellere Fehlerzuordnung eingesetzt - wartende, lokal gespeicherte Highscore-Einträge für die spätere sichere Serverübertragung - eigener Multiplayerbereich mit schneller Spielersuche sowie privaten Koop-Räumen mit sechsstelligen Raumcodes - gemeinsame Online-Lobby mit Spielernamen, festen Verteidigungsseiten und manuellem Bereit-Status für beide Piloten @@ -81,6 +82,8 @@ Der Prototyp wurde mit der offiziellen Godot-Version 4.7.2 importiert und im aut Der mitgelieferte Logiktest lässt sich bei Bedarf mit `godot --headless --path . --script res://tests/smoke_test.gd` erneut ausführen. +Für reproduzierbare Windows-Builds steht `packaging/windows/build.ps1` bereit. Godot und NSIS werden als Parameter übergeben; das Skript exportiert das Spiel, erstellt den Installer und gibt anschließend Größe und SHA-256-Prüfsumme aus. Die manuellen Freigabeprüfungen stehen in `ALPHA_TEST_CHECKLIST.md`. + ## Werbung und Datenschutz Die mobile Werbeanbindung ist technisch vorbereitet, aber absichtlich noch nicht für echte Werbung freigeschaltet: @@ -107,7 +110,7 @@ Die öffentliche Spiel-API ist unter `api.void-bastion.gergames.de` erreichbar. ## Steuerung -> **Online-Stand 0.23:** Der offizielle Nakama-Godot-Client, automatische Gastanmeldung, servergeprüfte öffentliche Spielernamen, Kontolöschung im Spiel, 36 autoritative Ranglisten, geprüfte Ergebnisübermittlung, Echtzeit-Sockets, serverautoritativer Zwei-Spieler-Koop, private Räume per Raumcode, eine Bereit-Lobby und Wiederverbindung sind eingebaut. Ranglisten lösen den aktuellen öffentlichen Profilnamen über die unveränderliche Spieler-ID auf. Die öffentliche API ist über `https://api.void-bastion.gergames.de` mit einem gültigen Let's-Encrypt-Zertifikat freigeschaltet. Details stehen in `ONLINE_SETUP.md`. +> **Online-Stand Alpha 0.1.0:** Der offizielle Nakama-Godot-Client, automatische Gastanmeldung, servergeprüfte öffentliche Spielernamen, Kontolöschung im Spiel, 36 autoritative Ranglisten, geprüfte Ergebnisübermittlung, Echtzeit-Sockets, serverautoritativer Zwei-Spieler-Koop, private Räume per Raumcode, eine Bereit-Lobby und Wiederverbindung sind eingebaut. Ranglisten lösen den aktuellen öffentlichen Profilnamen über die unveränderliche Spieler-ID auf. Die öffentliche API ist über `https://api.void-bastion.gergames.de` mit einem gültigen Let's-Encrypt-Zertifikat freigeschaltet. Details stehen in `ONLINE_SETUP.md`. Die Schaltfläche **Bestenliste** im Hauptmenü zeigt die zwölf besten Piloten für die aktuell gewählte Karte und Schwierigkeit. Zwischen Mission und Endlosmodus kann direkt in der Ansicht umgeschaltet werden; der eigene Eintrag wird farblich hervorgehoben. diff --git a/export_presets.cfg b/export_presets.cfg index 01f00af..b966c80 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -8,7 +8,7 @@ dedicated_server=false custom_features="" export_filter="all_resources" include_filter="" -exclude_filter="" +exclude_filter=".mcp.json,*.md,tests/*,server/*,tools/*,packaging/*,addons/agent_tools/*,build/*,dist/*" export_path="build/windows/Void-Bastion.exe" patches=PackedStringArray() diff --git a/main.gd b/main.gd index 3ef1a19..f38cef4 100644 --- a/main.gd +++ b/main.gd @@ -155,6 +155,7 @@ var profile_name_input := "" var profile_message := "" var profile_delete_armed := false var profile_rename_button := Rect2(220, 305, 250, 48) +var profile_support_button := Rect2(570, 685, 250, 48) var profile_privacy_button := Rect2(840, 685, 250, 48) var profile_delete_button := Rect2(1110, 685, 250, 48) var profile_name_input_rect := Rect2(460, 330, 680, 82) @@ -505,6 +506,8 @@ func _handle_pointer(position: Vector2, is_touch := false) -> void: game_mode = "menu" elif profile_rename_button.has_point(position): _open_profile_name_entry() + elif profile_support_button.has_point(position): + OS.shell_open(_support_email_url()) elif profile_privacy_button.has_point(position): OS.shell_open("https://void-bastion.gergames.de/privacy.html") elif profile_delete_button.has_point(position): @@ -742,6 +745,20 @@ func _hide_virtual_keyboard() -> void: DisplayServer.virtual_keyboard_hide() +func _app_version() -> String: + return str(ProjectSettings.get_setting("application/config/version", "Entwicklung")) + + +func _support_email_url() -> String: + var subject := "Void Bastion Feedback %s" % _app_version() + var body := "Version: %s\nPlattform: %s\nSpieler-ID: %s\n\nBeschreibung:\n" % [ + _app_version(), + OS.get_name(), + account_manager.short_id(), + ] + return "mailto:support@gergames.de?subject=%s&body=%s" % [subject.uri_encode(), body.uri_encode()] + + func _open_profile_name_entry() -> void: profile_name_input = account_manager.display_name profile_message = "" @@ -1796,6 +1813,7 @@ func _draw_profile() -> void: draw_rect(Rect2(Vector2.ZERO, SCREEN), Color(0.005, 0.015, 0.035, 0.76)) _draw_button(profile_back_button, "< ZURÜCK", false, Color("26394a")) draw_string(font, Vector2(0, 112), "PILOTENPROFIL", HORIZONTAL_ALIGNMENT_CENTER, 1600, 44, Color("c7f5ff")) + draw_string(font, Vector2(1110, 112), "VERSION %s" % _app_version(), HORIZONTAL_ALIGNMENT_RIGHT, 290, 14, Color("91a8bc")) draw_rect(Rect2(170, 155, 1260, 600), Color(0.012, 0.035, 0.065, 0.94), true) draw_rect(Rect2(170, 155, 1260, 600), Color("4ab8d1"), false, 2.0) draw_string(font, Vector2(220, 220), account_manager.display_name, HORIZONTAL_ALIGNMENT_LEFT, 520, 30, Color("8ff4ff")) @@ -1824,6 +1842,7 @@ func _draw_profile() -> void: draw_string(font, Vector2(1160, y), "%07d" % score, HORIZONTAL_ALIGNMENT_CENTER, 180, 16, Color("8fffea")) if not profile_message.is_empty(): draw_string(font, Vector2(220, 658), profile_message, HORIZONTAL_ALIGNMENT_CENTER, 1160, 14, Color("ffcf7a")) + _draw_button(profile_support_button, "SUPPORT / FEEDBACK", false, Color("24516a")) _draw_button(profile_privacy_button, "DATENSCHUTZ", false, Color("24475b")) _draw_button(profile_delete_button, "LÖSCHEN BESTÄTIGEN" if profile_delete_armed else "KONTO LÖSCHEN", profile_delete_armed, Color("6b2d3b")) diff --git a/online_manager.gd b/online_manager.gd index f849c7f..b12db13 100644 --- a/online_manager.gd +++ b/online_manager.gd @@ -129,7 +129,7 @@ func submit_run(record: Dictionary) -> bool: if not is_authenticated(): return false var payload := record.duplicate(true) - payload["client_version"] = "0.23" + payload["client_version"] = str(ProjectSettings.get_setting("application/config/version", "development")) var result = await client.rpc_async(session, "submit_run", JSON.stringify(payload)) var parsed := _parse_rpc_result(result) if parsed.is_empty() or not bool(parsed.get("accepted", false)): diff --git a/packaging/windows/build.ps1 b/packaging/windows/build.ps1 new file mode 100644 index 0000000..58db919 --- /dev/null +++ b/packaging/windows/build.ps1 @@ -0,0 +1,44 @@ +param( + [Parameter(Mandatory = $true)] + [string]$GodotExe, + + [Parameter(Mandatory = $true)] + [string]$MakeNsisExe +) + +$ErrorActionPreference = "Stop" +$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path +$gameExe = Join-Path $projectRoot "build\windows\Void-Bastion.exe" +$installer = Join-Path $projectRoot "dist\Void-Bastion-Setup-x64.exe" +$installerScript = Join-Path $PSScriptRoot "installer.nsi" + +foreach ($requiredFile in @($GodotExe, $MakeNsisExe, $installerScript)) { + if (-not (Test-Path -LiteralPath $requiredFile -PathType Leaf)) { + throw "Benötigte Datei fehlt: $requiredFile" + } +} + +New-Item -ItemType Directory -Force -Path (Split-Path $gameExe), (Split-Path $installer) | Out-Null + +& $GodotExe --headless --path $projectRoot --export-release "Windows x64" $gameExe +if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $gameExe)) { + throw "Der Godot-Windows-Export ist fehlgeschlagen." +} + +Push-Location $PSScriptRoot +try { + & $MakeNsisExe $installerScript + if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $installer)) { + throw "Die Installer-Erstellung mit NSIS ist fehlgeschlagen." + } +} +finally { + Pop-Location +} + +$installerFile = Get-Item -LiteralPath $installer +$hash = Get-FileHash -Algorithm SHA256 -LiteralPath $installer +Write-Host "Windows-Installer erfolgreich erstellt:" +Write-Host " Datei: $($installerFile.FullName)" +Write-Host " Größe: $([math]::Round($installerFile.Length / 1MB, 1)) MiB" +Write-Host " SHA256: $($hash.Hash)" diff --git a/tests/smoke_test.gd b/tests/smoke_test.gd index 032f663..d482e20 100644 --- a/tests/smoke_test.gd +++ b/tests/smoke_test.gd @@ -82,6 +82,10 @@ func _run() -> void: assert(game.get("game_mode") == "menu", "Die Bestenliste benötigt einen funktionierenden Zurück-Button.") assert(not account_manager.player_id.is_empty(), "Beim ersten Start muss automatisch ein stabiles Gastprofil angelegt werden.") assert(not account_manager.display_name.is_empty(), "Das Gastprofil benötigt einen sichtbaren Pilotennamen.") + assert(game.call("_app_version") == "0.1.0-alpha", "Profil, Installer und Online-Übertragung müssen dieselbe Alpha-Version verwenden.") + var support_url: String = game.call("_support_email_url") + assert(support_url.begins_with("mailto:support@gergames.de?"), "Die Feedback-Schaltfläche muss die öffentliche Supportadresse verwenden.") + assert(support_url.contains("0.1.0-alpha") and support_url.contains(account_manager.short_id()), "Die Support-Mail muss Version und gekürzte Spieler-ID zur Fehlerzuordnung enthalten.") account_manager.storage_enabled = false var original_display_name: String = account_manager.display_name assert(account_manager.validate_display_name("Kommandant Ägir").is_empty(), "Ein normaler internationaler Spielername muss gültig sein.")