diff --git a/docs/README-PLAYTEST.txt b/docs/README-PLAYTEST.txt index 258ea6a..a73faf0 100644 --- a/docs/README-PLAYTEST.txt +++ b/docs/README-PLAYTEST.txt @@ -1,6 +1,6 @@ NETfishing -v0.6.7-alpha -Alpha 0.6.7 +v0.6.8-alpha +Alpha 0.6.8 Thank you for trying this early private playtest. diff --git a/export_presets.cfg b/export_presets.cfg index ff8f39e..cb2bb67 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.7-alpha/windows-x86_64/NETfishing.exe" +export_path="builds/v0.6.8-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.7.0" -application/product_version="0.6.7.0" +application/file_version="0.6.8.0" +application/product_version="0.6.8.0" application/company_name="Woofmeow" application/product_name="NETfishing" -application/file_description="NETfishing v0.6.7-alpha" +application/file_description="NETfishing v0.6.8-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.7-alpha/linux-arm64/NETfishing.arm64" +export_path="builds/v0.6.8-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.7-alpha/macos/NETfishing.zip" +export_path="builds/v0.6.8-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.7" -application/version="0.6.7" +application/short_version="0.6.8" +application/version="0.6.8" 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.7-alpha/server-linux-x86_64/NETfishingServer.x86_64" +export_path="builds/v0.6.8-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.7-alpha/android/NETfishing.apk" +export_path="builds/v0.6.8-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=60700 -version/name="v0.6.7-alpha" +version/code=60800 +version/name="v0.6.8-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.7-alpha/linux-x86_64/NETfishing.x86_64" +export_path="builds/v0.6.8-alpha/linux-x86_64/NETfishing.x86_64" patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" diff --git a/main/main.gd b/main/main.gd index 589099a..51929a0 100644 --- a/main/main.gd +++ b/main/main.gd @@ -1078,6 +1078,8 @@ func _confirm_server_trust() -> void: _server_trust_dialog.ok_button_text = "Replace Pin" _server_trust_dialog.popup_centered(Vector2i(520, 260)) return + if _server_trust_dialog != null: + _server_trust_dialog.hide() _network_session.resolve_server_trust(true) diff --git a/project.godot b/project.godot index b2e2d38..6975e3b 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="NETFISHING" -config/version="0.6.7-alpha" +config/version="0.6.8-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 eaf1106..980d4c4 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.7-alpha" +readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.8-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.7-alpha-windows-x86_64.zip" -readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.6.7-alpha-linux-x86_64.zip" +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 GODOT_BIN="${GODOT_BIN:-godot}" if [[ ! -f "${PROJECT_ROOT}/project.godot" ]]; then diff --git a/scripts/run_validations.sh b/scripts/run_validations.sh index 080bb3d..f7f77f3 100755 --- a/scripts/run_validations.sh +++ b/scripts/run_validations.sh @@ -84,6 +84,37 @@ run_test() { --headless --path "${PROJECT_ROOT}" --script "${script}" } + +wait_for_network_host() { + local script="$1" + local host_pid="$2" + local test_port="7777" + local declared_port + declared_port="$( + sed -n -E \ + 's/^const TEST_PORT(: int)? = ([0-9]+)$/\2/p' \ + "${PROJECT_ROOT}/${script}" + )" + if [[ -n "${declared_port}" ]]; then + test_port="${declared_port}" + fi + if ! command -v ss >/dev/null 2>&1; then + sleep 2 + return 0 + fi + for _attempt in {1..100}; do + if ! kill -0 "${host_pid}" 2>/dev/null; then + return 1 + fi + if ss -H -lun "sport = :${test_port}" | grep -q .; then + return 0 + fi + sleep 0.1 + done + return 1 +} + + run_network_test() { local script="$1" local name="${script#tests/}" @@ -99,7 +130,17 @@ run_network_test() { --headless --path "${PROJECT_ROOT}" --script "${script}" -- host \ >"${host_root}/output.log" 2>&1 & host_pid=$! - sleep 0.75 + if ! wait_for_network_host "${script}" "${host_pid}"; then + set +e + wait "${host_pid}" + host_status=$? + set -e + printf '%s\n' '-- host output --' + sed -n '1,240p' "${host_root}/output.log" + printf 'error: host did not become ready (exit %d)\n' \ + "${host_status}" >&2 + return 1 + fi set +e XDG_DATA_HOME="${client_root}/data" \ XDG_CONFIG_HOME="${client_root}/config" \ diff --git a/tests/dedicated_server_config_validation.gd b/tests/dedicated_server_config_validation.gd index 2da24a3..6fef7c6 100644 --- a/tests/dedicated_server_config_validation.gd +++ b/tests/dedicated_server_config_validation.gd @@ -168,14 +168,14 @@ func _run() -> void: "error": { "code": "game_version_mismatch", "message": "generic mismatch", - "required_game_version": "0.6.8-alpha", + "required_game_version": "99.0.0-alpha", }, } var outdated_message: String = str( discovery.call("_request_failure", outdated_discovery_error) ) assert("out of date" in outdated_message) - assert("0.6.8-alpha" in outdated_message) + assert("99.0.0-alpha" in outdated_message) assert("will not be listed" in outdated_message) discovery.set( "_host_request_kind", @@ -196,7 +196,7 @@ func _run() -> void: "error": { "code": "game_version_mismatch", "message": "generic mismatch", - "required_game_version": "0.6.6-alpha", + "required_game_version": "0.0.0-alpha", }, } var newer_message: String = str( diff --git a/tests/economy_regression_validation.gd b/tests/economy_regression_validation.gd index 846da46..51ba48d 100644 --- a/tests/economy_regression_validation.gd +++ b/tests/economy_regression_validation.gd @@ -84,6 +84,7 @@ func _run() -> void: main.queue_free() for _frame: int in 4: await process_frame + await create_timer(0.1).timeout quit() @@ -97,7 +98,10 @@ func _run_multiplayer_host() -> void: var session := main.get_node("%NetworkSession") as NetworkSession assert(session.set_host_open(true)) var client_connected := false - var connection_deadline: int = Time.get_ticks_msec() + 30000 + # Loading the full client world can take longer on a cold import. Keep the + # host available until the client has completed authentication rather than + # timing out while the client is still starting. + var connection_deadline: int = Time.get_ticks_msec() + 60000 while Time.get_ticks_msec() < connection_deadline: await process_frame if session.get_authenticated_peer_ids().size() >= 2: diff --git a/tests/fishing_multiplayer_validation.gd b/tests/fishing_multiplayer_validation.gd index f6a8b40..f0976f8 100644 --- a/tests/fishing_multiplayer_validation.gd +++ b/tests/fishing_multiplayer_validation.gd @@ -32,7 +32,7 @@ func _run_host() -> void: "%NetworkFishingService" ) as NetworkFishingService - var join_deadline: int = Time.get_ticks_msec() + 20000 + var join_deadline: int = Time.get_ticks_msec() + 60000 while ( Time.get_ticks_msec() < join_deadline and session.get_authenticated_peer_ids().size() < 2 @@ -55,7 +55,7 @@ func _run_host() -> void: var remote_visuals := remote_avatar.get_node("Visuals") as Node3D remote_visuals.rotation.y = PI * 0.5 session.publish_authoritative_teleport(remote_peer_id) - var aiming_deadline: int = Time.get_ticks_msec() + 5000 + var aiming_deadline: int = Time.get_ticks_msec() + 15000 while ( Time.get_ticks_msec() < aiming_deadline and int(remote_avatar.get("_fishing_visual_phase")) @@ -110,7 +110,7 @@ func _run_host() -> void: Player.FishingVisualPhase.FISHING, ] ) - var sitting_deadline: int = Time.get_ticks_msec() + 3000 + var sitting_deadline: int = Time.get_ticks_msec() + 10000 while Time.get_ticks_msec() < sitting_deadline and not remote_avatar.is_sitting(): await process_frame assert(remote_avatar.is_sitting()) @@ -139,6 +139,12 @@ func _run_host() -> void: int(remote_avatar.get("_fishing_visual_phase")) == Player.FishingVisualPhase.RETRACT ) + var retract_animation_deadline: int = Time.get_ticks_msec() + 3000 + while ( + Time.get_ticks_msec() < retract_animation_deadline + and remote_animation_player.current_animation != &"retract_sit" + ): + await process_frame assert(remote_animation_player.current_animation == &"retract_sit") var completion_deadline: int = Time.get_ticks_msec() + 12000 @@ -209,7 +215,7 @@ func _run_client() -> void: for _frame: int in 30: await physics_frame player.set_fishing_visual(false) - player.call("_set_sitting", true) + player.call("_set_sitting", true, true) assert(player.is_sitting()) for _frame: int in 15: await physics_frame @@ -303,6 +309,12 @@ func _run_client() -> void: int(player.get("_fishing_visual_phase")) == Player.FishingVisualPhase.RETRACT ) + var retract_animation_deadline: int = Time.get_ticks_msec() + 3000 + while ( + Time.get_ticks_msec() < retract_animation_deadline + and character_animation_player.current_animation != &"retract_sit" + ): + await process_frame assert(character_animation_player.current_animation == &"retract_sit") var bobber := fishing_spot.get_node( "FishingPresentation/Bobber" diff --git a/tests/fishing_surface_validation.gd b/tests/fishing_surface_validation.gd index 531e0ba..5b52b41 100644 --- a/tests/fishing_surface_validation.gd +++ b/tests/fishing_surface_validation.gd @@ -162,6 +162,34 @@ func _validate_portable_water_body() -> void: func _validate_starter_region_surfaces() -> void: var region := StarterRegionScene.instantiate() as Node3D root.add_child(region) + var ocean_region := region.get_node( + "WaterBodies/Ocean/FishingRegions/OceanFishingRegion" + ) as FishableWaterRegionType + var minimum_x: float = INF + var maximum_x: float = -INF + var minimum_z: float = INF + var maximum_z: float = -INF + for child: Node in ocean_region.get_children(): + var shape_node := child as CollisionShape3D + assert(shape_node != null and shape_node.shape is BoxShape3D) + var box := shape_node.shape as BoxShape3D + assert(is_equal_approx(box.size.y, 2.0)) + minimum_x = minf(minimum_x, shape_node.position.x - box.size.x * 0.5) + maximum_x = maxf(maximum_x, shape_node.position.x + box.size.x * 0.5) + minimum_z = minf(minimum_z, shape_node.position.z - box.size.z * 0.5) + maximum_z = maxf(maximum_z, shape_node.position.z + box.size.z * 0.5) + var ocean_footprint := Vector2( + maximum_x - minimum_x, + maximum_z - minimum_z, + ) + assert(is_equal_approx( + ocean_footprint.x * ocean_footprint.y, + 12840.0, + )) + assert(is_equal_approx( + ocean_footprint.x / 107.0, + ocean_footprint.y / 80.0, + )) var fishing_spot := FishingSpotScene.instantiate() as FishingSpotType root.add_child(fishing_spot) await physics_frame @@ -179,6 +207,14 @@ func _validate_starter_region_surfaces() -> void: ) assert(ocean.is_fishable()) assert(is_equal_approx(ocean.position.y, -0.45)) + var expanded_ocean: FishingSurfaceSampleType = ( + fishing_spot.resolve_fishing_surface( + Vector3(72.0, -0.45, 0.0), + 4.0, + ) + ) + assert(expanded_ocean.is_fishable()) + assert(is_equal_approx(expanded_ocean.position.y, -0.45)) var covered_ocean: FishingSurfaceSampleType = ( fishing_spot.resolve_fishing_surface( Vector3(20.0, -0.45, 0.0), diff --git a/tests/logbook_runtime_validation.gd b/tests/logbook_runtime_validation.gd index 3df30a6..88227aa 100644 --- a/tests/logbook_runtime_validation.gd +++ b/tests/logbook_runtime_validation.gd @@ -97,6 +97,7 @@ func _run() -> void: main.queue_free() for _frame: int in 4: await process_frame + await create_timer(0.1).timeout quit() diff --git a/ui/game_ui.gd b/ui/game_ui.gd index 08717b3..27db6e8 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -1517,7 +1517,9 @@ func _refresh_gameplay_hud_visibility() -> void: and not _shop_open ) _gameplay_transient_hud.visible = show_world_hud - _experience_presentation.visible = show_world_hud + _experience_presentation.visible = ( + _gameplay_ui_enabled and not _gameplay_hud_hidden + ) func set_system_menu_open(is_open: bool) -> void: diff --git a/ui/title_screen.tscn b/ui/title_screen.tscn index 95928f9..c79e894 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.7-alpha" +text = "v0.6.8-alpha" horizontal_alignment = 1 [node name="Spacer" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"] diff --git a/world/generated/shorelines/starter_ocean_shoreline.tres b/world/generated/shorelines/starter_ocean_shoreline.tres index 1092f5e..627b08e 100644 --- a/world/generated/shorelines/starter_ocean_shoreline.tres +++ b/world/generated/shorelines/starter_ocean_shoreline.tres @@ -1,4 +1,4 @@ -[gd_resource type="ArrayMesh" format=4] +[gd_resource type="ArrayMesh" format=4 uid="uid://dmgps6rhbdjlw"] [resource] _surfaces = [{ diff --git a/world/regions/starter_island_region.tscn b/world/regions/starter_island_region.tscn index 8ac2484..d956feb 100644 --- a/world/regions/starter_island_region.tscn +++ b/world/regions/starter_island_region.tscn @@ -37,23 +37,23 @@ size = Vector2(10000, 10000) [sub_resource type="BoxShape3D" id="OceanWestShape"] resource_local_to_scene = true -size = Vector3(17, 2, 80) +size = Vector3(20.820662814, 2, 97.979589711) [sub_resource type="BoxShape3D" id="OceanEastShape"] resource_local_to_scene = true -size = Vector3(42, 2, 80) +size = Vector3(51.439284598, 2, 97.979589711) [sub_resource type="BoxShape3D" id="OceanNorthShape"] resource_local_to_scene = true -size = Vector3(48, 2, 18) +size = Vector3(58.787753827, 2, 22.045407685) [sub_resource type="BoxShape3D" id="OceanSouthShape"] resource_local_to_scene = true -size = Vector3(48, 2, 18) +size = Vector3(58.787753827, 2, 22.045407685) [sub_resource type="BoxShape3D" id="OceanCenterShape"] resource_local_to_scene = true -size = Vector3(48, 2, 44) +size = Vector3(58.787753827, 2, 53.888774341) [sub_resource type="BoxShape3D" id="OceanWestRecoveryShape"] resource_local_to_scene = true @@ -180,23 +180,23 @@ fish_pool = ExtResource("4_pool") surface_height_mode = 1 [node name="WestShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=357406062] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45.5, -1, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -55.613519213, -1, 0) shape = SubResource("OceanWestShape") [node name="EastShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=772224738] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 32, -1, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 39.30420832, -1, 0) shape = SubResource("OceanEastShape") [node name="NorthShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=646147646] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, -1, 31) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15.809310892, -1, 37.967091013) shape = SubResource("OceanNorthShape") [node name="SouthShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion" unique_id=894083854] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -13, -1, -31) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15.809310892, -1, -37.967091013) shape = SubResource("OceanSouthShape") [node name="CenterShape" type="CollisionShape3D" parent="WaterBodies/Ocean/FishingRegions/OceanFishingRegion"] -position = Vector3(-13, -1, 0) +position = Vector3(-15.809310892, -1, 0) shape = SubResource("OceanCenterShape") [node name="RecoveryRegions" type="Node3D" parent="WaterBodies/Ocean" unique_id=463706703]