diff --git a/fishing/fishing_spot.gd b/fishing/fishing_spot.gd index 243a069..167a08b 100644 --- a/fishing/fishing_spot.gd +++ b/fishing/fishing_spot.gd @@ -868,7 +868,7 @@ func _on_cast_completed() -> void: _cast_origin_position.z + _cast_direction.z * withdrawal_cancel_distance ) _presentation.set_line_mode(FishingPresentationType.LineMode.SLACK) - status_changed.emit("waiting for a bite...") + status_changed.emit("") func _consume_active_bait() -> void: @@ -1131,10 +1131,7 @@ func _on_catch_escaped() -> void: return _stop_fight_audio() - var fish_name: String = "the fish" - if _selected_fish != null and not _selected_fish.display_name.is_empty(): - fish_name = "the %s" % _selected_fish.display_name - _cleanup_attempt("%s got away!" % fish_name, &"escape") + _cleanup_attempt("", &"escape") func _on_outcome_completed(outcome: StringName) -> void: @@ -1597,7 +1594,7 @@ func _on_network_cast_accepted( _network_input_resend_elapsed = 0.0 _presentation.show_withdrawal_position(_bobber_water_position) _presentation.set_line_mode(FishingPresentationType.LineMode.SLACK) - status_changed.emit("waiting for a bite...") + status_changed.emit("") func _on_network_cast_rejected(message: String) -> void: diff --git a/main/main.gd b/main/main.gd index 51929a0..af27fed 100644 --- a/main/main.gd +++ b/main/main.gd @@ -1555,8 +1555,13 @@ func _on_pause_join_game_requested(endpoint: String) -> void: _join_requested_from_title = false _pending_join_endpoint = endpoint _game_ui.get_pause_menu().close_for_title_transition() + var preserved_public_join: bool = ( + _discovery.preserve_public_join_for_session_switch() + ) _network_session.disconnect_session("Connecting to another game.") if not _network_session.join_direct(endpoint): + if preserved_public_join: + _discovery.cancel_pending_public_join() _handle_failed_session_switch( "Could not begin the direct connection." ) diff --git a/network/discovery_client.gd b/network/discovery_client.gd index 476cff4..bf15bbf 100644 --- a/network/discovery_client.gd +++ b/network/discovery_client.gd @@ -85,6 +85,7 @@ var _join_request_in_flight: bool = false var _pending_join_endpoint: String = "" var _pending_join_token: String = "" var _pending_join_room_id: String = "" +var _preserve_pending_join_on_inactive: bool = false var _upnp_thread: Thread var _upnp_mapping_in_progress: bool = false var _upnp_operation_is_renewal: bool = false @@ -291,6 +292,17 @@ func is_public_join_preparing() -> bool: return _join_request_in_flight +func preserve_public_join_for_session_switch() -> bool: + _preserve_pending_join_on_inactive = not _pending_join_token.is_empty() + return _preserve_pending_join_on_inactive + + +func cancel_pending_public_join() -> void: + _preserve_pending_join_on_inactive = false + _set_public_join_state(PublicJoinState.IDLE) + _clear_pending_join() + + func prepare_public_join(room: Dictionary) -> bool: if _join_request_in_flight: return false @@ -913,18 +925,30 @@ func _discovery_version_mismatch_message(required_version: String) -> String: func _on_session_state_changed(state: NetworkSession.State) -> void: if state == NetworkSession.State.CONNECTING and not _pending_join_token.is_empty(): + _preserve_pending_join_on_inactive = false _set_public_join_state(PublicJoinState.CONNECTING) _join_probe_timer.start() call_deferred("_send_pending_join_probe") elif state in [ NetworkSession.State.JOINED_CLIENT, NetworkSession.State.SERVER_LOST, - NetworkSession.State.INACTIVE, ]: + _preserve_pending_join_on_inactive = false _set_public_join_state(PublicJoinState.IDLE) _join_probe_timer.stop() _clear_pending_join() + elif state == NetworkSession.State.INACTIVE: + if ( + _preserve_pending_join_on_inactive + and not _pending_join_token.is_empty() + ): + _preserve_pending_join_on_inactive = false + else: + _set_public_join_state(PublicJoinState.IDLE) + _join_probe_timer.stop() + _clear_pending_join() elif state == NetworkSession.State.CONNECTION_FAILED: + _preserve_pending_join_on_inactive = false _set_public_join_state(PublicJoinState.ERROR) _join_probe_timer.stop() _clear_pending_join() diff --git a/network/network_fishing_service.gd b/network/network_fishing_service.gd index a5a2154..2015681 100644 --- a/network/network_fishing_service.gd +++ b/network/network_fishing_service.gd @@ -953,7 +953,7 @@ func _on_attempt_escaped(peer_id: int) -> void: if attempt == null: return attempt.phase = NetworkFishingAttempt.Phase.ESCAPED - _broadcast_public_outcome(attempt, &"escape", "The fish got away!") + _broadcast_public_outcome(attempt, &"escape", "") _dispose_attempt(peer_id) diff --git a/tests/fishing_authority_validation.gd b/tests/fishing_authority_validation.gd index 20f02e1..6b0429d 100644 --- a/tests/fishing_authority_validation.gd +++ b/tests/fishing_authority_validation.gd @@ -31,6 +31,9 @@ func _run() -> void: "%NetworkFishingService" ) as NetworkFishingService var fishing_spot := main.get_node("%FishingSpot") as FishingSpotType + var game_ui := main.get_node("UIPresentation/UIViewport/GameUI") + var fishing_status := game_ui.get_node("%StatusLabel") as Label + var fishing_panel := game_ui.get_node("%FishingPanel") as PanelContainer var player := main.get("_player") as Player assert(session.is_host()) assert(not session.is_open_host()) @@ -71,6 +74,9 @@ func _run() -> void: await process_frame assert(fishing_spot.state == FishingSpotType.FishingState.WAITING_FOR_BITE) assert(service.has_local_attempt()) + assert(fishing_status.text.is_empty()) + assert(not fishing_status.visible) + assert(not fishing_panel.visible) var attempts: Dictionary = service.get("_attempts") var attempt: NetworkFishingAttempt = attempts.get(session.get_local_peer_id()) assert(attempt != null) @@ -164,9 +170,12 @@ func _run() -> void: <= FishQuality.BARRIER_HEALTH_MAXIMUMS[fish_catch.quality] ) reference_controller.queue_free() - service.call("_cancel_attempt", session.get_local_peer_id(), "") + service.call("_on_attempt_escaped", session.get_local_peer_id()) await process_frame assert(not service.has_local_attempt()) + assert(fishing_status.text.is_empty()) + assert(not fishing_status.visible) + assert(not fishing_panel.visible) print("Fishing authority validation: PASS") session.disconnect_session("") diff --git a/tests/job_system_validation.gd b/tests/job_system_validation.gd index 32f6c05..32fe10e 100644 --- a/tests/job_system_validation.gd +++ b/tests/job_system_validation.gd @@ -206,7 +206,7 @@ func _run() -> void: assert(int(save_data.get("save_version", -1)) == 7) assert(PlayerJobService.validate_save_data(save_data.get("jobs", {}))) - session.disconnect_session("Job system validation complete.") + _validate_pause_session_switch(main, session) main.queue_free() for _frame: int in 4: await process_frame @@ -215,6 +215,28 @@ func _run() -> void: quit() +func _validate_pause_session_switch( + main: Node, + session: NetworkSession, +) -> void: + var discovery := main.get_node("%DiscoveryClient") as DiscoveryClient + discovery.set("_pending_join_endpoint", "127.0.0.1:18141") + discovery.set("_pending_join_room_id", "session-switch-room") + discovery.set("_pending_join_token", "session-switch-token") + discovery.call( + "_set_public_join_state", + DiscoveryClient.PublicJoinState.CONNECTING, + ) + main.call("_on_pause_join_game_requested", "127.0.0.1:18141") + assert(session.state == NetworkSession.State.CONNECTING) + assert(discovery.get("_pending_join_token") == "session-switch-token") + var join_probe_timer := discovery.get("_join_probe_timer") as Timer + assert(join_probe_timer != null and not join_probe_timer.is_stopped()) + session.cancel_connection() + assert(session.state == NetworkSession.State.INACTIVE) + assert(str(discovery.get("_pending_join_token")).is_empty()) + + func _validate_unavailable_fishnet_layout() -> void: var unavailable_page := TheNetPage.new() unavailable_page.size = Vector2(1280.0, 720.0) diff --git a/tests/world_weather_validation.gd b/tests/world_weather_validation.gd index 01ac8e1..a725117 100644 --- a/tests/world_weather_validation.gd +++ b/tests/world_weather_validation.gd @@ -456,8 +456,14 @@ func _validate_weather_presentation() -> void: assert(rain.emitting) assert(rain.amount_ratio > 0.99) assert(rain.amount == WorldTimeVisualControllerType.RAIN_PARTICLE_AMOUNT) + assert(rain.visibility_aabb == ( + WorldTimeVisualControllerType.RAIN_VISIBILITY_AABB + )) var rain_material := rain.process_material as ParticleProcessMaterial assert(rain_material != null) + assert(rain_material.emission_box_extents.is_equal_approx( + WorldTimeVisualControllerType.RAIN_EMISSION_EXTENTS + )) assert(is_equal_approx( rain_material.initial_velocity_min, WorldTimeVisualControllerType.RAIN_VELOCITY_MIN, diff --git a/world/world_time_visual_controller.gd b/world/world_time_visual_controller.gd index 231bbfd..99b0429 100644 --- a/world/world_time_visual_controller.gd +++ b/world/world_time_visual_controller.gd @@ -5,7 +5,12 @@ const UPDATE_INTERVAL_SECONDS: float = 0.1 const SUN_YAW_DEGREES: float = -32.0 const WEATHER_TRANSITION_SECONDS: float = 10.0 const RAIN_EMITTER_OFFSET := Vector3(0.0, 7.0, 0.0) -const RAIN_PARTICLE_AMOUNT: int = 560 +const RAIN_EMISSION_EXTENTS := Vector3(13.0, 1.0, 13.0) +const RAIN_VISIBILITY_AABB := AABB( + Vector3(-13.5, -9.0, -13.5), + Vector3(27.0, 12.0, 27.0), +) +const RAIN_PARTICLE_AMOUNT: int = 2240 const RAIN_VELOCITY_MIN: float = 16.0 const RAIN_VELOCITY_MAX: float = 20.0 const RAIN_DROP_SIZE := Vector3(0.014, 0.34, 0.014) @@ -187,14 +192,12 @@ func _prepare_rain() -> void: _rain.lifetime = 1.25 _rain.fixed_fps = 30 _rain.local_coords = false - _rain.visibility_aabb = AABB( - Vector3(-7.0, -9.0, -7.0), Vector3(14.0, 12.0, 14.0) - ) + _rain.visibility_aabb = RAIN_VISIBILITY_AABB var process_material := ParticleProcessMaterial.new() process_material.emission_shape = ( ParticleProcessMaterial.EMISSION_SHAPE_BOX ) - process_material.emission_box_extents = Vector3(6.5, 1.0, 6.5) + process_material.emission_box_extents = RAIN_EMISSION_EXTENTS process_material.direction = Vector3.DOWN process_material.spread = 5.0 process_material.initial_velocity_min = RAIN_VELOCITY_MIN