Files
pirate-dice/tests/mp_client.gd
2026-09-16 16:15:13 +02:00

148 lines
5.3 KiB
GDScript

extends "res://tests/basis.gd"
## Ein Client. "-- host" macht den Raum auf, "-- join" tritt bei und steigt
## in Runde zwei einmal aus und wieder ein. Beide spielen über die echten
## RPC-Wege. Der Raumcode wandert über eine Datei im Scratch-Verzeichnis.
var game: Node
var rolle := ""
var runden := 0
var zuege := 0
var wuerfe := 0
var genau := 0
var wieder_da := false
var _letzte_phase := ""
var _rng := RandomNumberGenerator.new()
var _code_datei := ""
func _ready() -> void:
_rng.randomize()
var args := OS.get_cmdline_user_args()
rolle = args[0]
_code_datei = args[1]
await get_tree().create_timer(0.5).timeout
_lauf()
func _lauf() -> void:
if rolle == "host":
Net.create_room("Host")
await _warte(func(): return Net.room_code != "", 40.0)
var f := FileAccess.open(_code_datei, FileAccess.WRITE)
f.store_string(Net.room_code)
f.close()
await _warte(func(): return Net.lobby.size() >= 2, 80.0)
Net.request_start(1, 1, 2)
else:
await _beitreten()
await _warte(func(): game = _find(get_tree().root, "Game"); return game != null, 120.0)
if game == null:
_pruef("%s: Spiel kommt an" % rolle, false)
_fertig()
return
var erste := await _warte(func(): return game.phase == "bid" or game.phase == "roll", 60.0)
_pruef("%s: erster Zustand kommt an (Platz %d)" % [rolle, game.local_seat], erste)
var t := 0.0
var gewartet := 0.0
while t < 1400.0 and is_instance_valid(game) and game.phase != "over":
await get_tree().create_timer(0.1).timeout
t += 0.1
if not is_instance_valid(game):
break
if game.phase != _letzte_phase:
_letzte_phase = game.phase
if game.phase == "roll":
runden += 1
# Der Gast geht in Runde zwei einmal raus und kommt zurück.
if rolle == "join" and not wieder_da and runden >= 2 and game.phase == "bid":
await _wiederkommen()
continue
var me: int = game.local_seat
if game.phase != "bid" or game.turn != me or not game.seats[me].alive:
gewartet = 0.0
continue
gewartet += 0.1
if gewartet < 0.2:
continue
var hand: Array = game.hand.get(me, [])
if not hand.is_empty() and (not BoosterDB.needs_bid(hand[0]) or not game.bid.is_empty()) and _rng.randf() < 0.7:
game._on_booster_chosen(hand[0], 0)
wuerfe += 1
await get_tree().create_timer(0.4).timeout
if not is_instance_valid(game) or game.turn != me or game.phase != "bid":
continue
var total := 0
for s in game.seats:
total += int(s.count)
var a: Dictionary = DiceAI.choose_action(game.my_values, total, game.bid, false, _rng)
if a.type == "challenge" and not game.bid.is_empty() and _rng.randf() < 0.3:
a = {"type": "exact"}
match a.type:
"bid":
game._on_bid_pressed(int(a.count), int(a.face))
"challenge":
game._on_challenge_pressed()
"exact":
game._on_exact_pressed()
genau += 1
zuege += 1
gewartet = -1.5
_pruef("%s: Partie endet über RPC (Runden=%d Züge=%d Würfe=%d Genau=%d)" % [rolle, runden, zuege, wuerfe, genau],
is_instance_valid(game) and game.phase == "over")
if rolle == "join":
_pruef("join: Wiedereinstieg hat geklappt", wieder_da)
# Nochmal: der Besitzer bittet darum, beide bekommen eine frische Partie.
var alt: Node = game
await get_tree().create_timer(0.5).timeout
if Net.is_owner:
Net.request_rematch()
var neu := await _warte(func(): game = _find(get_tree().root, "Game"); return game != null and game != alt, 20.0)
var laeuft := false
if neu:
laeuft = await _warte(func(): return game.phase == "bid" or game.phase == "roll", 20.0)
_pruef("%s: Nochmal im Netz bringt eine neue Partie (Platz %d)" % [rolle, game.local_seat if neu else -1],
neu and laeuft and game.local_seat >= 0)
# Kurz bleiben: sonst schliesst der Raum, bevor der Server-Läufer die
# neue Partie überhaupt gesehen hat.
await get_tree().create_timer(1.5).timeout
if is_instance_valid(game):
print("%s: '%s'" % [rolle, game.banner])
for i in game.seats.size():
print("%s: Platz %d %s count=%d alive=%s" % [rolle, i, game.seats[i].name, game.seats[i].count, game.seats[i].alive])
_fertig()
func _beitreten() -> void:
await _warte(func(): return FileAccess.file_exists(_code_datei), 80.0)
await get_tree().create_timer(0.5).timeout
Net.join_room(FileAccess.get_file_as_string(_code_datei).strip_edges(), "Gast")
## Verbindung reisst ab (kein bewusstes Verlassen), frisches Menü, dann der
## Weg des Knopfs "Zurück in Raum XY".
func _wiederkommen() -> void:
var alter_platz: int = game.local_seat
var alte_zuege := zuege
var code := Net.room_code
multiplayer.multiplayer_peer.close()
if Net.in_game:
Net._on_server_disconnected() # der Verlust-Handler, wie bei echtem Abbruch
await get_tree().create_timer(1.0).timeout
_pruef("join: Raum nach Abbruch gemerkt", Net.letzter_raum.get("code", "") == code)
game = null
Net.wieder_rein()
var ok := await _warte(func(): game = _find(get_tree().root, "Game"); return game != null, 60.0)
if not ok:
_pruef("join: nach Rückkehr kommt ein Spiel", false)
wieder_da = true
return
var zustand := await _warte(func(): return game.phase == "bid" or game.phase == "roll" or game.phase == "reveal", 60.0)
_pruef("join: nach Rückkehr kommt der Zustand", zustand)
_pruef("join: alter Platz zurück, kein Bot mehr",
game.local_seat == alter_platz and not game.seats[game.local_seat].ai)
_pruef("join: HUD steht wieder", game.hud != null and game.hud.hand_box != null)
_letzte_phase = ""
wieder_da = true
zuege = alte_zuege