M1c: Gleichschritt im Netz #9
+33
-5
@@ -57,6 +57,9 @@ var _save_blocked := false
|
|||||||
var _figure_wait := 0.0
|
var _figure_wait := 0.0
|
||||||
## The address a guest is joining, for messages.
|
## The address a guest is joining, for messages.
|
||||||
var _joining := ""
|
var _joining := ""
|
||||||
|
## The host's accepted guests when _process last looked. A new one ends a
|
||||||
|
## pause.
|
||||||
|
var _known_guests := 0
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@@ -233,12 +236,37 @@ func _leave(message: String) -> void:
|
|||||||
_show_menu(message)
|
_show_menu(message)
|
||||||
|
|
||||||
|
|
||||||
|
## A broken session runs no more ticks. Instead of leaving the others
|
||||||
|
## waiting, a host closes its world to guests and a guest leaves.
|
||||||
|
func _stop_sharing() -> void:
|
||||||
|
if not session.is_host():
|
||||||
|
_leave(tr("SIMULATION_STOPPED"))
|
||||||
|
return
|
||||||
|
link.link.close()
|
||||||
|
link = null
|
||||||
|
figures.clear()
|
||||||
|
_update_status()
|
||||||
|
|
||||||
|
|
||||||
|
## Ends a pause once the host accepted a guest: the guest joins in the next
|
||||||
|
## tick, and a paused world would keep it waiting. A connection that has not
|
||||||
|
## greeted yet is no guest.
|
||||||
|
func _end_pause_for_new_guests() -> void:
|
||||||
|
var guests := session.guest_count()
|
||||||
|
if guests > _known_guests and clock.paused:
|
||||||
|
_set_paused(false)
|
||||||
|
_known_guests = guests
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if link != null:
|
if link != null:
|
||||||
for notice in link.pump(delta):
|
for notice in link.pump(delta):
|
||||||
_on_notice(notice)
|
_on_notice(notice)
|
||||||
|
if session != null and session.is_broken() and link != null:
|
||||||
|
_stop_sharing()
|
||||||
if session == null or not session.is_running() or session.is_broken():
|
if session == null or not session.is_running() or session.is_broken():
|
||||||
return
|
return
|
||||||
|
_end_pause_for_new_guests()
|
||||||
_update_marker()
|
_update_marker()
|
||||||
if Input.is_action_pressed("dig") and not clock.paused:
|
if Input.is_action_pressed("dig") and not clock.paused:
|
||||||
_try_dig()
|
_try_dig()
|
||||||
@@ -257,11 +285,10 @@ func _process(delta: float) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _on_notice(notice: Dictionary) -> void:
|
func _on_notice(notice: Dictionary) -> void:
|
||||||
|
# An earlier notice of the same batch may have ended the session.
|
||||||
|
if session == null:
|
||||||
|
return
|
||||||
match notice["type"]:
|
match notice["type"]:
|
||||||
"guest_arrived":
|
|
||||||
# Pausing is for playing alone; a guest ends the pause.
|
|
||||||
if clock.paused:
|
|
||||||
_set_paused(false)
|
|
||||||
"world_received":
|
"world_received":
|
||||||
if notice["resync"]:
|
if notice["resync"]:
|
||||||
resyncs += 1
|
resyncs += 1
|
||||||
@@ -304,7 +331,8 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
elif event.is_action_pressed("load_game"):
|
elif event.is_action_pressed("load_game"):
|
||||||
_reload()
|
_reload()
|
||||||
elif event.is_action_pressed("toggle_pause"):
|
elif event.is_action_pressed("toggle_pause"):
|
||||||
if session.is_host() and session.guest_count() == 0:
|
# Ending a pause is always allowed, starting one only alone.
|
||||||
|
if clock.paused or (session.is_host() and session.guest_count() == 0):
|
||||||
_set_paused(not clock.paused)
|
_set_paused(not clock.paused)
|
||||||
else:
|
else:
|
||||||
hud.flash("PAUSE_ONLY_ALONE")
|
hud.flash("PAUSE_ONLY_ALONE")
|
||||||
|
|||||||
@@ -268,6 +268,34 @@ func test_only_the_host_saves_and_only_alone_one_may_pause_or_load() -> void:
|
|||||||
_clean_up([host_dir, guest_dir])
|
_clean_up([host_dir, guest_dir])
|
||||||
|
|
||||||
|
|
||||||
|
func test_an_accepted_guest_ends_a_pause_and_a_bare_connection_does_not() -> void:
|
||||||
|
var host_dir := TestFiles.temporary_dir("host")
|
||||||
|
var guest_dir := TestFiles.temporary_dir("guest")
|
||||||
|
var host := _start_host(host_dir)
|
||||||
|
_press(host, "toggle_pause")
|
||||||
|
check(host.clock.paused, "the host pauses while alone")
|
||||||
|
|
||||||
|
# A connection that never greets is no guest.
|
||||||
|
var stranger := NetLink.new()
|
||||||
|
stranger.join("127.0.0.1", host.link.link.local_port())
|
||||||
|
var seen := func() -> bool:
|
||||||
|
stranger.poll(1.0 / 60.0)
|
||||||
|
return host.link.link._peers.size() == 1
|
||||||
|
check(await _wait_until(seen), "the host sees a bare connection")
|
||||||
|
check(host.clock.paused, "which leaves the pause alone")
|
||||||
|
|
||||||
|
var guest := _start_guest(guest_dir, host)
|
||||||
|
check(await _wait_until(_joined.bind(guest)), "a guest joins the paused world")
|
||||||
|
check(not host.clock.paused, "and ends the pause")
|
||||||
|
|
||||||
|
# Should a pause ever meet a guest, the host can still end it.
|
||||||
|
host._set_paused(true)
|
||||||
|
_press(host, "toggle_pause")
|
||||||
|
check(not host.clock.paused, "the host can always end a pause")
|
||||||
|
stranger.close()
|
||||||
|
_clean_up([host_dir, guest_dir])
|
||||||
|
|
||||||
|
|
||||||
func test_the_host_sees_a_guest_leave() -> void:
|
func test_the_host_sees_a_guest_leave() -> void:
|
||||||
var host_dir := TestFiles.temporary_dir("host")
|
var host_dir := TestFiles.temporary_dir("host")
|
||||||
var guest_dir := TestFiles.temporary_dir("guest")
|
var guest_dir := TestFiles.temporary_dir("guest")
|
||||||
@@ -295,6 +323,36 @@ func test_a_guest_goes_back_to_the_menu_when_the_host_closes() -> void:
|
|||||||
_clean_up([host_dir, guest_dir])
|
_clean_up([host_dir, guest_dir])
|
||||||
|
|
||||||
|
|
||||||
|
func test_a_host_that_stops_sharing_lets_its_guests_go() -> void:
|
||||||
|
var host_dir := TestFiles.temporary_dir("host")
|
||||||
|
var guest_dir := TestFiles.temporary_dir("guest")
|
||||||
|
var host := _start_host(host_dir)
|
||||||
|
var guest := _start_guest(guest_dir, host)
|
||||||
|
await _wait_until(_joined.bind(guest))
|
||||||
|
# An internal error of the core cannot be caused from here. For a broken
|
||||||
|
# session, _process calls what this test calls.
|
||||||
|
host._stop_sharing()
|
||||||
|
check(host.link == null, "the host no longer listens")
|
||||||
|
check_eq(host.hud.status_text(), "", "and no longer says that it does")
|
||||||
|
check(await _wait_until(func() -> bool: return guest.session == null), "the guest's session ends")
|
||||||
|
check_eq(guest.menu.status(), tr("HOST_LEFT"), "and the guest learns why")
|
||||||
|
_clean_up([host_dir, guest_dir])
|
||||||
|
|
||||||
|
|
||||||
|
func test_a_guest_that_stops_sharing_leaves() -> void:
|
||||||
|
var host_dir := TestFiles.temporary_dir("host")
|
||||||
|
var guest_dir := TestFiles.temporary_dir("guest")
|
||||||
|
var host := _start_host(host_dir)
|
||||||
|
var guest := _start_guest(guest_dir, host)
|
||||||
|
await _wait_until(_joined.bind(guest))
|
||||||
|
# As above, for a guest whose own core stopped.
|
||||||
|
guest._stop_sharing()
|
||||||
|
check(guest.session == null, "the guest's session ends")
|
||||||
|
check_eq(guest.menu.status(), tr("SIMULATION_STOPPED"), "the menu says why")
|
||||||
|
check(await _wait_until(func() -> bool: return host.hud.message() == tr("PLAYER_LEFT") % "Grace"), "the host sees the guest go")
|
||||||
|
_clean_up([host_dir, guest_dir])
|
||||||
|
|
||||||
|
|
||||||
func test_a_guest_that_is_turned_away_learns_why() -> void:
|
func test_a_guest_that_is_turned_away_learns_why() -> void:
|
||||||
var host_dir := TestFiles.temporary_dir("host")
|
var host_dir := TestFiles.temporary_dir("host")
|
||||||
var host := _start_host(host_dir)
|
var host := _start_host(host_dir)
|
||||||
@@ -305,6 +363,17 @@ func test_a_guest_that_is_turned_away_learns_why() -> void:
|
|||||||
_clean_up([host_dir])
|
_clean_up([host_dir])
|
||||||
|
|
||||||
|
|
||||||
|
func test_notices_after_the_end_of_a_session_are_ignored() -> void:
|
||||||
|
var directory := TestFiles.temporary_dir("late")
|
||||||
|
var main := _start_main(directory)
|
||||||
|
# In one batch of notices, an earlier one may have ended the session.
|
||||||
|
main._on_notice({"type": "world_received", "resync": true})
|
||||||
|
main._on_notice({"type": "protocol_error"})
|
||||||
|
check(main.session == null, "no session")
|
||||||
|
check(main.menu.visible, "the menu stays")
|
||||||
|
_clean_up([directory])
|
||||||
|
|
||||||
|
|
||||||
func test_a_mistyped_address_is_reported() -> void:
|
func test_a_mistyped_address_is_reported() -> void:
|
||||||
var directory := TestFiles.temporary_dir("typo")
|
var directory := TestFiles.temporary_dir("typo")
|
||||||
var main := _start_main(directory)
|
var main := _start_main(directory)
|
||||||
|
|||||||
Reference in New Issue
Block a user