Leaderboard attiva di default, senza configurazione
La classifica globale non dipende più da un argomento: sui build desktop il client usa il servizio pubblico DEFAULT_API_BASE (https://enne2.net/api/zombie-fps). - scripts/leaderboard.gd: resolve_base() statica con priorità --api=<url> (test) → --offline/--no-api (opt-out esplicito per le partite di prova) → origine della pagina sul Web → servizio pubblico. Rimosso l'avviso "Classifica disattivata". - Le suite di test (--script) restano spente a meno di un --api= esplicito, così nessun test può scrivere nella classifica pubblica. - tools/test_start_screen.gd: verifica default attivo, opt-out e override; tools/test_rebalance_branding.gd: suite di test senza invii. - docs/HIGHSCORES.md: nuova regola della base URL e diagnostica aggiornata. Verifiche: test_start_screen 18/18 PASS con display; test_rebalance_branding 14/14 PASS; test_leaderboard.sh invariato (12 PASS, 2 FAIL preesistenti su questa macchina anche prima della modifica).
This commit is contained in:
+12
-8
@@ -125,13 +125,17 @@ sudo nginx -t && sudo systemctl reload nginx # se -t fallisce: ripristinar
|
|||||||
|
|
||||||
## 5. Client Godot
|
## 5. Client Godot
|
||||||
|
|
||||||
* **Base URL** (`scripts/leaderboard.gd`, `_resolve_base()`):
|
* **Base URL** (`scripts/leaderboard.gd`, `resolve_base()`):
|
||||||
1. argomento `--api=http://host:porta` (test);
|
1. argomento `--api=http://host:porta` (test o servizio alternativo);
|
||||||
2. su Web: `window.location.origin + /api/zombie-fps`;
|
2. argomento `--offline` / `--no-api` → classifica **spenta** (partite di prova);
|
||||||
3. altrimenti **niente**: la classifica resta disattivata con
|
3. su Web: `window.location.origin + /api/zombie-fps`;
|
||||||
`DISABLED_REASON = "classifica non attiva in questa build"`.
|
4. altrimenti il servizio pubblico `DEFAULT_API_BASE =
|
||||||
Regola importante: una build desktop di prova **non deve** poter inviare punteggi
|
https://enne2.net/api/zombie-fps`.
|
||||||
alla classifica pubblica (è già successo: ci sono finite righe `Test`).
|
La classifica è quindi **attiva di default**: non serve nesuna configurazione né
|
||||||
|
argomento per leggere e inviare punteggi. L'opt-out esiste solo per le partite di
|
||||||
|
prova, che non devono finire nella classifica pubblica (è già successo: ci sono
|
||||||
|
finite righe `Test`). Le suite di test girano con `--script` e restano spente a
|
||||||
|
meno di un `--api=` esplicito.
|
||||||
* Due nodi `HTTPRequest` separati (lettura e invio non si sovrappongono); timeout 12 s,
|
* Due nodi `HTTPRequest` separati (lettura e invio non si sovrappongono); timeout 12 s,
|
||||||
`body_size_limit` 256 KB / 64 KB.
|
`body_size_limit` 256 KB / 64 KB.
|
||||||
* Retry su 429/5xx/errore di trasporto: fino a 3 tentativi, 6 s di pausa, **stesso
|
* Retry su 429/5xx/errore di trasporto: fino a 3 tentativi, 6 s di pausa, **stesso
|
||||||
@@ -223,4 +227,4 @@ curl -s "https://enne2.net/api/zombie-fps/scores?limit=5"
|
|||||||
| `classifica non disponibile (rete non disponibile)` nel menu | servizio spento o nginx non ricaricato: `docker ps`, `curl 127.0.0.1:8791/health`, `sudo nginx -t` |
|
| `classifica non disponibile (rete non disponibile)` nel menu | servizio spento o nginx non ricaricato: `docker ps`, `curl 127.0.0.1:8791/health`, `sudo nginx -t` |
|
||||||
| punteggio non inviato (`429`) | limite di frequenza per IP (20/ora, 5 s tra invii); i retry riusano lo stesso `submission_id`, quindi non raddoppiano |
|
| punteggio non inviato (`429`) | limite di frequenza per IP (20/ora, 5 s tra invii); i retry riusano lo stesso `submission_id`, quindi non raddoppiano |
|
||||||
| la pagina resta su una release vecchia | `index.html` è servito con `cache-control: no-cache`; forzare il ricaricamento della pagina |
|
| la pagina resta su una release vecchia | `index.html` è servito con `cache-control: no-cache`; forzare il ricaricamento della pagina |
|
||||||
| righe di prova nella classifica pubblica | una build desktop ha inviato senza `--api=`: verificare `leaderboard.disabled` (test `test_start_screen.gd`) |
|
| righe di prova nella classifica pubblica | la classifica è attiva di default: le partite di prova vanno lanciate con `-- --offline` (verificare `leaderboard.disabled`, test `test_start_screen.gd`) |
|
||||||
|
|||||||
+34
-10
@@ -3,8 +3,12 @@ extends Node
|
|||||||
##
|
##
|
||||||
## - Web: stessa origine della pagina (window.location.origin) + /api/zombie-fps, cosi'
|
## - Web: stessa origine della pagina (window.location.origin) + /api/zombie-fps, cosi'
|
||||||
## il browser non ha bisogno di CORS.
|
## il browser non ha bisogno di CORS.
|
||||||
## - Desktop: nessuna classifica, a meno di --api=http://... (serve ai test). Cosi' una
|
## - Desktop: servizio pubblico `DEFAULT_API_BASE`, quindi la classifica e' ATTIVA DI
|
||||||
## partita di prova non finisce MAI nella classifica pubblica.
|
## DEFAULT: non serve nessuna configurazione ne' argomento per leggere e inviare.
|
||||||
|
## - Override espliciti: `--api=<url>` punta a un altro servizio (test), `--offline`
|
||||||
|
## (alias `--no-api`) spegne la classifica per le partite di prova.
|
||||||
|
## - Suite di test (avviate con `--script`): spente, a meno di un `--api=` esplicito,
|
||||||
|
## cosi' nessun test puo' scrivere nella classifica pubblica.
|
||||||
## - Due nodi HTTPRequest separati: Godot non ammette due richieste insieme sullo
|
## - Due nodi HTTPRequest separati: Godot non ammette due richieste insieme sullo
|
||||||
## stesso nodo, e lettura e invio possono sovrapporsi (l'invio avviene alla morte).
|
## stesso nodo, e lettura e invio possono sovrapporsi (l'invio avviene alla morte).
|
||||||
## - Un errore di rete non deve mai rompere il gioco: si segnala e basta.
|
## - Un errore di rete non deve mai rompere il gioco: si segnala e basta.
|
||||||
@@ -14,6 +18,8 @@ signal score_submitted(info: Dictionary)
|
|||||||
signal submit_failed(reason: String)
|
signal submit_failed(reason: String)
|
||||||
|
|
||||||
const API_PATH := "/api/zombie-fps"
|
const API_PATH := "/api/zombie-fps"
|
||||||
|
## Servizio pubblico: la classifica funziona senza alcuna configurazione.
|
||||||
|
const DEFAULT_API_BASE := "https://enne2.net/api/zombie-fps"
|
||||||
const TOP_LIMIT := 10
|
const TOP_LIMIT := 10
|
||||||
const RETRY_DELAY := 6.0
|
const RETRY_DELAY := 6.0
|
||||||
const RETRY_LIMIT := 3
|
const RETRY_LIMIT := 3
|
||||||
@@ -22,7 +28,7 @@ const DISABLED_REASON := "classifica non attiva in questa build"
|
|||||||
|
|
||||||
var player_id := ""
|
var player_id := ""
|
||||||
var base_url := ""
|
var base_url := ""
|
||||||
## Vero quando non c'e' nessun servizio da usare (build desktop senza --api=).
|
## Vero solo quando il servizio e' spento apposta (--offline) o in una suite di test.
|
||||||
var disabled := false
|
var disabled := false
|
||||||
|
|
||||||
var _get_request: HTTPRequest
|
var _get_request: HTTPRequest
|
||||||
@@ -52,19 +58,37 @@ func _ready() -> void:
|
|||||||
add_child(_retry_timer)
|
add_child(_retry_timer)
|
||||||
_retry_timer.timeout.connect(_send_pending)
|
_retry_timer.timeout.connect(_send_pending)
|
||||||
|
|
||||||
## Ordine di priorita': --api=..., poi l'origine della pagina sul Web. Senza nessuno
|
## Ordine di priorita': `--api=<url>` (test), `--offline`/`--no-api` (spegne), origine
|
||||||
## dei due non si usa NIENTE: una build desktop di prova non deve poter inviare
|
## della pagina sul Web, altrimenti il servizio pubblico. La classifica e' quindi
|
||||||
## punteggi alla classifica pubblica (e' successo: righe "Test" in produzione).
|
## abilitata di default e non dipende da configurazione; `--offline` esiste solo per
|
||||||
func _resolve_base() -> String:
|
## le partite di prova che non devono finire nella classifica pubblica.
|
||||||
for argument in OS.get_cmdline_user_args():
|
## `script_run` e' il guard delle suite di test: non e' configurazione utente, la
|
||||||
|
## passa `_resolve_base()` quando il processo gira con `--script`.
|
||||||
|
static func resolve_base(user_args: PackedStringArray, script_run := false) -> String:
|
||||||
|
for argument in user_args:
|
||||||
if argument.begins_with("--api="):
|
if argument.begins_with("--api="):
|
||||||
return argument.substr(6).trim_suffix("/")
|
return argument.substr(6).trim_suffix("/")
|
||||||
|
for argument in user_args:
|
||||||
|
if argument == "--offline" or argument == "--no-api":
|
||||||
|
return ""
|
||||||
if OS.has_feature("web") and ClassDB.class_exists("JavaScriptBridge"):
|
if OS.has_feature("web") and ClassDB.class_exists("JavaScriptBridge"):
|
||||||
var origin := str(JavaScriptBridge.eval("window.location.origin", true))
|
var origin := str(JavaScriptBridge.eval("window.location.origin", true))
|
||||||
if origin.begins_with("http"):
|
if origin.begins_with("http"):
|
||||||
return origin.trim_suffix("/") + API_PATH
|
return origin.trim_suffix("/") + API_PATH
|
||||||
push_warning("Classifica disattivata: usa --api=http://host:porta per indicare il servizio.")
|
if script_run:
|
||||||
return ""
|
return "" # suite di test: mai verso il servizio pubblico
|
||||||
|
return DEFAULT_API_BASE
|
||||||
|
|
||||||
|
## Le suite di test girano come script (`--script ...`): in quel caso la classifica
|
||||||
|
## resta spenta, a meno che il test non abbia passato un suo `--api=`.
|
||||||
|
static func _is_script_run() -> bool:
|
||||||
|
for argument in OS.get_cmdline_args():
|
||||||
|
if argument == "--script" or argument == "-s":
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _resolve_base() -> String:
|
||||||
|
return resolve_base(OS.get_cmdline_user_args(), _is_script_run())
|
||||||
|
|
||||||
## Godot non ammette due richieste insieme sullo stesso nodo: una lettura chiesta
|
## Godot non ammette due richieste insieme sullo stesso nodo: una lettura chiesta
|
||||||
## mentre la precedente e' in volo viene rimandata a fine risposta invece di fallire
|
## mentre la precedente e' in volo viene rimandata a fine risposta invece di fallire
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
extends SceneTree
|
extends SceneTree
|
||||||
## Exact damage regression + visible game identity, no public score submissions.
|
## Exact damage regression + visible game identity. La classifica e' attiva di default
|
||||||
|
## nelle build di gioco; qui, girando come --script, resta spenta.
|
||||||
const Player = preload("res://scripts/player.gd")
|
const Player = preload("res://scripts/player.gd")
|
||||||
const Grenade = preload("res://scripts/grenade.gd")
|
const Grenade = preload("res://scripts/grenade.gd")
|
||||||
const Boss = preload("res://scripts/alien_grunt.gd")
|
const Boss = preload("res://scripts/alien_grunt.gd")
|
||||||
@@ -41,7 +42,8 @@ func run_test() -> void:
|
|||||||
if label.text == "Work-Death Balance":
|
if label.text == "Work-Death Balance":
|
||||||
title_found = true
|
title_found = true
|
||||||
check(title_found, "menu mostra Work-Death Balance")
|
check(title_found, "menu mostra Work-Death Balance")
|
||||||
check(world.leaderboard.disabled, "build di prova non invia punteggi pubblici")
|
check(world.leaderboard.disabled and world.leaderboard.base_url == "",
|
||||||
|
"suite di test: nessun invio alla classifica pubblica")
|
||||||
var zombie = load("res://scripts/zombie.gd").new()
|
var zombie = load("res://scripts/zombie.gd").new()
|
||||||
zombie.player = world.player
|
zombie.player = world.player
|
||||||
zombie.position = Vector3(0, 0.05, -3)
|
zombie.position = Vector3(0, 0.05, -3)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ extends SceneTree
|
|||||||
## (con display, per gli eventi di puntamento: --rendering-method gl_compatibility)
|
## (con display, per gli eventi di puntamento: --rendering-method gl_compatibility)
|
||||||
const Profile = preload("res://scripts/player_profile.gd")
|
const Profile = preload("res://scripts/player_profile.gd")
|
||||||
const StartScreen = preload("res://scripts/start_screen.gd")
|
const StartScreen = preload("res://scripts/start_screen.gd")
|
||||||
|
const Leaderboard = preload("res://scripts/leaderboard.gd")
|
||||||
|
|
||||||
var world: Node3D
|
var world: Node3D
|
||||||
var screen: CanvasLayer
|
var screen: CanvasLayer
|
||||||
@@ -101,10 +102,21 @@ func test_world_menu() -> void:
|
|||||||
or Input.mouse_mode == Input.MOUSE_MODE_CAPTURED),
|
or Input.mouse_mode == Input.MOUSE_MODE_CAPTURED),
|
||||||
"playing=%s mode=%s display=%s" % [str(world.player.playing), str(Input.mouse_mode),
|
"playing=%s mode=%s display=%s" % [str(world.player.playing), str(Input.mouse_mode),
|
||||||
DisplayServer.get_name()])
|
DisplayServer.get_name()])
|
||||||
# Regressione: senza --api= questa e' una build desktop di prova e non deve poter
|
# La classifica e' attiva di default (nessuna configurazione). Il nodo qui e' spento
|
||||||
# inviare punteggi alla classifica pubblica (ci sono finite righe "Test").
|
# perche' questa suite gira come --script: le regole si verificano su resolve_base().
|
||||||
check("senza --api= la build di prova non invia punteggi alla classifica pubblica",
|
check("senza argomenti la classifica e' attiva sul servizio pubblico",
|
||||||
world.leaderboard.disabled == true and world.leaderboard.base_url == "",
|
Leaderboard.resolve_base(PackedStringArray(), false) == Leaderboard.DEFAULT_API_BASE
|
||||||
|
and Leaderboard.DEFAULT_API_BASE == "https://enne2.net/api/zombie-fps",
|
||||||
|
"base=%s" % str(Leaderboard.resolve_base(PackedStringArray(), false)))
|
||||||
|
check("--offline (e --no-api) spengono la classifica per le partite di prova",
|
||||||
|
Leaderboard.resolve_base(PackedStringArray(["--offline"]), false) == ""
|
||||||
|
and Leaderboard.resolve_base(PackedStringArray(["--no-api"]), false) == "")
|
||||||
|
check("--api= vince su --offline e normalizza la barra finale",
|
||||||
|
Leaderboard.resolve_base(PackedStringArray(["--offline", "--api=http://127.0.0.1:8799/"]), false)
|
||||||
|
== "http://127.0.0.1:8799")
|
||||||
|
check("in una suite di test la classifica resta spenta",
|
||||||
|
world.leaderboard.disabled == true and world.leaderboard.base_url == ""
|
||||||
|
and Leaderboard.resolve_base(PackedStringArray(), true) == "",
|
||||||
"base=%s disattivata=%s" % [str(world.leaderboard.base_url), str(world.leaderboard.disabled)])
|
"base=%s disattivata=%s" % [str(world.leaderboard.base_url), str(world.leaderboard.disabled)])
|
||||||
world.queue_free()
|
world.queue_free()
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|||||||
Reference in New Issue
Block a user