diff --git a/.github/actions/godot-build/action.yml b/.github/actions/godot-build/action.yml new file mode 100644 index 00000000..540f46a8 --- /dev/null +++ b/.github/actions/godot-build/action.yml @@ -0,0 +1,52 @@ +name: Build Godot +description: Build Godot with the provided options. +defaults: + run: + working-directory: ./godot-engine +inputs: + target: + description: Build target (editor, template_release, template_debug). + default: "editor" + tests: + description: Unit tests. + default: false + platform: + description: Target platform. + required: false + sconsflags: + default: "" + scons-cache: + description: The scons cache path. + default: "${{ github.workspace }}/.scons-cache/" + scons-cache-limit: + description: The scons cache size limit. + # actions/cache has 10 GiB limit, and GitHub runners have a 14 GiB disk. + # Limit to 7 GiB to avoid having the extracted cache fill the disk. + default: 7168 +runs: + using: "composite" + steps: + - name: Scons Build + shell: sh + env: + SCONSFLAGS: ${{ inputs.sconsflags }} + SCONS_CACHE: ${{ inputs.scons-cache }} + SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }} + run: | + cd godot-engine + echo "Building with flags:" platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }} + + if [ "${{ inputs.target }}" != "editor" ]; then + # Ensure we don't include editor code in export template builds. + rm -rf editor + fi + + if [ "${{ github.event.number }}" != "" ]; then + # Set build identifier with pull request number if available. This is displayed throughout the editor. + export BUILD_NAME="gh-${{ github.event.number }}" + else + export BUILD_NAME="gh" + fi + + scons platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }} + ls -l bin/ diff --git a/.github/workflows/install_vulkan_sdk_macos.sh b/.github/workflows/install_vulkan_sdk_macos.sh new file mode 100644 index 00000000..17d567f2 --- /dev/null +++ b/.github/workflows/install_vulkan_sdk_macos.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +set -euo pipefail +IFS=$'\n\t' + +# Download and install the Vulkan SDK. +curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.dmg" -o /tmp/vulkan-sdk.dmg +hdiutil attach /tmp/vulkan-sdk.dmg -mountpoint /Volumes/vulkan-sdk +/Volumes/vulkan-sdk/InstallVulkan.app/Contents/MacOS/InstallVulkan \ + --accept-licenses --default-answer --confirm-command install + +cnt=5 +until hdiutil detach -force /Volumes/vulkan-sdk +do + [[ cnt -eq "0" ]] && break + sleep 1 + ((cnt--)) +done + +rm -f /tmp/vulkan-sdk.dmg + +echo 'Vulkan SDK installed successfully! You can now build Godot by running "scons".' diff --git a/.github/workflows/linux_builds_deployment.yml b/.github/workflows/linux_builds_deployment.yml new file mode 100644 index 00000000..37e2ffe7 --- /dev/null +++ b/.github/workflows/linux_builds_deployment.yml @@ -0,0 +1,104 @@ +name: 🐧 Linux Builds +on: push +# on: +# push: +# paths: +# - godot-engine/** + +defaults: + run: + working-directory: ./godot-engine + +# Global Settings +env: + # Used for the cache key. Add version suffix to force clean build. + GODOT_BASE_BRANCH: master + SCONSFLAGS: verbose=yes warnings=extra werror=no module_text_server_fb_enabled=yes fontconfig=no + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + TSAN_OPTIONS: suppressions=misc/error_suppressions/tsan.txt + +concurrency: + # workflow name - PR || fallback to unique run id, this happens when you're not building a PR + # this ensures all branches build properly and cancel their previous runs + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build-linux: + runs-on: "ubuntu-20.04" # MUST run on the old version for GLIBC compatibility + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: Editor + cache-name: linux-editor + target: editor + sconsflags: arch=x86_64 debug_symbols=no optimize=speed production=yes + strip: false + bin: "./bin/godot.linuxbsd.editor.x86_64" + artifact-name: "MirrorGodotEditorLinux.x86_64" + artifact: true + tests: no + + - name: Template + cache-name: linux-template + target: template_debug + strip: true + sconsflags: arch=x86_64 debug_symbols=no optimize=speed + bin: "./bin/godot.linuxbsd.template_debug.x86_64" + artifact-name: "linux_release.x86_64" + artifact: true + tests: no + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Godot build cache + uses: ./godot-engine/.github/actions/godot-cache + with: + cache-name: ${{ matrix.cache-name }} + continue-on-error: true + + - name: Setup scons + shell: bash + run: | + python -c "import sys; print(sys.version)" + python -m pip install scons==4.4.0 + scons --version + + - name: Setup GCC problem matcher + uses: ammaraskar/gcc-problem-matcher@master + + - name: Compilation + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }} + platform: linuxbsd + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Strip binaries + if: ${{ matrix.strip }} + run: | + strip bin/godot.* + + # - name: Shrink debug symbols + # if: ${{ !matrix.strip }} + # run: | + # # remove duplicate symbols from binary + # dwz ${{ matrix.bin }} -L none -o Middleman.debug + # # make the debug symbols compressed + # objcopy --compress-debug-sections Middleman.debug FinalMan.debug + # # overwrite the original file + # mv FinalMan.debug ${{ matrix.bin }} + + - name: Prepare artifact + if: ${{ matrix.artifact }} + run: | + chmod +x bin/godot.* + mv ${{ matrix.bin }} bin/${{ matrix.artifact-name }} + diff --git a/.github/workflows/macos_builds_deployment.yml b/.github/workflows/macos_builds_deployment.yml new file mode 100644 index 00000000..a619b7e6 --- /dev/null +++ b/.github/workflows/macos_builds_deployment.yml @@ -0,0 +1,134 @@ +name: 🍎 macOS Builds +on: push +# on: +# push: +# paths: +# - godot-engine/** + +defaults: + run: + working-directory: ./godot-engine + +# Global Settings +env: + # Used for the cache key. Add version suffix to force clean build. + GODOT_BASE_BRANCH: master + SCONSFLAGS: verbose=yes warnings=extra werror=no module_text_server_fb_enabled=yes fontconfig=no use_volk=no + +concurrency: + # workflow name - PR || fallback to unique run id, this happens when you're not building a PR + # this ensures all branches build properly and cancel their previous runs + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build-macos: + runs-on: "macos-latest" + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: Editor + cache-name: macos-editor + target: editor + tests: false + strip: true + sconsflags: debug_symbols=no optimize=speed production=yes + dist-app: "macos_tools.app" + packaged-app: "MirrorGodotEditorMac.app" + bin-name: "godot.macos.editor.universal" + bin-name-x86_64: "godot.macos.editor.x86_64" + bin-name-arm64: "godot.macos.editor.arm64" + artifact-bin-name: "Godot" + artifact-name: "MirrorGodotEditorMac.app" + + - name: Template + cache-name: macos-template + target: template_debug + tests: false + strip: true + sconsflags: debug_symbols=no optimize=speed + dist-app: "macos_template.app" + packaged-app: "macos_template.app" + bin-name: "godot.macos.template_debug.universal" + bin-name-x86_64: "godot.macos.template_debug.x86_64" + bin-name-arm64: "godot.macos.template_debug.arm64" + artifact-bin-name: "godot_macos_release.universal" + artifact-name: "macos_template.app" + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup python and scons + uses: ./godot-engine/.github/actions/godot-deps + - name: Setup Vulkan SDK + run: | + # Download and install the Vulkan SDK. + curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.dmg" -o /tmp/vulkan-sdk.dmg + hdiutil attach /tmp/vulkan-sdk.dmg -mountpoint /Volumes/vulkan-sdk + /Volumes/vulkan-sdk/InstallVulkan.app/Contents/MacOS/InstallVulkan \ + --accept-licenses --default-answer --confirm-command install + + - name: Setup Godot build cache + uses: ./godot-engine/.github/actions/godot-cache + with: + cache-name: ${{ matrix.cache-name }} + continue-on-error: true + + - name: Setup scons (python is already installed on self-hosted runners!) + shell: bash + run: | + python3 -c "import sys; print(sys.version)" + python3 -m ensurepip --upgrade + python3 -m pip install --user scons + scons --version + + - name: Setup cmake + shell: bash + run: | + brew install cmake + cmake --version + + - name: Remove existing binaries + run: | + rm -Rf bin/ + + - name: Compilation (x86_64) + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} arch=x86_64 + platform: macos + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Compilation (arm64) + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} arch=arm64 + platform: macos + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Strip binaries + if: ${{ matrix.strip }} + run: | + echo "Stripping binaries" + strip bin/* + + - name: Prepare universal executable + run: | + lipo -create bin/${{ matrix.bin-name-x86_64 }} bin/${{ matrix.bin-name-arm64 }} -output bin/${{ matrix.bin-name }} + chmod -R +x bin/* + + - name: Package in macOS app bundle + shell: sh + run: | + cp -R misc/dist/${{ matrix.dist-app }} bin/${{ matrix.packaged-app }} + cd bin/ + mkdir -p ${{ matrix.packaged-app }}/Contents/MacOS + cp ${{ matrix.bin-name }} ${{ matrix.packaged-app }}/Contents/MacOS/${{ matrix.artifact-bin-name }} + chmod -R +x ${{ matrix.packaged-app }} + xattr -rc ${{ matrix.packaged-app }} + zip -q -9 -r ${{ matrix.artifact-name }}.zip ${{ matrix.packaged-app }} diff --git a/.github/workflows/windows_builds_deployment.yml b/.github/workflows/windows_builds_deployment.yml new file mode 100644 index 00000000..c7a3896d --- /dev/null +++ b/.github/workflows/windows_builds_deployment.yml @@ -0,0 +1,98 @@ +name: 🏁 Windows Builds +on: push +# on: + # push: + # paths: + # - godot-engine/** + +defaults: + run: + working-directory: ./godot-engine + + # Global Settings +# SCONS_CACHE for windows must be set in the build environment +env: + # Used for the cache key. Add version suffix to force clean build. + GODOT_BASE_BRANCH: master + SCONSFLAGS: verbose=yes warnings=extra werror=no module_text_server_fb_enabled=yes fontconfig=no + SCONS_CACHE_MSVC_CONFIG: true + +concurrency: + # workflow name - PR || fallback to unique run id, this happens when you're not building a PR + # this ensures all branches build properly and cancel their previous runs + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build-windows: + runs-on: "windows-latest" + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: Editor + cache-name: windows-editor + target: editor + strip: true + tests: false + sconsflags: arch=x86_64 debug_symbols=no windows_subsystem=console optimize=speed production=yes + bin: "./bin/godot.windows.editor.x86_64" + artifact-name: "MirrorGodotEditorWindows" + artifact: true + + - name: Template + cache-name: windows-template + target: template_debug + strip: true + tests: false + sconsflags: arch=x86_64 debug_symbols=no optimize=speed + bin: "./bin/godot.windows.template_debug.x86_64" + artifact-name: "windows_release_x86_64" + artifact: true + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Godot build cache + uses: ./godot-engine/.github/actions/godot-cache + with: + cache-name: ${{ matrix.cache-name }} + continue-on-error: true + + - name: Setup python and scons + uses: ./godot-engine/.github/actions/godot-deps + + #- name: Download Direct3D 12 SDK components + # run: python ./misc/scripts/install_d3d12_sdk_windows.py + + - name: Setup MSVC problem matcher + uses: ammaraskar/msvc-problem-matcher@master + + - name: Compilation + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }} + platform: windows + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Strip binaries + if: ${{ matrix.strip }} + run: | + Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force + + - name: Move PDB file (if not stripped) + if: ${{ !matrix.strip }} + run: | + dir -Path ./bin/ + mv ${{matrix.bin}}.pdb bin/${{ matrix.artifact-name}}.pdb + + - name: Prepare artifact + if: ${{ matrix.artifact }} + run: | + mv ${{ matrix.bin }}.exe bin/${{ matrix.artifact-name }}.exe + + diff --git a/mirror-godot-app/gameplay/space_object/scaled_model.gd b/mirror-godot-app/gameplay/space_object/scaled_model.gd index e9c4cfae..9fd36657 100644 --- a/mirror-godot-app/gameplay/space_object/scaled_model.gd +++ b/mirror-godot-app/gameplay/space_object/scaled_model.gd @@ -257,21 +257,48 @@ func _setup_new_physics_colliders(desired_shape_type: String) -> void: # Note: Trigger or not doesn't matter because the layer is NO_COLLIDE. model_body.body_mode = JBody3D.BodyMode.STATIC model_body.set_layer_name(&"NO_COLLIDE") + var asset_hash = Net.file_client._file_cache.get_hash_for_asset(_space_object.asset_data.file_url) + var cache_key = asset_hash + "-" + desired_shape_type + if desired_shape_type == "Model Shapes": - _generate_model_shape_collision() + var promise = await _generate_model_shape_collision(cache_key) + await promise.wait_till_fulfilled() + _space_object.shape = promise.get_result() + _model_provided_shapes_require_static = not _space_object.shape.is_convex() elif desired_shape_type == "Capsule": _generate_capsule_shape_collision() else: # Concave or Convex mesh shape. - _generate_mesh_collision(desired_shape_type == "Concave") + var promise = await _generate_mesh_collision(cache_key, desired_shape_type == "Concave") + await promise.wait_till_fulfilled() + _space_object.shape = promise.get_result() await get_tree().create_timer(2.0).timeout _space_object.set_ignore_state_sync(false) -func _generate_model_shape_collision() -> void: +func _generate_capsule_shape_collision() -> void: + var aabb: AABB = TMNodeUtil.get_local_aabb_of_descendants(self) + var capsule = JCapsuleShape3D.new() + capsule.height = max(aabb.size.y, aabb.size.x/2.0) + capsule.radius = aabb.size.x/2.0 + var body_transform := Transform3D(Basis.IDENTITY, aabb.get_center()) + var compound_shape := JCompoundShape3D.new() + compound_shape.shapes = [capsule] + compound_shape.transforms = [body_transform] + _space_object.shape = compound_shape + _model_provided_shapes_require_static = false + + +func _generate_model_shape_collision(cache_key: String) -> Promise: + if Zone.hash_requests.has(cache_key): + Zone.hash_requests[cache_key] += 1 + return Zone.physics_hash_promises[cache_key] + else: + Zone.hash_requests[cache_key] = 1 + Zone.physics_hash_promises[cache_key] = Promise.new() + var shapes: Array[JShape3D] = [] var transforms: Array[Transform3D] = [] - for model_body in _model_provided_bodies: if not is_instance_valid(model_body): printerr("A model body was invalid! Model nodes should never be freed while the SpaceObject still exists.") @@ -281,31 +308,26 @@ func _generate_model_shape_collision() -> void: if shape: shapes.push_back(shape) transforms.push_back(body_transform) - var compound_shape := JCompoundShape3D.new() compound_shape.shapes = shapes compound_shape.transforms = transforms - _space_object.shape = compound_shape + var promise = Zone.physics_hash_promises[cache_key] + promise.set_result(compound_shape) + return promise - _model_provided_shapes_require_static = not compound_shape.is_convex() +static var cumulative_collision_mesh_assignment_time = 0 -func _generate_capsule_shape_collision() -> void: - var aabb: AABB = TMNodeUtil.get_local_aabb_of_descendants(self) - var capsule = JCapsuleShape3D.new() - capsule.height = max(aabb.size.y, aabb.size.x/2.0) - capsule.radius = aabb.size.x/2.0 - var body_transform := Transform3D(Basis.IDENTITY, aabb.get_center()) - var compound_shape := JCompoundShape3D.new() - compound_shape.shapes = [capsule] - compound_shape.transforms = [body_transform] - _space_object.shape = compound_shape - _model_provided_shapes_require_static = false - - -func _generate_mesh_collision(is_concave: bool) -> void: - var mi: Array[Node] = TMNodeUtil.recursive_find_nodes_by_type(self, MeshInstance3D) +func _generate_mesh_collision(cache_key: String, is_concave: bool) -> Promise: + if Zone.hash_requests.has(cache_key): + Zone.hash_requests[cache_key] += 1 + return Zone.physics_hash_promises[cache_key] + else: + Zone.hash_requests[cache_key] = 1 + Zone.physics_hash_promises[cache_key] = Promise.new() + var start_time = Time.get_unix_time_from_system() + var mi := Util.recursive_find_nodes_of_type(self, MeshInstance3D) var mesh_instances: Array[MeshInstance3D] = [] mesh_instances.resize(mi.size()) for i in range(mi.size()): @@ -315,6 +337,7 @@ func _generate_mesh_collision(is_concave: bool) -> void: var async_collider_construction = true var shape: JShape3D = null + if not async_collider_construction: shape = Zone.shapes_generator.generate_shape_for_meshes(body, mesh_instances, is_concave)[0] else: @@ -325,6 +348,16 @@ func _generate_mesh_collision(is_concave: bool) -> void: assert(shape != null) body.shape = shape + # to ensure the pointers always match we must set it to the same reference + # this de-duplicates the shapes + # this is faster because one pointer for 50+ shapes, and 0 generation time for them. + var promise = Zone.physics_hash_promises[cache_key] + promise.set_result(body.shape) + var shape_time = Time.get_unix_time_from_system() - start_time + print("took ", shape_time, "to generate collision shape") + cumulative_collision_mesh_assignment_time += shape_time + print("cumulatively we have taken: ", cumulative_collision_mesh_assignment_time, " seconds") + return promise ## Ensure concave shapes are static, since physics engines do not support diff --git a/mirror-godot-app/prefabs/autoload/zone/collision_shape_generator/collision_shape_generator.gd b/mirror-godot-app/prefabs/autoload/zone/collision_shape_generator/collision_shape_generator.gd index a3f36789..d451a447 100644 --- a/mirror-godot-app/prefabs/autoload/zone/collision_shape_generator/collision_shape_generator.gd +++ b/mirror-godot-app/prefabs/autoload/zone/collision_shape_generator/collision_shape_generator.gd @@ -14,8 +14,8 @@ var _mesh_rid_to_convex_shape_map = { var _meshes_to_generate_queue = [ #MeshPromisePair, (...) ] -var _generate_thread: Thread = Thread.new() - +# var _generate_thread: Thread = Thread.new() +var cumulative_collision_shape_generation_time = 0.0 func async_generate_shape_for_meshes(body: JBody3D, in_meshes: Array[MeshInstance3D], is_concave: bool) -> Promise: var promise = Promise.new() @@ -43,6 +43,7 @@ func _thread_generate_collision(mesh_promise_pair: MeshPromisePair): func generate_shape_for_meshes(body: JBody3D, in_meshes: Array[MeshInstance3D], is_concave: bool) -> Array: + var start_time = Time.get_unix_time_from_system() var generated_shapes: Array[JShape3D] var generated_shapes_rid: Array[RID] var shapes: Array[JShape3D] @@ -105,6 +106,10 @@ func generate_shape_for_meshes(body: JBody3D, in_meshes: Array[MeshInstance3D], # The shape is 100% expected at this point. assert(result_shape) + var cumulative_time = Time.get_unix_time_from_system() - start_time + print("Import time for shape: ", cumulative_time) + cumulative_collision_shape_generation_time += cumulative_time + print("[", Zone.get_instance_type(), "] Cumulative import time ", cumulative_collision_shape_generation_time) return [result_shape, generated_shapes, generated_shapes_rid] diff --git a/mirror-godot-app/scripts/autoload/zone/client.gd b/mirror-godot-app/scripts/autoload/zone/client.gd index c0948b93..e32acdd9 100644 --- a/mirror-godot-app/scripts/autoload/zone/client.gd +++ b/mirror-godot-app/scripts/autoload/zone/client.gd @@ -543,11 +543,9 @@ func _join_new_server_locally(space_id: String) -> bool: if pid != null: OS.kill(pid) var firebase_auth = str(Firebase.Auth.auth.refreshtoken) - print(firebase_auth) # For debugging this allows you to grab breakpoints from the server "--remote-debug", "tcp://127.0.0.1:6008"] # If enabled it could cause join time to be much longer when booting server - var arguments = ["--server", "--space", space_id, "--mode", "edit", "--uuid", "localhost", "--server_login", firebase_auth, "--headless"] - print("SERVER ARGS: ", arguments) + var arguments = ["--server", "--space", space_id, "--mode", "edit", "--uuid", "localhost", "--server_login", firebase_auth, "--headless", "--remote-debug", "tcp://127.0.0.1:6008"] pid = OS.create_process(OS.get_executable_path(), arguments, true) start_join_localhost() return true diff --git a/mirror-godot-app/scripts/autoload/zone/server.gd b/mirror-godot-app/scripts/autoload/zone/server.gd index 0e6c5e63..8f8895fb 100644 --- a/mirror-godot-app/scripts/autoload/zone/server.gd +++ b/mirror-godot-app/scripts/autoload/zone/server.gd @@ -243,6 +243,7 @@ func _on_edit_server_zone_socket_logged_in() -> void: push_error("Critical: cannot start server: ", promise.get_error_message()) return _on_zone_socket_space_received(promise.get_result()) + print("Started getting space objects: ", Time.get_datetime_string_from_system()) Net.zone_socket.get_space_objects_page(space_id, 1) @@ -272,31 +273,41 @@ func _handle_zone_socket_error(request: Dictionary) -> void: push_error(error_msg, " ", request) print(error_msg) - +var _loaded_pages = 0 +var _queued_assets = [] func _on_zone_socket_space_object_page_received(space_objects_page: Dictionary) -> void: var page = space_objects_page.get("page", 1) var total_pages = space_objects_page.get("totalPage", 1) var space_objects_arr = space_objects_page.get("result", []) - for obj in space_objects_arr: - # if we already have the asset in memory, no need to get it again - if not Net.asset_client.get_asset_json(obj["asset"]).is_empty(): - continue - Net.zone_socket.queue_download_asset(obj["asset"]) + _loaded_pages +=1 + print("Loaded page: ", page, " time: ", Time.get_datetime_string_from_system()) + print("Total pages loaded: ", _loaded_pages, " total pages ", total_pages) - for space_obj in space_objects_arr: - #assert(not space_obj.has("receipt"), "May happen with old spaces, but should not happen with new spaces.") - if space_obj.has("receipt"): - space_obj["creator"] = space_obj["receipt"].get("created_by_user", "") - space_obj.erase("receipt") Zone.space_objects.append_array(space_objects_arr) # if we've reached the final page, we're done getting all the data - if page >= total_pages: - print("All Space Objects Received") + if _loaded_pages >= total_pages: + print("All Space Objects Received: ", Time.get_datetime_string_from_system()) + print("Downloading assets for space objects") + for obj in Zone.space_objects: + if obj.has("receipt"): + obj["creator"] = obj["receipt"].get("created_by_user", "") + obj.erase("receipt") + # if we already have the asset in memory, no need to get it again + if not Net.asset_client.get_asset_json(obj["asset"]).is_empty(): + continue + var asset_id = obj["asset"] + if not _queued_assets.has(asset_id): + Net.zone_socket.queue_download_asset(asset_id) + _queued_assets.push_back(asset_id) + print("All assets have been queued: ", Time.get_datetime_string_from_system()) + Zone.instance_manager.setup_space_objects() _server_data_received = true return # get the next page - Net.zone_socket.get_space_objects_page(space_id, page + 1) + if page == 1: + for page_id in range(2, total_pages+1): + Net.zone_socket.get_space_objects_page(space_id, page_id) func _on_zone_socket_asset_received(asset: Variant) -> void: diff --git a/mirror-godot-app/scripts/autoload/zone/zone.gd b/mirror-godot-app/scripts/autoload/zone/zone.gd index 643e8336..23663d7a 100644 --- a/mirror-godot-app/scripts/autoload/zone/zone.gd +++ b/mirror-godot-app/scripts/autoload/zone/zone.gd @@ -43,6 +43,9 @@ enum DENIED_REASON { @onready var _ws_debug_prints = ProjectSettings.get_setting("debug_flags/show_web_socket_debug", false) @onready var _space_scene: PackedScene = preload("res://scenes/space_scene.tscn") +var hash_requests: Dictionary = {} +var physics_hash_promises: Dictionary = {} + var current_mode = ZONE_MODE.EDIT var space: Dictionary = {} var space_objects: Array = [] diff --git a/mirror-godot-app/scripts/net/file_cache.gd b/mirror-godot-app/scripts/net/file_cache.gd index 7a3cc29c..c00a0a07 100644 --- a/mirror-godot-app/scripts/net/file_cache.gd +++ b/mirror-godot-app/scripts/net/file_cache.gd @@ -5,7 +5,9 @@ signal threaded_model_loaded(cache_key: String, node: Node) const _STORAGE_CACHE_FILENAME: String = "cache.json" -var _storage_cache: Dictionary = {} +var _storage_cache: Dictionary = {} # url, filename +var _file_hashes: Dictionary = {} # url, hash +var _duplicate_hash_counter: Dictionary = {} # hash, times occoured var _model_load_queue: Array[KeyPromisePair] = [] @@ -117,6 +119,30 @@ func get_file_path(file_name: String) -> String: return storage_path_format % file_name +func get_hash_for_asset(cache_key: String) -> Variant: + cache_key = cache_key.uri_decode() + if not _storage_cache.has(cache_key): + return "" + var file_name: String = _storage_cache[cache_key] + var file_path: String = get_file_path(file_name) + if not FileAccess.file_exists(file_path): + return "" + if not _file_hashes.has(cache_key): + var ctx = HashingContext.new() + ctx.start(HashingContext.HASH_SHA1) + var file = FileAccess.open(file_path, FileAccess.READ) + while not file.eof_reached(): + ctx.update(file.get_buffer(1024)) + var res = ctx.finish() + var hash_string: String = res.hex_encode() + _file_hashes[cache_key] = hash_string + _duplicate_hash_counter[hash_string] = 0 + return hash_string + else: + _duplicate_hash_counter[_file_hashes[cache_key]] += 1 + return _file_hashes[cache_key] + + ## Tries to load a file into memory from disk cache and returns the payload or null. func try_load_cached_file(cache_key: String) -> Variant: cache_key = cache_key.uri_decode() diff --git a/mirror-godot-app/scripts/net/web_socket_client.gd b/mirror-godot-app/scripts/net/web_socket_client.gd index 3ae900ea..a6718d11 100644 --- a/mirror-godot-app/scripts/net/web_socket_client.gd +++ b/mirror-godot-app/scripts/net/web_socket_client.gd @@ -18,8 +18,8 @@ signal message_received(message: Variant) func connect_to_url(url) -> int: - _socket.set_inbound_buffer_size(_socket.get_inbound_buffer_size() * 4) - _socket.set_outbound_buffer_size(_socket.get_outbound_buffer_size() * 4) + _socket.set_inbound_buffer_size(_socket.get_inbound_buffer_size() * 16) + _socket.set_outbound_buffer_size(_socket.get_outbound_buffer_size() * 16) _socket.supported_protocols = supported_protocols _socket.handshake_headers = handshake_headers var tls := TLSOptions.client(tls_trusted_certificate) diff --git a/mirror-godot-app/scripts/net/zone_socket.gd b/mirror-godot-app/scripts/net/zone_socket.gd index 1f08489b..dff61c92 100644 --- a/mirror-godot-app/scripts/net/zone_socket.gd +++ b/mirror-godot-app/scripts/net/zone_socket.gd @@ -300,7 +300,7 @@ func update_status(status: Dictionary) -> void: func get_space_objects_page(space_id: String, page: int=1) -> void: var request = { "event": ZONE_GET_SPACE_OBJECTS_PAGE, - "data": { "id": space_id, "page": page, "perPage": 40 } + "data": { "id": space_id, "page": page, "perPage": 150 } } _requests.push_back(request) diff --git a/mirror-godot-app/scripts/space/instance_manager.gd b/mirror-godot-app/scripts/space/instance_manager.gd index 373910bc..37f973e5 100644 --- a/mirror-godot-app/scripts/space/instance_manager.gd +++ b/mirror-godot-app/scripts/space/instance_manager.gd @@ -123,9 +123,12 @@ func _preload_space_objects() -> void: func setup_space_objects() -> void: assert(Zone.is_host()) clear_children() + print("Preloading space objects: ", Time.get_datetime_string_from_system()) # preload necessary space_objects on server first _preload_space_objects() _load_object_start_time = Time.get_unix_time_from_system() + + print("Creating standard space objects: ", Time.get_datetime_string_from_system()) for space_obj in Zone.space_objects: if space_obj.get("preloadBeforeSpaceStarts", false): continue @@ -139,6 +142,7 @@ func setup_space_objects() -> void: create_space_object(space_obj, {}) await wait_until_ready_to_simulate() print("Completed loading server objects took %d seconds" % (Time.get_unix_time_from_system() - _load_object_start_time)) + print("Physics Ready at: ", Time.get_datetime_string_from_system()) space_objects_created.emit() diff --git a/mirror-godot-app/ui/game/connect_by_ip_ui.gd b/mirror-godot-app/ui/game/connect_by_ip_ui.gd index 2eda9851..f53d57b5 100644 --- a/mirror-godot-app/ui/game/connect_by_ip_ui.gd +++ b/mirror-godot-app/ui/game/connect_by_ip_ui.gd @@ -6,5 +6,5 @@ func _ready(): func _on_connect_pressed(): var line_edit = $MarginContainer/HBoxContainer/LineEdit - print("Attempting connection: ", line_edit) + print("Attempting connection: ", line_edit.text) Zone.client.connect_to_server_by_string(line_edit.text)