From e54ad1332bca444f24f8f87958f531b7a5bef3a1 Mon Sep 17 00:00:00 2001 From: Voyager Date: Thu, 13 Aug 2026 22:20:43 -0400 Subject: [PATCH 1/2] Fix rain coverage and in-game session switching --- fishing/fishing_spot.gd | 9 +++------ main/main.gd | 5 +++++ network/discovery_client.gd | 26 +++++++++++++++++++++++++- network/network_fishing_service.gd | 2 +- tests/fishing_authority_validation.gd | 11 ++++++++++- tests/job_system_validation.gd | 24 +++++++++++++++++++++++- tests/world_weather_validation.gd | 6 ++++++ world/world_time_visual_controller.gd | 13 ++++++++----- 8 files changed, 81 insertions(+), 15 deletions(-) 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 From 5d7c3d7459e6872e85070624b7c3eb5232dd7327 Mon Sep 17 00:00:00 2001 From: Voyager Date: Thu, 13 Aug 2026 22:25:55 -0400 Subject: [PATCH 2/2] Prepare v0.7.1-alpha release --- docs/README-PLAYTEST.txt | 4 ++-- export_presets.cfg | 26 +++++++++++++------------- project.godot | 2 +- scripts/build_playtest.sh | 6 +++--- ui/title_screen.tscn | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/README-PLAYTEST.txt b/docs/README-PLAYTEST.txt index a73faf0..9d895f1 100644 --- a/docs/README-PLAYTEST.txt +++ b/docs/README-PLAYTEST.txt @@ -1,6 +1,6 @@ NETfishing -v0.6.8-alpha -Alpha 0.6.8 +v0.7.1-alpha +Alpha 0.7.1 Thank you for trying this early private playtest. diff --git a/export_presets.cfg b/export_presets.cfg index cb2bb67..bbcdec2 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -9,7 +9,7 @@ custom_features="" export_filter="all_resources" include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" exclude_filter="builds/*,scripts/*" -export_path="builds/v0.6.8-alpha/windows-x86_64/NETfishing.exe" +export_path="builds/v0.7.1-alpha/windows-x86_64/NETfishing.exe" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -35,11 +35,11 @@ application/modify_resources=true application/icon="res://art/exported/system_icons/netfishing.ico" application/console_wrapper_icon="" application/icon_interpolation=4 -application/file_version="0.6.8.0" -application/product_version="0.6.8.0" +application/file_version="0.7.1.0" +application/product_version="0.7.1.0" application/company_name="Woofmeow" application/product_name="NETfishing" -application/file_description="NETfishing v0.6.8-alpha" +application/file_description="NETfishing v0.7.1-alpha" application/copyright="Copyright © 2026 Woofmeow" application/trademarks="NETfishing and Woofmeow branding is reserved; see TRADEMARKS.md" application/export_angle=0 @@ -62,7 +62,7 @@ custom_features="" export_filter="all_resources" include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" exclude_filter="builds/*,scripts/*" -export_path="builds/v0.6.8-alpha/linux-arm64/NETfishing.arm64" +export_path="builds/v0.7.1-alpha/linux-arm64/NETfishing.arm64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -92,7 +92,7 @@ custom_features="" export_filter="all_resources" include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" exclude_filter="builds/*,scripts/*" -export_path="builds/v0.6.8-alpha/macos/NETfishing.zip" +export_path="builds/v0.7.1-alpha/macos/NETfishing.zip" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -108,8 +108,8 @@ custom_template/release="" application/bundle_identifier="io.woofmeow.netfishing" application/icon="res://art/exported/system_icons/netfishing_1024.png" application/icon_interpolation=0 -application/short_version="0.6.8" -application/version="0.6.8" +application/short_version="0.7.1" +application/version="0.7.1" application/architecture="universal" codesign/enable=false notarization/enable=false @@ -125,7 +125,7 @@ custom_features="" export_filter="all_resources" include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" exclude_filter="builds/*,scripts/*,tests/*" -export_path="builds/v0.6.8-alpha/server-linux-x86_64/NETfishingServer.x86_64" +export_path="builds/v0.7.1-alpha/server-linux-x86_64/NETfishingServer.x86_64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -155,7 +155,7 @@ custom_features="" export_filter="all_resources" include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" exclude_filter="builds/*,scripts/*" -export_path="builds/v0.6.8-alpha/android/NETfishing.apk" +export_path="builds/v0.7.1-alpha/android/NETfishing.apk" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" @@ -176,8 +176,8 @@ architectures/armeabi-v7a=false architectures/arm64-v8a=true architectures/x86=false architectures/x86_64=false -version/code=60800 -version/name="v0.6.8-alpha" +version/code=70100 +version/name="v0.7.1-alpha" package/unique_name="io.woofmeow.netfishing" package/name="NETfishing" package/signed=true @@ -214,7 +214,7 @@ custom_features="" export_filter="all_resources" include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" exclude_filter="builds/*,scripts/*" -export_path="builds/v0.6.8-alpha/linux-x86_64/NETfishing.x86_64" +export_path="builds/v0.7.1-alpha/linux-x86_64/NETfishing.x86_64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" diff --git a/project.godot b/project.godot index 6975e3b..1745ecc 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="NETFISHING" -config/version="0.6.8-alpha" +config/version="0.7.1-alpha" run/main_scene="res://main/main.tscn" config/features=PackedStringArray("4.7", "GL Compatibility") config/icon="res://art/exported/system_icons/netfishing_256.png" diff --git a/scripts/build_playtest.sh b/scripts/build_playtest.sh index 980d4c4..7749b3d 100755 --- a/scripts/build_playtest.sh +++ b/scripts/build_playtest.sh @@ -4,14 +4,14 @@ set -euo pipefail readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" -readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.8-alpha" +readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.7.1-alpha" readonly WINDOWS_DIR="${BUILD_ROOT}/windows-x86_64" readonly LINUX_DIR="${BUILD_ROOT}/linux-x86_64" readonly README_SOURCE="${PROJECT_ROOT}/docs/README-PLAYTEST.txt" readonly SOURCE_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)" readonly SOURCE_URL="https://forge.makearmy.io/woofmeow/netfishing" -readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.6.8-alpha-windows-x86_64.zip" -readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.6.8-alpha-linux-x86_64.zip" +readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.7.1-alpha-windows-x86_64.zip" +readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.7.1-alpha-linux-x86_64.zip" readonly GODOT_BIN="${GODOT_BIN:-godot}" if [[ ! -f "${PROJECT_ROOT}/project.godot" ]]; then diff --git a/ui/title_screen.tscn b/ui/title_screen.tscn index c79e894..ae0c0c6 100644 --- a/ui/title_screen.tscn +++ b/ui/title_screen.tscn @@ -221,7 +221,7 @@ unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1) theme_override_font_sizes/font_size = 22 -text = "v0.6.8-alpha" +text = "v0.7.1-alpha" horizontal_alignment = 1 [node name="Spacer" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"]