diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d54be2e86e..ad9486f7b4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,4 +10,4 @@ AssemblyInfo.cs @NoelStephensUnity @EmandM @Unity-Technologies/netcode-qa .github/ @NoelStephensUnity @EmandM @Unity-Technologies/netcode-qa .yamato/ @NoelStephensUnity @EmandM @Unity-Technologies/netcode-qa com.unity.netcode.gameobjects/Documentation*/ @jabbacakes -com.unity.netcode.gameobjects/Runtime/Transports/UTP/ @Unity-Technologies/multiplayer-workflows +com.unity.netcode.gameobjects/Runtime/Transports/UTP/ @simon-lemay-unity diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1af44ed7da..5872f6cbbd 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -56,6 +56,12 @@ _Does the change require QA team to:_ If any boxes above are checked the QA team will be automatically added as a PR reviewer. +## Up-port +[//]: # ( +This section is REQUIRED and should link to the PR that targets develop-3.0.0 branch. Assuming that the PR lands agains default develop-2.0.0 branch +If this is not needed, for example feature specific to NGOv2.X, then just mention this fact. +) + ## Backports [//]: # ( This section is REQUIRED and should link to the PR that targets other NGO version which is either develop or develop-2.0.0 branch diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 1143236874..31a5612ec8 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,5 +1,5 @@ { - "baseBranches": ["develop", "develop-2.0.0"], + "baseBranches": ["develop-2.0.0", "develop-3.x.x"], "dependencyDashboard": true, "$schema": "https://docs.renovatebot.com/renovate-schema.json", @@ -27,10 +27,12 @@ "unity-upm-package" ], "enabled": "true", - "schedule": [ - "every weekend" - ], + "schedule": ["every weekend"], "rollbackPrs": false, + }, + { + "groupName": "CI deps updates", + "matchPackagePatterns": ["^RecipeEngine", "actions/checkout", "package-ci/ubuntu-22.04"] } ], } diff --git a/.github/workflows/autoupdate.yaml b/.github/workflows/autoupdate.yaml index 764b52b373..298c87bc37 100644 --- a/.github/workflows/autoupdate.yaml +++ b/.github/workflows/autoupdate.yaml @@ -4,6 +4,7 @@ on: branches: - develop - develop-2.0.0 + - develop-3.x.x jobs: autoupdate: name: auto-update diff --git a/.github/workflows/conventional-pr.yml b/.github/workflows/conventional-pr.yml index 30274254e2..e5176d4912 100644 --- a/.github/workflows/conventional-pr.yml +++ b/.github/workflows/conventional-pr.yml @@ -6,6 +6,7 @@ on: branches: - develop - develop-2.0.0 + - develop-3.x.x # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" diff --git a/.github/workflows/pr-description-validation.yml b/.github/workflows/pr-description-validation.yml index 9d2b4ccc75..adb0b9dfda 100644 --- a/.github/workflows/pr-description-validation.yml +++ b/.github/workflows/pr-description-validation.yml @@ -14,6 +14,7 @@ on: branches: - develop - develop-2.0.0 + - develop-3.x.x - release/* jobs: @@ -21,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Check PR description uses: actions/github-script@v9 @@ -29,14 +30,14 @@ jobs: script: | const pr = context.payload.pull_request; const body = pr.body || ''; - + // List of users to skip description validation // This should be automations where we don't care that much about the description format const skipUsersPrefixes = [ 'unity-renovate', 'svc-' ]; - + // If PR author is in the skip list, exit early const author = pr.user.login; console.log(`PR author: ${author}`); @@ -49,7 +50,11 @@ jobs: const requiredSections = [ { header: '## Backports', - description: 'PR description must include a "## Backports" section. Please add this section and provide information about this PR backport to develop or develop-2.0.0 branch respectively or explain why backport is not needed.' + description: 'PR description must include a "## Backports" section. Please add this section and provide information about this PR backport to develop, develop-2.0.0 or develop-3.x.x branch respectively or explain why backport is not needed.' + }, + { + header: '## Up-port', + description: 'PR description must include a "## Up-port" section. Please add this section and provide information about this PR up-port to develop-3.0.0 branch or explain why up-port is not needed.' }, { header: '## Testing & QA', diff --git a/.github/workflows/pr-supervisor.yaml b/.github/workflows/pr-supervisor.yaml index ba8da21e93..e2429a3a4e 100644 --- a/.github/workflows/pr-supervisor.yaml +++ b/.github/workflows/pr-supervisor.yaml @@ -2,7 +2,7 @@ # We are using https://cli.github.com/manual/gh_pr_checks # The aim is to ensure that conditionally triggered Yamato jobs are completed successfully before allowing merges -# This job will be required in branch protection rules for develop, develop-2.0.0, and release/* branches. It's only goal will be to ensure that Yamato jobs are completed successfully before allowing Pr to merge. +# This job will be required in branch protection rules for develop, develop-2.0.0, develop-3.x.x, and release/* branches. It's only goal will be to ensure that Yamato jobs are completed successfully before allowing Pr to merge. # Note that conditional jobs will have 30s to show which is always the cas since they are showing up as soon as in distribution stage. name: Yamato PR Supervisor @@ -13,8 +13,9 @@ on: branches: - develop - develop-2.0.0 + - develop-3.x.x - release/* - + concurrency: group: pr-${{ github.event.pull_request.number }} cancel-in-progress: true @@ -25,33 +26,32 @@ jobs: timeout-minutes: 720 steps: - name: Checkout repository - uses: actions/checkout@v6 - + uses: actions/checkout@v7 - name: Wait and Verify Yamato Job Status env: GH_TOKEN: ${{ secrets.GH_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} run: | set -e - - + + MAX_ATTEMPTS=$((12*60)) INTERVAL=60 - + sleep $INTERVAL for ((i=1;i<=MAX_ATTEMPTS;i++)); do echo "Polling PR checks (attempt $i/$MAX_ATTEMPTS)..." - + # We want to watch for pending checks beside this check checks=$(gh pr checks $PR_NUMBER --json name,state --jq '[ .[] | select(.name != "yamato-supervisor") ]') - + pending=$(echo "$checks" | jq '[.[] | select(.state == "PENDING")] | length') skipping=$(echo "$checks" | jq '[.[] | select(.state == "SKIPPED")] | length') passed=$(echo "$checks" | jq '[.[] | select(.state == "SUCCESS")] | length') failed=$(echo "$checks" | jq '[.[] | select(.state == "FAILURE")] | length') - + echo "Pending checks: $pending, Skipping checks: $skipping", Passed checks: $passed, Failed checks: $failed - + if [[ "$failed" -gt 0 ]]; then echo "A check has failed! Failing fast." exit 1 @@ -61,6 +61,6 @@ jobs: echo "All non-supervisor checks are completed!" exit 0 fi - + sleep $INTERVAL - done \ No newline at end of file + done diff --git a/.gitignore b/.gitignore index 1d3ee28bec..b38b538937 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ utr.bat Tools/CI/bin Tools/CI/obj +# Do not include the packages-lock file +packages-lock.json diff --git a/.yamato/_run-all.yml b/.yamato/_run-all.yml index 24daf40665..6cc1709b16 100644 --- a/.yamato/_run-all.yml +++ b/.yamato/_run-all.yml @@ -16,7 +16,7 @@ run_quick_checks: name: Run Quick Initial Checks dependencies: # Ensure the code is running to our current standards - - .yamato/project-standards.yml#standards_ubuntu_testproject_{{ validation_editors.default }} + - .yamato/project-standards.yml#standards_ubuntu_testproject_{{ validation_editors.minimal }} # This is needed for most of the jobs to execute tests + it runs xray PVP checks (all fast checks) - .yamato/package-pack.yml#package_pack_-_ngo_win @@ -48,12 +48,20 @@ run_all_package_tests_pinnedTrunk: - .yamato/package-tests.yml#package_test_-_ngo_{{ pinnedTrunk }}_{{ platform.name }} {% endfor -%} -# Runs all package tests on mimimum supported editor (6000.0 in case of NGOv2.X) -run_all_package_tests_6000: - name: Run All Package Tests [6000.0] +# Runs all package tests on minimum supported editor +run_all_package_tests_minimum_editor: + name: Run All Package Tests [{{ validation_editors.minimal }}] dependencies: {% for platform in test_platforms.desktop -%} - - .yamato/package-tests.yml#package_test_-_ngo_6000.0_{{ platform.name }} + - .yamato/package-tests.yml#package_test_-_ngo_{{ validation_editors.minimal }}_{{ platform.name }} +{% endfor -%} + +# Runs all package tests on default editor +run_all_package_tests_default: + name: Run All Package Tests [{{ validation_editors.default }}] + dependencies: +{% for platform in test_platforms.desktop -%} + - .yamato/package-tests.yml#package_test_-_ngo_{{ validation_editors.default }}_{{ platform.name }} {% endfor -%} @@ -96,14 +104,26 @@ run_all_project_tests_pinnedTrunk: {% endif -%} {% endfor -%} -# Runs all projects tests on mimimum supported editor (6000.0 in case of NGOv2.X) -run_all_project_tests_6000: - name: Run All Project Tests [6000.0] +# Runs all projects tests on minimum supported editor +run_all_project_tests_minimum_editor: + name: Run All Project Tests [{{ validation_editors.minimal }}] + dependencies: +{% for project in projects.all -%} +{% if project.has_tests == "true" -%} +{% for platform in test_platforms.desktop -%} + - .yamato/project-tests.yml#test_{{ project.name }}_{{ platform.name }}_{{ validation_editors.minimal }} +{% endfor -%} +{% endif -%} +{% endfor -%} + +# Runs all projects tests on default editor +run_all_project_tests_default: + name: Run All Project Tests [{{ validation_editors.default }}] dependencies: {% for project in projects.all -%} {% if project.has_tests == "true" -%} {% for platform in test_platforms.desktop -%} - - .yamato/project-tests.yml#test_{{ project.name }}_{{ platform.name }}_6000.0 + - .yamato/project-tests.yml#test_{{ project.name }}_{{ platform.name }}_{{ validation_editors.default }} {% endfor -%} {% endif -%} {% endfor -%} @@ -115,7 +135,7 @@ run_all_projects_standards: dependencies: {% for platform in test_platforms.default -%} {% for project in projects.all -%} -{% for editor in validation_editors.default -%} +{% for editor in validation_editors.minimal -%} - .yamato/project-standards.yml#standards_{{ platform.name }}_{{ project.name }}_{{ editor }} {% endfor -%} {% endfor -%} @@ -156,13 +176,23 @@ run_all_webgl_builds_pinnedTrunk: {% endfor -%} {% endfor -%} -# Runs all WebGL builds on 6000.0 editor -run_all_webgl_builds_6000: - name: Run All WebGl Build [6000.0] +# Runs all WebGL builds on minimum supported editor +run_all_webgl_builds_minimum_editor: + name: Run All WebGl Build [{{ validation_editors.minimal }}] dependencies: {% for project in projects.default -%} {% for platform in test_platforms.desktop -%} - - .yamato/webgl-build.yml#webgl_build_{{ project.name }}_{{ platform.name }}_6000.0 + - .yamato/webgl-build.yml#webgl_build_{{ project.name }}_{{ platform.name }}_{{ validation_editors.minimal }} +{% endfor -%} +{% endfor -%} + +# Runs all WebGL builds on default editor +run_all_webgl_builds_default: + name: Run All WebGl Build [{{ validation_editors.default }}] + dependencies: +{% for project in projects.default -%} +{% for platform in test_platforms.desktop -%} + - .yamato/webgl-build.yml#webgl_build_{{ project.name }}_{{ platform.name }}_{{ validation_editors.default }} {% endfor -%} {% endfor -%} @@ -206,14 +236,26 @@ run_all_project_tests_desktop_standalone_pinnedTrunk: {% endfor -%} {% endfor -%} -# Runs all Desktop tests on mimimum supported editor (6000.0 in case of NGOv2.X) -run_all_project_tests_desktop_standalone_6000: - name: Run All Standalone Tests - Desktop [6000.0] +# Runs all Desktop tests on minimum supported editor +run_all_project_tests_desktop_standalone_minimum_editor: + name: Run All Standalone Tests - Desktop [{{ validation_editors.minimal }}] + dependencies: +{% for project in projects.default -%} +{% for platform in test_platforms.desktop -%} +{% for backend in scripting_backends -%} + - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ validation_editors.minimal }} +{% endfor -%} +{% endfor -%} +{% endfor -%} + +# Runs all Desktop tests on default editor +run_all_project_tests_desktop_standalone_default: + name: Run All Standalone Tests - Desktop [{{ validation_editors.default }}] dependencies: {% for project in projects.default -%} {% for platform in test_platforms.desktop -%} {% for backend in scripting_backends -%} - - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_6000.0 + - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ validation_editors.default }} {% endfor -%} {% endfor -%} {% endfor -%} @@ -251,13 +293,23 @@ run_all_project_tests_mobile_standalone_pinnedTrunk: {% endfor -%} {% endfor -%} -# Runs all Mobile tests on mimimum supported editor (6000.0 in case of NGOv2.X) -run_all_project_tests_mobile_standalone_6000: - name: Run All Standalone Tests - Mobile [6000.0] +# Runs all Mobile tests on minimum supported editor +run_all_project_tests_mobile_standalone_minimum_editor: + name: Run All Standalone Tests - Mobile [{{ validation_editors.minimal }}] + dependencies: +{% for project in projects.default -%} +{% for platform in test_platforms.mobile_test -%} + - .yamato/mobile-standalone-test.yml#mobile_standalone_test_{{ project.name }}_{{ platform.name }}_{{ validation_editors.minimal }} +{% endfor -%} +{% endfor -%} + +# Runs all Mobile tests on default editor +run_all_project_tests_mobile_standalone_default: + name: Run All Standalone Tests - Mobile [{{ validation_editors.default }}] dependencies: {% for project in projects.default -%} {% for platform in test_platforms.mobile_test -%} - - .yamato/mobile-standalone-test.yml#mobile_standalone_test_{{ project.name }}_{{ platform.name }}_6000.0 + - .yamato/mobile-standalone-test.yml#mobile_standalone_test_{{ project.name }}_{{ platform.name }}_{{ validation_editors.default }} {% endfor -%} {% endfor -%} @@ -295,13 +347,23 @@ run_all_project_tests_console_standalone_pinnedTrunk: {% endfor -%} {% endfor -%} -# Runs all Console tests on mimimum supported editor (6000.0 in case of NGOv2.X) -run_all_project_tests_console_standalone_6000: - name: Run All Standalone Tests - Console [6000.0] +# Runs all Console tests on minimum supported editor +run_all_project_tests_console_standalone_minimum_editor: + name: Run All Standalone Tests - Console [{{ validation_editors.minimal }}] dependencies: {% for project in projects.default -%} {% for platform in test_platforms.console_test -%} - - .yamato/console-standalone-test.yml#console_standalone_test_{{ project.name }}_{{ platform.name }}_6000.0 + - .yamato/console-standalone-test.yml#console_standalone_test_{{ project.name }}_{{ platform.name }}_{{ validation_editors.minimal }} +{% endfor -%} +{% endfor -%} + +# Runs all Console tests on default editor +run_all_project_tests_console_standalone_default: + name: Run All Standalone Tests - Console [{{ validation_editors.default }}] + dependencies: +{% for project in projects.default -%} +{% for platform in test_platforms.console_test -%} + - .yamato/console-standalone-test.yml#console_standalone_test_{{ project.name }}_{{ platform.name }}_{{ validation_editors.default }} {% endfor -%} {% endfor -%} @@ -344,14 +406,26 @@ run_all_project_tests_cmb_service_pinnedTrunk: {% endfor -%} {% endfor -%} -# Runs all CMB service tests on mimimum supported editor (6000.0 in case of NGOv2.X) -run_all_project_tests_cmb_service_6000: - name: Run All CMB Service Tests [6000.0] +# Runs all CMB service tests on minimum supported editor +run_all_project_tests_cmb_service_minimum_editor: + name: Run All CMB Service Tests [{{ validation_editors.minimal }}] + dependencies: +{% for project in projects.default -%} +{% for platform in test_platforms.default -%} +{% for backend in scripting_backends -%} + - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ validation_editors.minimal }} +{% endfor -%} +{% endfor -%} +{% endfor -%} + +# Runs all CMB service tests on default editor +run_all_project_tests_cmb_service_default: + name: Run All CMB Service Tests [{{ validation_editors.default }}] dependencies: {% for project in projects.default -%} {% for platform in test_platforms.default -%} {% for backend in scripting_backends -%} - - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_6000.0 + - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ validation_editors.default }} {% endfor -%} {% endfor -%} {% endfor -%} diff --git a/.yamato/_triggers.yml b/.yamato/_triggers.yml index 8cdf8445ce..a1c2779ac7 100644 --- a/.yamato/_triggers.yml +++ b/.yamato/_triggers.yml @@ -17,7 +17,7 @@ # 1) Minimal PR checks that run on all PRs (even if only docs are changed) # 2) More extensive pr_code_changes_checks that run if code is changed. This test validates Standards, Package tests, Project tests and Desktop standalone tests to ensure that main platforms are covered # By default pr_minimal_required_checks it's triggered if - # 1) PR targets develop, develop-2.0.0 or release branches + # 1) PR targets develop, develop-2.0.0, develop-3.x.x or release branches # 2) PR is not a draft # Then pr_code_changes_checks it's triggered if the same conditions apply plus: # 1) PR changes code in com.unity.netcode.gameobjects package (Editor, Runtime or Tests folders) or in testproject folder or package.json file @@ -27,7 +27,7 @@ # Nightly: # This test validates same subset as pull_request_trigger with addition of mobile/console tests and webgl builds # Runs daily on develop (local configuration) - # Includes all test types but only on trunk. + # Includes all test types but only on trunk and the default editor. # Adds platform-specific and APV validation # Weekly: @@ -51,13 +51,14 @@ pr_minimal_required_checks: name: Minimal PR checks dependencies: - - .yamato/project-standards.yml#standards_ubuntu_testproject_{{ validation_editors.default }} + - .yamato/project-standards.yml#standards_ubuntu_testproject_{{ validation_editors.minimal }} - .yamato/package-pack.yml#package_pack_-_ngo_win triggers: expression: |- (pull_request.comment eq "ngo" OR (pull_request.target eq "develop" OR pull_request.target eq "develop-2.0.0" OR + pull_request.target eq "develop-3.x.x" OR pull_request.target match "release/*")) AND NOT pull_request.draft cancel_old_ci: true @@ -75,13 +76,13 @@ pr_code_changes_checks: # Run API validation to early-detect all new APIs that would force us to release new minor version of the package. Note that for this to work the package version in package.json must correspond to "actual package state" which means that it should be higher than last released version - .yamato/vetting-test.yml#vetting_test - # Run package EditMode and Playmode package tests on trunk and an older supported editor (6000.0) + # Run package EditMode and Playmode package tests on pinned trunk and the minimal supported editor - .yamato/package-tests.yml#package_test_-_ngo_{{ pinnedTrunk }}_mac - - .yamato/package-tests.yml#package_test_-_ngo_6000.0_win + - .yamato/package-tests.yml#package_test_-_ngo_{{ validation_editors.minimal }}_win - # Run testproject EditMode and Playmode project tests on trunk and an older supported editor (6000.0) + # Run testproject EditMode and Playmode project tests on pinned trunk and the minimal supported editor - .yamato/project-tests.yml#test_testproject_win_{{ pinnedTrunk }} - - .yamato/project-tests.yml#test_testproject_mac_6000.0 + - .yamato/project-tests.yml#test_testproject_mac_{{ validation_editors.minimal }} # Run standalone test. We run it only on Ubuntu since it's the fastest machine, and it was noted that for example distribution on macOS is taking 40m since we switched to Apple Silicon # Coverage on other standalone machines is present in Nightly job so it's enough to not run all of them for PRs @@ -89,11 +90,14 @@ pr_code_changes_checks: # Note that our daily tests will anyway run both test configurations in "minimal supported" and "trunk" configurations - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_{{ pinnedTrunk }} - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_{{ pinnedTrunk }} + # Run code coverage test + - .yamato/code-coverage.yml#code_coverage_ubuntu_{{ validation_editors.default }} triggers: expression: |- (pull_request.comment eq "ngo" OR (pull_request.target eq "develop" OR pull_request.target eq "develop-2.0.0" OR + pull_request.target eq "develop-3.x.x" OR pull_request.target match "release/*")) AND NOT pull_request.draft AND pull_request.changes.any match [ @@ -116,47 +120,49 @@ pr_code_changes_checks: -# Run all tests on trunk on nightly basis. +# Run all tests on nightly basis. # Same subset as pull_request_trigger with addition of mobile/desktop/console tests and webgl builds -# Those tests are all running on trunk editor (since it's daily and running all of them would add a lot of overhead) +# Those tests are all running on trunk and the default editor (since it's daily and running all of them would add a lot of overhead) develop_nightly: - name: "\U0001F319 [Nightly] Run All Tests [Trunk and 6000]" + name: "\U0001F319 [Nightly] Run All Tests [Trunk and Default]" triggers: recurring: - - branch: develop-2.0.0 + - branch: develop-3.x.x frequency: daily rerun: always dependencies: # Run project standards to verify package/default project - - .yamato/project-standards.yml#standards_ubuntu_testproject_{{ validation_editors.default }} + - .yamato/project-standards.yml#standards_ubuntu_testproject_{{ validation_editors.minimal }} # Run APV jobs to make sure the change won't break any dependants - .yamato/wrench/preview-a-p-v.yml#all_preview_apv_jobs - # Run package EditMode and Playmode tests on desktop platforms on trunk and 6000.0 + # Run package EditMode and Playmode tests on desktop platforms on trunk and default editor - .yamato/_run-all.yml#run_all_package_tests_trunk - - .yamato/_run-all.yml#run_all_package_tests_6000 - # Run project EditMode and PLaymode tests on desktop platforms on trunk and 6000.0 + - .yamato/_run-all.yml#run_all_package_tests_default + # Run project EditMode and PLaymode tests on desktop platforms on trunk and default editor - .yamato/_run-all.yml#run_all_project_tests_trunk - - .yamato/_run-all.yml#run_all_project_tests_6000 - # Run Runtime tests on desktop players on trunk and 6000.0 editors + - .yamato/_run-all.yml#run_all_project_tests_default + # Run Runtime tests on desktop players on trunk and default editors - .yamato/_run-all.yml#run_all_project_tests_desktop_standalone_trunk - - .yamato/_run-all.yml#run_all_project_tests_desktop_standalone_6000 - # Run Runtime tests on mobile players on trunk and 6000.0 editors + - .yamato/_run-all.yml#run_all_project_tests_desktop_standalone_default + # Run Runtime tests on mobile players on trunk and default editors - .yamato/_run-all.yml#run_all_project_tests_mobile_standalone_trunk - - .yamato/_run-all.yml#run_all_project_tests_mobile_standalone_6000 - # Run Runtime tests on console players on trunk and 6000.0 editors + - .yamato/_run-all.yml#run_all_project_tests_mobile_standalone_default + # Run Runtime tests on console players on trunk and default editors - .yamato/_run-all.yml#run_all_project_tests_console_standalone_trunk - - .yamato/_run-all.yml#run_all_project_tests_console_standalone_6000 - # Build player for webgl platform on trunk and 6000.0 editors + - .yamato/_run-all.yml#run_all_project_tests_console_standalone_default + # Build player for webgl platform on trunk and default editors - .yamato/_run-all.yml#run_all_webgl_builds_trunk - - .yamato/_run-all.yml#run_all_webgl_builds_6000 - # Run Runtime tests against cmb service on trunk and 6000.0 editors + - .yamato/_run-all.yml#run_all_webgl_builds_default + # Run Runtime tests against cmb service on trunk and default editors - .yamato/_run-all.yml#run_all_project_tests_cmb_service_trunk - - .yamato/_run-all.yml#run_all_project_tests_cmb_service_6000 - # Build player for webgl platform on trunk and 6000.0 editors + - .yamato/_run-all.yml#run_all_project_tests_cmb_service_default + # Build player for webgl platform on trunk and default editors - .yamato/project-updated-dependencies-test.yml#updated-dependencies_testproject_NGO_ubuntu_trunk - - .yamato/project-updated-dependencies-test.yml#updated-dependencies_testproject_NGO_win_6000.0 + - .yamato/project-updated-dependencies-test.yml#updated-dependencies_testproject_NGO_win_{{ validation_editors.default }} # Run API validation to early-detect all new APIs that would force us to release new minor version of the package. Note that for this to work the package version in package.json must correspond to "actual package state" which means that it should be higher than last released version - .yamato/vetting-test.yml#vetting_test + # Run code coverage test + - .yamato/code-coverage.yml#code_coverage_ubuntu_{{ validation_editors.default }} # Run all tests on weekly bases @@ -167,7 +173,7 @@ develop_weekly_trunk: name: "\U0001F319 [Weekly] Run All Tests" triggers: recurring: - - branch: develop-2.0.0 + - branch: develop-3.x.x frequency: weekly rerun: always dependencies: @@ -187,5 +193,3 @@ develop_weekly_trunk: - .yamato/_run-all.yml#run_all_webgl_builds # Run Runtime tests against CMB service - .yamato/_run-all.yml#run_all_project_tests_cmb_service - # Run code coverage test - - .yamato/code-coverage.yml#code_coverage_ubuntu_{{ validation_editors.default }} diff --git a/.yamato/code-coverage.yml b/.yamato/code-coverage.yml index caeeff72ec..64a2692225 100644 --- a/.yamato/code-coverage.yml +++ b/.yamato/code-coverage.yml @@ -39,7 +39,7 @@ code_coverage_{{ platform.name }}_{{ editor }}: commands: - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor {% if platform.name == "mac" %} --arch arm64 {% endif %} # For macOS we use ARM64 models - upm-pvp create-test-project test-project --packages "upm-ci~/packages/*.tgz" --unity .Editor - - UnifiedTestRunner --suite=editor --suite=playmode --editor-location=.Editor --testproject=test-project --enable-code-coverage coverage-upload-options="reportsDir:$PWD/test-results/CoverageResults;name:NGOv2_{{ platform.name }}_{{ editor }};flags:NGOv2_{{ platform.name }}_{{ editor }};verbose" --coverage-results-path=$PWD/test-results/CoverageResults --coverage-options="generateHtmlReport;generateAdditionalMetrics;assemblyFilters:+Unity.Netcode.Editor,+Unity.Netcode.Runtime" --extra-editor-arg=--burst-disable-compilation --timeout={{ test_timeout }} --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --artifacts-path=test-results + - UnifiedTestRunner --suite=editor --suite=playmode --editor-location=.Editor --testproject=test-project --enable-code-coverage --coverage-upload-options="reportsDir:$PWD/test-results/CoverageResults;name:NGOv2_{{ platform.name }}_{{ editor }};flags:NGOv2_{{ platform.name }}_{{ editor }};verbose" --coverage-results-path=$PWD/test-results/CoverageResults --coverage-options="generateHtmlReport;generateAdditionalMetrics;assemblyFilters:+Unity.Netcode.Editor,+Unity.Netcode.Runtime" --extra-editor-arg=--burst-disable-compilation --timeout={{ test_timeout }} --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --artifacts-path=test-results artifacts: logs: paths: diff --git a/.yamato/package-pack.yml b/.yamato/package-pack.yml index 94c940b74b..7554274fe8 100644 --- a/.yamato/package-pack.yml +++ b/.yamato/package-pack.yml @@ -40,7 +40,7 @@ package_pack_-_ngo_{{ platform.name }}: {% endif %} timeout: 0.25 variables: - XRAY_PROFILE: "gold ./pvpExceptions.json" + XRAY_PROFILE: "gold ./pvpExceptions.json rme" commands: - upm-pvp pack "com.unity.netcode.gameobjects" --output upm-ci~/packages - upm-pvp xray --packages "upm-ci~/packages/com.unity.netcode.gameobjects*.tgz" --results pvp-results diff --git a/.yamato/package-tests.yml b/.yamato/package-tests.yml index e9df795f10..50799533dd 100644 --- a/.yamato/package-tests.yml +++ b/.yamato/package-tests.yml @@ -36,7 +36,7 @@ package_test_-_ngo_{{ editor }}_{{ platform.name }}: model: {{ platform.model }} # This is set only in platforms where we want non-default model to use (more information in project.metafile) {% endif %} variables: - XRAY_PROFILE: "gold ./pvpExceptions.json" + XRAY_PROFILE: "gold ./pvpExceptions.json rme" UNITY_EXT_LOGGING: 1 commands: - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor {% if platform.name == "mac" %} --arch arm64 {% endif %} # For macOS we use ARM64 models. diff --git a/.yamato/project-standards.yml b/.yamato/project-standards.yml index 24380a152e..137a9f1c50 100644 --- a/.yamato/project-standards.yml +++ b/.yamato/project-standards.yml @@ -30,7 +30,7 @@ {% for project in projects.all -%} {% for platform in test_platforms.default -%} -{% for editor in validation_editors.default -%} +{% for editor in validation_editors.minimal -%} standards_{{ platform.name }}_{{ project.name }}_{{ editor }}: name: Standards Check - NGO {{ project.name }} [{{ platform.name }}, {{ editor }}] agent: @@ -48,8 +48,7 @@ standards_{{ platform.name }}_{{ project.name }}_{{ editor }}: - unity-downloader-cli --fast --wait -u {{ editor }} -c editor {% if platform.name == "mac" %} --arch arm64 {% endif %} # For macOS we use ARM64 models. Downloads basic editor - .Editor/Unity -batchmode -nographics -logFile - -executeMethod Packages.Rider.Editor.RiderScriptEditor.SyncSolution -projectPath {{ project.path }} -quit # This command is used to invoke Unity in a "headless" mode. It's used to sync the project - dotnet run --project=dotnet-tools/netcode.standards -- --project={{ project.path }} --fix # Auto-fix formatting issues - - git checkout -- {{ project.path }}/Packages/manifest.json {{ project.path }}/Packages/packages-lock.json {{ project.path }}/ProjectSettings/ProjectVersion.txt 2>/dev/null || true # Restore files that Unity may have modified (we only want to check code formatting) - - 'git diff --exit-code || (echo "ERROR: Code formatting issues found. Run dotnet run --project=dotnet-tools/netcode.standards -- --project={{ project.path }} --fix locally and commit the changes." && exit 1)' # Fail if formatter made any changes + - 'git diff --exit-code || (echo "ERROR: Code formatting issues found. Run dotnet run --project=dotnet-tools/netcode.standards -- --project={{ project.path }} --fix locally and commit the changes. Note that dotnet version that was used in this check depends on the image that was used so local results may differ (see the first command of this job to know which dotnet version was used)" && exit 1)' # Fail if formatter made any changes {% endfor -%} {% endfor -%} {% endfor -%} \ No newline at end of file diff --git a/.yamato/project.metafile b/.yamato/project.metafile index 871ff18710..f1e5c5c924 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -24,7 +24,7 @@ small_agent_platform: - name: ubuntu type: Unity::VM - image: package-ci/ubuntu-22.04:v4.83.0 + image: package-ci/ubuntu-22.04:v4.87.0 flavor: b1.small @@ -39,15 +39,15 @@ test_platforms: default: - name: ubuntu type: Unity::VM - image: package-ci/ubuntu-22.04:v4.83.0 + image: package-ci/ubuntu-22.04:v4.87.0 flavor: b1.large standalone: StandaloneLinux64 desktop: - name: ubuntu type: Unity::VM - image: package-ci/ubuntu-22.04:v4.83.0 + image: package-ci/ubuntu-22.04:v4.87.0 flavor: b1.large - smaller_flavor: b1.medium + smaller_flavor: b1.large larger_flavor: b1.xlarge standalone: StandaloneLinux64 - name: win @@ -117,12 +117,17 @@ test_platforms: # flavor: b1.large # larger_flavor: b1.xlarge # standalone: PS5 - - name: switch - type: Unity::VM - image: package-ci/win10-switch:v4 - flavor: b1.large - larger_flavor: b1.xlarge - standalone: Switch + # - name: switch --> TEMPORARILY DISABLED. SEE MTT-12118 + # The NintendoSDK on the win10-switch:v4 Bokken image is incomplete for the SDK + # version the newer editor Playback Engines require: 18_3_0 is missing the + # nintendo-switch-support lib the new toolchain links against, and 21_4_3 is missing the + # Tools\Graphics\NvnTools (GraphicsConverter) libraries needed to package textures. + # Neither is fixable from .yamato; re-enable once Package CI ships a complete SDK on the image. + # type: Unity::VM + # image: package-ci/win10-switch:v4 + # flavor: b1.large + # larger_flavor: b1.xlarge + # standalone: Switch - name: GameCoreXboxOne type: Unity::VM image: package-ci/win10-xbox:v4 @@ -148,13 +153,13 @@ test_platforms: # flavor: b1.large # larger_flavor: b1.xlarge # standalone: PS5 - - name: switch - type: Unity::console::switch - image: package-ci/win10-switch:v4 - flavor: b1.large - larger_flavor: b1.xlarge - standalone: Switch - base: win + # - name: switch --> TEMPORARILY DISABLED. SEE MTT-12118 (incomplete NintendoSDK on win10-switch:v4 image) + # type: Unity::console::switch + # image: package-ci/win10-switch:v4 + # flavor: b1.large + # larger_flavor: b1.xlarge + # standalone: Switch + # base: win - name: GameCoreXboxOne type: Unity::console::xbox image: package-ci/win10-xbox:v4 @@ -174,17 +179,15 @@ test_platforms: validation_editors: default: - - 6000.3 + - 6000.6 all: - - 6000.0 - - 6000.3 - - 6000.4 - - 6000.5 + - 6000.6.0b5 + - 6000.6 - trunk - - a4ce83e807ca9aff8394d1cc07341168dc49df03 + - 5fe7931aab8c4fff9274e15ef0800125c68b8d6a minimal: - - 6000.0 -pinnedTrunk: a4ce83e807ca9aff8394d1cc07341168dc49df03 + - 6000.6.0b5 +pinnedTrunk: 5fe7931aab8c4fff9274e15ef0800125c68b8d6a # Scripting backends used by Standalone RunTimeTests--------------------------------------------------- diff --git a/.yamato/wrench/api-validation-jobs.yml b/.yamato/wrench/api-validation-jobs.yml index d99abd06f0..611902cc9a 100644 --- a/.yamato/wrench/api-validation-jobs.yml +++ b/.yamato/wrench/api-validation-jobs.yml @@ -60,8 +60,8 @@ api_validation_-_netcode_gameobjects_-_6000_0_-_win10: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 diff --git a/.yamato/wrench/package-pack-jobs.yml b/.yamato/wrench/package-pack-jobs.yml index 3994a564c2..a40678465b 100644 --- a/.yamato/wrench/package-pack-jobs.yml +++ b/.yamato/wrench/package-pack-jobs.yml @@ -29,5 +29,5 @@ package_pack_-_netcode_gameobjects: UPMCI_ACK_LARGE_PACKAGE: 1 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 diff --git a/.yamato/wrench/preview-a-p-v.yml b/.yamato/wrench/preview-a-p-v.yml index 55736dc6b8..bafc711428 100644 --- a/.yamato/wrench/preview-a-p-v.yml +++ b/.yamato/wrench/preview-a-p-v.yml @@ -16,18 +16,18 @@ all_preview_apv_jobs: - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_3_-_macos13 - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_3_-_ubuntu2204 - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_3_-_win10 - - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_4_-_macos13 - - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_4_-_ubuntu2204 - - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_4_-_win10 - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_5_-_macos13 - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_5_-_ubuntu2204 - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_5_-_win10 - - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_6_-_macos13 + - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_6_-_macos13arm - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_6_-_ubuntu2204 - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_6_-_win10 + - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_7_-_macos13arm + - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_7_-_ubuntu2204 + - path: .yamato/wrench/preview-a-p-v.yml#preview_apv_-_6000_7_-_win10 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Functional tests for dependents found in the latest 6000.0 manifest (MacOS). preview_apv_-_6000_0_-_macos13: @@ -82,10 +82,10 @@ preview_apv_-_6000_0_-_macos13: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Functional tests for dependents found in the latest 6000.0 manifest (Ubuntu). preview_apv_-_6000_0_-_ubuntu2204: @@ -140,10 +140,10 @@ preview_apv_-_6000_0_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Functional tests for dependents found in the latest 6000.0 manifest (Windows). preview_apv_-_6000_0_-_win10: @@ -199,10 +199,10 @@ preview_apv_-_6000_0_-_win10: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Functional tests for dependents found in the latest 6000.3 manifest (MacOS). preview_apv_-_6000_3_-_macos13: @@ -257,10 +257,10 @@ preview_apv_-_6000_3_-_macos13: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Functional tests for dependents found in the latest 6000.3 manifest (Ubuntu). preview_apv_-_6000_3_-_ubuntu2204: @@ -315,10 +315,10 @@ preview_apv_-_6000_3_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Functional tests for dependents found in the latest 6000.3 manifest (Windows). preview_apv_-_6000_3_-_win10: @@ -374,14 +374,14 @@ preview_apv_-_6000_3_-_win10: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.4 manifest (MacOS). -preview_apv_-_6000_4_-_macos13: - name: Preview APV - 6000.4 - macos13 +# Functional tests for dependents found in the latest 6000.5 manifest (MacOS). +preview_apv_-_6000_5_-_macos13: + name: Preview APV - 6000.5 - macos13 agent: image: package-ci/macos-13:v4 type: Unity::VM::osx @@ -394,10 +394,10 @@ preview_apv_-_6000_4_-_macos13: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u 6000.4/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.4 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.5 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - command: echo 'Skipping Editor Manifest Validator as it is only supported on Windows' after: - command: bash .yamato/generated-scripts/infrastructure-instability-detection-mac.sh @@ -432,14 +432,14 @@ preview_apv_-_6000_4_-_macos13: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.4 manifest (Ubuntu). -preview_apv_-_6000_4_-_ubuntu2204: - name: Preview APV - 6000.4 - ubuntu2204 +# Functional tests for dependents found in the latest 6000.5 manifest (Ubuntu). +preview_apv_-_6000_5_-_ubuntu2204: + name: Preview APV - 6000.5 - ubuntu2204 agent: image: package-ci/ubuntu-22.04:v4 type: Unity::VM @@ -452,10 +452,10 @@ preview_apv_-_6000_4_-_ubuntu2204: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u 6000.4/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.4 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.5 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - command: echo 'Skipping Editor Manifest Validator as it is only supported on Windows' after: - command: bash .yamato/generated-scripts/infrastructure-instability-detection-linux.sh @@ -490,14 +490,14 @@ preview_apv_-_6000_4_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.4 manifest (Windows). -preview_apv_-_6000_4_-_win10: - name: Preview APV - 6000.4 - win10 +# Functional tests for dependents found in the latest 6000.5 manifest (Windows). +preview_apv_-_6000_5_-_win10: + name: Preview APV - 6000.5 - win10 agent: image: package-ci/win10:v4 type: Unity::VM @@ -511,11 +511,11 @@ preview_apv_-_6000_4_-_win10: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u 6000.4/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.4 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - - command: python PythonScripts/editor_manifest_validator.py --version=6000.4 --wrench-config=.yamato/wrench/wrench_config.json + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.5 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/editor_manifest_validator.py --version=6000.5 --wrench-config=.yamato/wrench/wrench_config.json after: - command: .yamato\generated-scripts\infrastructure-instability-detection-win.cmd artifacts: @@ -549,18 +549,19 @@ preview_apv_-_6000_4_-_win10: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.5 manifest (MacOS). -preview_apv_-_6000_5_-_macos13: - name: Preview APV - 6000.5 - macos13 +# Functional tests for dependents found in the latest 6000.6 manifest (MacOS). +preview_apv_-_6000_6_-_macos13arm: + name: Preview APV - 6000.6 - macos13arm agent: - image: package-ci/macos-13:v4 + image: package-ci/macos-13-arm64:v4 type: Unity::VM::osx - flavor: b1.xlarge + flavor: m1.mac + model: M1 commands: - command: curl https://artifactory.prd.it.unity3d.com/artifactory/stevedore-unity-internal/wrench-localapv/1-3-3_51b4e6affb4c10b90f92db9551b9dc80c5520794983600cff4fa925111db116d.zip -o wrench-localapv.zip - command: 7z x -aoa wrench-localapv.zip @@ -569,10 +570,10 @@ preview_apv_-_6000_5_-_macos13: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.6/staging -c editor --path .Editor --fast --arch arm64 timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.5 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.6 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - command: echo 'Skipping Editor Manifest Validator as it is only supported on Windows' after: - command: bash .yamato/generated-scripts/infrastructure-instability-detection-mac.sh @@ -607,14 +608,14 @@ preview_apv_-_6000_5_-_macos13: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.5 manifest (Ubuntu). -preview_apv_-_6000_5_-_ubuntu2204: - name: Preview APV - 6000.5 - ubuntu2204 +# Functional tests for dependents found in the latest 6000.6 manifest (Ubuntu). +preview_apv_-_6000_6_-_ubuntu2204: + name: Preview APV - 6000.6 - ubuntu2204 agent: image: package-ci/ubuntu-22.04:v4 type: Unity::VM @@ -627,10 +628,10 @@ preview_apv_-_6000_5_-_ubuntu2204: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.6/staging -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.5 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.6 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - command: echo 'Skipping Editor Manifest Validator as it is only supported on Windows' after: - command: bash .yamato/generated-scripts/infrastructure-instability-detection-linux.sh @@ -665,14 +666,14 @@ preview_apv_-_6000_5_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.5 manifest (Windows). -preview_apv_-_6000_5_-_win10: - name: Preview APV - 6000.5 - win10 +# Functional tests for dependents found in the latest 6000.6 manifest (Windows). +preview_apv_-_6000_6_-_win10: + name: Preview APV - 6000.6 - win10 agent: image: package-ci/win10:v4 type: Unity::VM @@ -686,11 +687,11 @@ preview_apv_-_6000_5_-_win10: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.6/staging -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.5 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - - command: python PythonScripts/editor_manifest_validator.py --version=6000.5 --wrench-config=.yamato/wrench/wrench_config.json + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.6 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/editor_manifest_validator.py --version=6000.6 --wrench-config=.yamato/wrench/wrench_config.json after: - command: .yamato\generated-scripts\infrastructure-instability-detection-win.cmd artifacts: @@ -724,18 +725,19 @@ preview_apv_-_6000_5_-_win10: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.6 manifest (MacOS). -preview_apv_-_6000_6_-_macos13: - name: Preview APV - 6000.6 - macos13 +# Functional tests for dependents found in the latest 6000.7 manifest (MacOS). +preview_apv_-_6000_7_-_macos13arm: + name: Preview APV - 6000.7 - macos13arm agent: - image: package-ci/macos-13:v4 + image: package-ci/macos-13-arm64:v4 type: Unity::VM::osx - flavor: b1.xlarge + flavor: m1.mac + model: M1 commands: - command: curl https://artifactory.prd.it.unity3d.com/artifactory/stevedore-unity-internal/wrench-localapv/1-3-3_51b4e6affb4c10b90f92db9551b9dc80c5520794983600cff4fa925111db116d.zip -o wrench-localapv.zip - command: 7z x -aoa wrench-localapv.zip @@ -744,10 +746,10 @@ preview_apv_-_6000_6_-_macos13: - command: npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm timeout: 20 retries: 10 - - command: unity-downloader-cli -u trunk -c editor --path .Editor --fast + - command: unity-downloader-cli -u trunk -c editor --path .Editor --fast --arch arm64 timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.6 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.7 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - command: echo 'Skipping Editor Manifest Validator as it is only supported on Windows' after: - command: bash .yamato/generated-scripts/infrastructure-instability-detection-mac.sh @@ -782,14 +784,14 @@ preview_apv_-_6000_6_-_macos13: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.6 manifest (Ubuntu). -preview_apv_-_6000_6_-_ubuntu2204: - name: Preview APV - 6000.6 - ubuntu2204 +# Functional tests for dependents found in the latest 6000.7 manifest (Ubuntu). +preview_apv_-_6000_7_-_ubuntu2204: + name: Preview APV - 6000.7 - ubuntu2204 agent: image: package-ci/ubuntu-22.04:v4 type: Unity::VM @@ -805,7 +807,7 @@ preview_apv_-_6000_6_-_ubuntu2204: - command: unity-downloader-cli -u trunk -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.6 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.7 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - command: echo 'Skipping Editor Manifest Validator as it is only supported on Windows' after: - command: bash .yamato/generated-scripts/infrastructure-instability-detection-linux.sh @@ -840,14 +842,14 @@ preview_apv_-_6000_6_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 -# Functional tests for dependents found in the latest 6000.6 manifest (Windows). -preview_apv_-_6000_6_-_win10: - name: Preview APV - 6000.6 - win10 +# Functional tests for dependents found in the latest 6000.7 manifest (Windows). +preview_apv_-_6000_7_-_win10: + name: Preview APV - 6000.7 - win10 agent: image: package-ci/win10:v4 type: Unity::VM @@ -864,8 +866,8 @@ preview_apv_-_6000_6_-_win10: - command: unity-downloader-cli -u trunk -c editor --path .Editor --fast timeout: 10 retries: 3 - - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.6 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ - - command: python PythonScripts/editor_manifest_validator.py --version=6000.6 --wrench-config=.yamato/wrench/wrench_config.json + - command: python PythonScripts/preview_apv.py --wrench-config=.yamato/wrench/wrench_config.json --editor-version=6000.7 --testsuite=editor,playmode --artifacts-path=PreviewApvArtifacts~ + - command: python PythonScripts/editor_manifest_validator.py --version=6000.7 --wrench-config=.yamato/wrench/wrench_config.json after: - command: .yamato\generated-scripts\infrastructure-instability-detection-win.cmd artifacts: @@ -899,8 +901,8 @@ preview_apv_-_6000_6_-_win10: UNITY_LICENSING_SERVER_DELETE_NUL: 0 UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 diff --git a/.yamato/wrench/promotion-jobs.yml b/.yamato/wrench/promotion-jobs.yml index e4beeec200..06dda4cdcf 100644 --- a/.yamato/wrench/promotion-jobs.yml +++ b/.yamato/wrench/promotion-jobs.yml @@ -91,102 +91,102 @@ publish_dry_run_netcode_gameobjects: UTR: location: results/UTR/validate-netcode.gameobjects-6000.3-win10 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_4_-_macos13 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_macos13 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.4-macos13 + location: results/pvp/validate-netcode.gameobjects-6000.5-macos13 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.4-macos13 + location: results/UTR/validate-netcode.gameobjects-6000.5-macos13 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_4_-_ubuntu2204 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.4-ubuntu2204 + location: results/pvp/validate-netcode.gameobjects-6000.5-ubuntu2204 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.4-ubuntu2204 + location: results/UTR/validate-netcode.gameobjects-6000.5-ubuntu2204 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_4_-_win10 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_win10 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.4-win10 + location: results/pvp/validate-netcode.gameobjects-6000.5-win10 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.4-win10 + location: results/UTR/validate-netcode.gameobjects-6000.5-win10 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_macos13 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_macos13arm specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.5-macos13 + location: results/pvp/validate-netcode.gameobjects-6000.6-macos13arm unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.5-macos13 + location: results/UTR/validate-netcode.gameobjects-6000.6-macos13arm unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.5-ubuntu2204 + location: results/pvp/validate-netcode.gameobjects-6000.6-ubuntu2204 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.5-ubuntu2204 + location: results/UTR/validate-netcode.gameobjects-6000.6-ubuntu2204 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_win10 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_win10 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.5-win10 + location: results/pvp/validate-netcode.gameobjects-6000.6-win10 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.5-win10 + location: results/UTR/validate-netcode.gameobjects-6000.6-win10 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_macos13 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_7_-_macos13arm specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.6-macos13 + location: results/pvp/validate-netcode.gameobjects-6000.7-macos13arm unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.6-macos13 + location: results/UTR/validate-netcode.gameobjects-6000.7-macos13arm unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_7_-_ubuntu2204 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.6-ubuntu2204 + location: results/pvp/validate-netcode.gameobjects-6000.7-ubuntu2204 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.6-ubuntu2204 + location: results/UTR/validate-netcode.gameobjects-6000.7-ubuntu2204 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_win10 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_7_-_win10 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.6-win10 + location: results/pvp/validate-netcode.gameobjects-6000.7-win10 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.6-win10 + location: results/UTR/validate-netcode.gameobjects-6000.7-win10 unzip: true variables: UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 # Publish for netcode.gameobjects to https://artifactory-slo.bf.unity3d.com/artifactory/api/npm/upm-npm publish_netcode_gameobjects: @@ -273,100 +273,101 @@ publish_netcode_gameobjects: UTR: location: results/UTR/validate-netcode.gameobjects-6000.3-win10 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_4_-_macos13 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_macos13 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.4-macos13 + location: results/pvp/validate-netcode.gameobjects-6000.5-macos13 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.4-macos13 + location: results/UTR/validate-netcode.gameobjects-6000.5-macos13 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_4_-_ubuntu2204 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.4-ubuntu2204 + location: results/pvp/validate-netcode.gameobjects-6000.5-ubuntu2204 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.4-ubuntu2204 + location: results/UTR/validate-netcode.gameobjects-6000.5-ubuntu2204 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_4_-_win10 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_win10 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.4-win10 + location: results/pvp/validate-netcode.gameobjects-6000.5-win10 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.4-win10 + location: results/UTR/validate-netcode.gameobjects-6000.5-win10 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_macos13 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_macos13arm specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.5-macos13 + location: results/pvp/validate-netcode.gameobjects-6000.6-macos13arm unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.5-macos13 + location: results/UTR/validate-netcode.gameobjects-6000.6-macos13arm unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.5-ubuntu2204 + location: results/pvp/validate-netcode.gameobjects-6000.6-ubuntu2204 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.5-ubuntu2204 + location: results/UTR/validate-netcode.gameobjects-6000.6-ubuntu2204 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_5_-_win10 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_win10 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.5-win10 + location: results/pvp/validate-netcode.gameobjects-6000.6-win10 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.5-win10 + location: results/UTR/validate-netcode.gameobjects-6000.6-win10 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_macos13 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_7_-_macos13arm specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.6-macos13 + location: results/pvp/validate-netcode.gameobjects-6000.7-macos13arm unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.6-macos13 + location: results/UTR/validate-netcode.gameobjects-6000.7-macos13arm unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_7_-_ubuntu2204 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.6-ubuntu2204 + location: results/pvp/validate-netcode.gameobjects-6000.7-ubuntu2204 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.6-ubuntu2204 + location: results/UTR/validate-netcode.gameobjects-6000.7-ubuntu2204 unzip: true - - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_6_-_win10 + - path: .yamato/wrench/validation-jobs.yml#validate_-_netcode_gameobjects_-_6000_7_-_win10 specific_options: packages: ignore_artifact: true pvp-results: - location: results/pvp/validate-netcode.gameobjects-6000.6-win10 + location: results/pvp/validate-netcode.gameobjects-6000.7-win10 unzip: true UTR: - location: results/UTR/validate-netcode.gameobjects-6000.6-win10 + location: results/UTR/validate-netcode.gameobjects-6000.7-win10 unzip: true variables: UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 + allow_on: branch match "^release/.*" metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 diff --git a/.yamato/wrench/recipe-regeneration.yml b/.yamato/wrench/recipe-regeneration.yml index 1550408dd2..343a1a9b85 100644 --- a/.yamato/wrench/recipe-regeneration.yml +++ b/.yamato/wrench/recipe-regeneration.yml @@ -31,5 +31,5 @@ test_-_wrench_jobs_up_to_date: cancel_old_ci: true metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 diff --git a/.yamato/wrench/validation-jobs.yml b/.yamato/wrench/validation-jobs.yml index f247f7d6a4..88d03e589b 100644 --- a/.yamato/wrench/validation-jobs.yml +++ b/.yamato/wrench/validation-jobs.yml @@ -69,10 +69,10 @@ validate_-_netcode_gameobjects_-_6000_0_-_macos13: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects @@ -139,10 +139,10 @@ validate_-_netcode_gameobjects_-_6000_0_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects @@ -209,10 +209,10 @@ validate_-_netcode_gameobjects_-_6000_0_-_win10: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects @@ -279,10 +279,10 @@ validate_-_netcode_gameobjects_-_6000_3_-_macos13: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects @@ -349,10 +349,10 @@ validate_-_netcode_gameobjects_-_6000_3_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects @@ -419,16 +419,16 @@ validate_-_netcode_gameobjects_-_6000_3_-_win10: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.4 - macos13 (6000.4 - MacOS). -validate_-_netcode_gameobjects_-_6000_4_-_macos13: - name: Validate - netcode.gameobjects - 6000.4 - macos13 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.5 - macos13 (6000.5 - MacOS). +validate_-_netcode_gameobjects_-_6000_5_-_macos13: + name: Validate - netcode.gameobjects - 6000.5 - macos13 agent: image: package-ci/macos-13:v4 type: Unity::VM::osx @@ -438,7 +438,7 @@ validate_-_netcode_gameobjects_-_6000_4_-_macos13: - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u 6000.4/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -489,16 +489,16 @@ validate_-_netcode_gameobjects_-_6000_4_-_macos13: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.4 - ubuntu2204 (6000.4 - Ubuntu). -validate_-_netcode_gameobjects_-_6000_4_-_ubuntu2204: - name: Validate - netcode.gameobjects - 6000.4 - ubuntu2204 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.5 - ubuntu2204 (6000.5 - Ubuntu). +validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204: + name: Validate - netcode.gameobjects - 6000.5 - ubuntu2204 agent: image: package-ci/ubuntu-22.04:v4 type: Unity::VM @@ -508,7 +508,7 @@ validate_-_netcode_gameobjects_-_6000_4_-_ubuntu2204: - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u 6000.4/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -559,16 +559,16 @@ validate_-_netcode_gameobjects_-_6000_4_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.4 - win10 (6000.4 - Windows). -validate_-_netcode_gameobjects_-_6000_4_-_win10: - name: Validate - netcode.gameobjects - 6000.4 - win10 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.5 - win10 (6000.5 - Windows). +validate_-_netcode_gameobjects_-_6000_5_-_win10: + name: Validate - netcode.gameobjects - 6000.5 - win10 agent: image: package-ci/win10:v4 type: Unity::VM @@ -578,7 +578,7 @@ validate_-_netcode_gameobjects_-_6000_4_-_win10: - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u 6000.4/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -629,26 +629,27 @@ validate_-_netcode_gameobjects_-_6000_4_-_win10: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.5 - macos13 (6000.5 - MacOS). -validate_-_netcode_gameobjects_-_6000_5_-_macos13: - name: Validate - netcode.gameobjects - 6000.5 - macos13 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.6 - macos13arm (6000.6 - MacOS). +validate_-_netcode_gameobjects_-_6000_6_-_macos13arm: + name: Validate - netcode.gameobjects - 6000.6 - macos13arm agent: - image: package-ci/macos-13:v4 + image: package-ci/macos-13-arm64:v4 type: Unity::VM::osx - flavor: b1.xlarge + flavor: m1.mac + model: M1 commands: - command: curl https://artifactory.prd.it.unity3d.com/artifactory/stevedore-unity-internal/wrench-localapv/1-3-3_51b4e6affb4c10b90f92db9551b9dc80c5520794983600cff4fa925111db116d.zip -o wrench-localapv.zip - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.6/staging -c editor --path .Editor --fast --arch arm64 timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -699,16 +700,16 @@ validate_-_netcode_gameobjects_-_6000_5_-_macos13: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.5 - ubuntu2204 (6000.5 - Ubuntu). -validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204: - name: Validate - netcode.gameobjects - 6000.5 - ubuntu2204 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.6 - ubuntu2204 (6000.6 - Ubuntu). +validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204: + name: Validate - netcode.gameobjects - 6000.6 - ubuntu2204 agent: image: package-ci/ubuntu-22.04:v4 type: Unity::VM @@ -718,7 +719,7 @@ validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204: - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.6/staging -c editor --path .Editor --fast timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -769,16 +770,16 @@ validate_-_netcode_gameobjects_-_6000_5_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.5 - win10 (6000.5 - Windows). -validate_-_netcode_gameobjects_-_6000_5_-_win10: - name: Validate - netcode.gameobjects - 6000.5 - win10 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.6 - win10 (6000.6 - Windows). +validate_-_netcode_gameobjects_-_6000_6_-_win10: + name: Validate - netcode.gameobjects - 6000.6 - win10 agent: image: package-ci/win10:v4 type: Unity::VM @@ -788,7 +789,7 @@ validate_-_netcode_gameobjects_-_6000_5_-_win10: - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u 6000.5/staging -c editor --path .Editor --fast + - command: unity-downloader-cli -u 6000.6/staging -c editor --path .Editor --fast timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -839,26 +840,27 @@ validate_-_netcode_gameobjects_-_6000_5_-_win10: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.6 - macos13 (6000.6 - MacOS). -validate_-_netcode_gameobjects_-_6000_6_-_macos13: - name: Validate - netcode.gameobjects - 6000.6 - macos13 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.7 - macos13arm (6000.7 - MacOS). +validate_-_netcode_gameobjects_-_6000_7_-_macos13arm: + name: Validate - netcode.gameobjects - 6000.7 - macos13arm agent: - image: package-ci/macos-13:v4 + image: package-ci/macos-13-arm64:v4 type: Unity::VM::osx - flavor: b1.xlarge + flavor: m1.mac + model: M1 commands: - command: curl https://artifactory.prd.it.unity3d.com/artifactory/stevedore-unity-internal/wrench-localapv/1-3-3_51b4e6affb4c10b90f92db9551b9dc80c5520794983600cff4fa925111db116d.zip -o wrench-localapv.zip - command: 7z x -aoa wrench-localapv.zip - command: pip install semver requests --index-url https://artifactory-slo.bf.unity3d.com/artifactory/api/pypi/pypi/simple - command: python PythonScripts/print_machine_info.py - - command: unity-downloader-cli -u trunk -c editor --path .Editor --fast + - command: unity-downloader-cli -u trunk -c editor --path .Editor --fast --arch arm64 timeout: 20 retries: 3 - command: upm-pvp create-test-project testproject --packages "upm-ci~/packages/*.tgz" --filter "com.unity.netcode.gameobjects com.unity.netcode.gameobjects.tests" --unity .Editor @@ -909,16 +911,16 @@ validate_-_netcode_gameobjects_-_6000_6_-_macos13: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.6 - ubuntu2204 (6000.6 - Ubuntu). -validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204: - name: Validate - netcode.gameobjects - 6000.6 - ubuntu2204 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.7 - ubuntu2204 (6000.7 - Ubuntu). +validate_-_netcode_gameobjects_-_6000_7_-_ubuntu2204: + name: Validate - netcode.gameobjects - 6000.7 - ubuntu2204 agent: image: package-ci/ubuntu-22.04:v4 type: Unity::VM @@ -979,16 +981,16 @@ validate_-_netcode_gameobjects_-_6000_6_-_ubuntu2204: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects -# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.6 - win10 (6000.6 - Windows). -validate_-_netcode_gameobjects_-_6000_6_-_win10: - name: Validate - netcode.gameobjects - 6000.6 - win10 +# PVP Editor and Playmode tests for Validate - netcode.gameobjects - 6000.7 - win10 (6000.7 - Windows). +validate_-_netcode_gameobjects_-_6000_7_-_win10: + name: Validate - netcode.gameobjects - 6000.7 - win10 agent: image: package-ci/win10:v4 type: Unity::VM @@ -1049,10 +1051,10 @@ validate_-_netcode_gameobjects_-_6000_6_-_win10: UNITY_LICENSING_SERVER_DELETE_ULF: 0 UNITY_LICENSING_SERVER_TOOLSET: pro UPMPVP_ACK_UPMPVP_DOES_NO_API_VALIDATION: 1 - UPMPVP_CONTEXT_WRENCH: 2.7.0.0 + UPMPVP_CONTEXT_WRENCH: 3.2.1.0 metadata: Job Maintainers: '#rm-packageworks' - Wrench: 2.7.0.0 + Wrench: 3.2.1.0 labels: - Packages:netcode.gameobjects diff --git a/.yamato/wrench/wrench_config.json b/.yamato/wrench/wrench_config.json index 2d7eb092b2..96b58ebf4f 100644 --- a/.yamato/wrench/wrench_config.json +++ b/.yamato/wrench/wrench_config.json @@ -39,7 +39,7 @@ }, "publishing_job": ".yamato/wrench/promotion-jobs.yml#publish_netcode_gameobjects", "branch_pattern": "ReleaseSlash", - "wrench_version": "2.7.0.0", + "wrench_version": "3.2.1.0", "pvp_exemption_path": ".yamato/wrench/pvp-exemptions.json", "cs_project_path": "Tools/CI/NGO.Cookbook.csproj" } \ No newline at end of file diff --git a/Examples/CharacterControllerMovingBodies/Assets/Scripts/ExtendedNetworkManager.cs b/Examples/CharacterControllerMovingBodies/Assets/Scripts/ExtendedNetworkManager.cs index 2c291bbe89..d2d9b29083 100644 --- a/Examples/CharacterControllerMovingBodies/Assets/Scripts/ExtendedNetworkManager.cs +++ b/Examples/CharacterControllerMovingBodies/Assets/Scripts/ExtendedNetworkManager.cs @@ -10,7 +10,7 @@ using SessionState = Unity.Services.Multiplayer.SessionState; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/CharacterControllerMovingBodies/Assets/Scripts/MoverScriptNoRigidbody.cs b/Examples/CharacterControllerMovingBodies/Assets/Scripts/MoverScriptNoRigidbody.cs index 13423f4629..c938e1587b 100644 --- a/Examples/CharacterControllerMovingBodies/Assets/Scripts/MoverScriptNoRigidbody.cs +++ b/Examples/CharacterControllerMovingBodies/Assets/Scripts/MoverScriptNoRigidbody.cs @@ -2,7 +2,7 @@ using Unity.Netcode.Components; using Unity.Netcode; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/CharacterControllerMovingBodies/Assets/Scripts/PlayerBallMotion.cs b/Examples/CharacterControllerMovingBodies/Assets/Scripts/PlayerBallMotion.cs index 7e525d6c33..9696d62f56 100644 --- a/Examples/CharacterControllerMovingBodies/Assets/Scripts/PlayerBallMotion.cs +++ b/Examples/CharacterControllerMovingBodies/Assets/Scripts/PlayerBallMotion.cs @@ -5,7 +5,7 @@ #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// @@ -17,7 +17,7 @@ public class PlayerBallMotionEditor : NetworkTransformEditor { private SerializedProperty m_RotationAxis; private SerializedProperty m_RotationSpeed; - + public override void OnEnable() { diff --git a/Examples/CharacterControllerMovingBodies/Assets/Scripts/RotatingBodyLogic.cs b/Examples/CharacterControllerMovingBodies/Assets/Scripts/RotatingBodyLogic.cs index eaa32a77d6..84977b59a4 100644 --- a/Examples/CharacterControllerMovingBodies/Assets/Scripts/RotatingBodyLogic.cs +++ b/Examples/CharacterControllerMovingBodies/Assets/Scripts/RotatingBodyLogic.cs @@ -3,7 +3,7 @@ using Unity.Netcode.Components; using UnityEngine; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/CharacterControllerMovingBodies/Packages/manifest.json b/Examples/CharacterControllerMovingBodies/Packages/manifest.json index 0a8e3c4a4b..cbeca64318 100644 --- a/Examples/CharacterControllerMovingBodies/Packages/manifest.json +++ b/Examples/CharacterControllerMovingBodies/Packages/manifest.json @@ -2,25 +2,25 @@ "dependencies": { "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", - "com.unity.ads": "4.4.2", - "com.unity.ai.navigation": "2.0.9", - "com.unity.analytics": "3.8.1", - "com.unity.collab-proxy": "2.10.0", + "com.unity.ads": "4.19.0", + "com.unity.ai.navigation": "2.0.13", + "com.unity.analytics": "3.8.2", + "com.unity.collab-proxy": "2.12.4", "com.unity.feature.development": "1.0.2", "com.unity.ide.rider": "3.0.38", - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.multiplayer.center": "1.0.0", - "com.unity.netcode.gameobjects": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-2.0.0", - "com.unity.purchasing": "4.12.2", - "com.unity.services.multiplayer": "1.2.0", - "com.unity.test-framework": "1.6.0", - "com.unity.timeline": "1.8.9", - "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", - "com.unity.transport": "2.6.0", - "com.unity.ugui": "2.0.0", - "com.unity.visualscripting": "1.9.7", - "com.unity.xr.legacyinputhelpers": "2.1.12", + "com.unity.ide.visualstudio": "2.0.26", + "com.unity.multiplayer.center": "2.0.1", + "com.unity.netcode.gameobjects": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-3.x.x", + "com.unity.purchasing": "5.4.1", + "com.unity.services.multiplayer": "2.2.4", + "com.unity.test-framework": "1.8.0", + "com.unity.timeline": "6.6.0", + "com.unity.transport": "6.6.0", + "com.unity.ugui": "2.6.0", + "com.unity.visualscripting": "1.9.11", + "com.unity.xr.legacyinputhelpers": "3.0.1", "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -34,10 +34,13 @@ "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.physicscore2d": "1.0.0", "com.unity.modules.screencapture": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tetgen": "1.0.0", "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.timelinefoundation": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.modules.umbra": "1.0.0", @@ -47,9 +50,9 @@ "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", - "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" } diff --git a/Examples/CharacterControllerMovingBodies/Packages/packages-lock.json b/Examples/CharacterControllerMovingBodies/Packages/packages-lock.json deleted file mode 100644 index c19504fc77..0000000000 --- a/Examples/CharacterControllerMovingBodies/Packages/packages-lock.json +++ /dev/null @@ -1,643 +0,0 @@ -{ - "dependencies": { - "com.unity.2d.sprite": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.2d.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.tilemap": "1.0.0", - "com.unity.modules.uielements": "1.0.0" - } - }, - "com.unity.ads": { - "version": "4.4.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ai.navigation": { - "version": "2.0.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.ai": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.analytics": { - "version": "3.8.1", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.analytics": "1.0.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.burst": { - "version": "1.8.25", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.collab-proxy": { - "version": "2.10.0", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.collections": { - "version": "2.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.23", - "com.unity.mathematics": "1.3.2", - "com.unity.test-framework": "1.4.6", - "com.unity.nuget.mono-cecil": "1.11.5", - "com.unity.test-framework.performance": "3.0.3" - }, - "url": "https://packages.unity.com" - }, - "com.unity.editorcoroutines": { - "version": "1.0.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.ext.nunit": { - "version": "2.0.5", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.feature.development": { - "version": "1.0.2", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.ide.rider": "3.0.38", - "com.unity.editorcoroutines": "1.0.1", - "com.unity.performance.profile-analyzer": "1.2.4", - "com.unity.test-framework": "1.6.0", - "com.unity.testtools.codecoverage": "1.2.7" - } - }, - "com.unity.ide.rider": { - "version": "3.0.38", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ext.nunit": "1.0.6" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ide.visualstudio": { - "version": "2.0.25", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.31" - }, - "url": "https://packages.unity.com" - }, - "com.unity.mathematics": { - "version": "1.3.2", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.multiplayer.center": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.uielements": "1.0.0" - } - }, - "com.unity.netcode.gameobjects": { - "version": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-2.0.0", - "depth": 0, - "source": "git", - "dependencies": { - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.6.0" - }, - "hash": "37bdf528127a9ae3e104d76b9a13343bffd653cb" - }, - "com.unity.nuget.mono-cecil": { - "version": "1.11.5", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.nuget.newtonsoft-json": { - "version": "3.2.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.performance.profile-analyzer": { - "version": "1.2.4", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.purchasing": { - "version": "4.12.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.12.5", - "com.unity.modules.androidjni": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.analytics": { - "version": "6.1.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.12.4", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.authentication": { - "version": "3.5.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.15.1", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.core": { - "version": "1.15.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.modules.androidjni": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment": { - "version": "1.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.15.1", - "com.unity.services.deployment.api": "1.1.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment.api": { - "version": "1.1.2", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.services.multiplayer": { - "version": "1.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.transport": "2.5.0", - "com.unity.collections": "2.2.1", - "com.unity.services.qos": "1.3.0", - "com.unity.services.core": "1.15.1", - "com.unity.services.wire": "1.4.0", - "com.unity.services.deployment": "1.6.2", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "3.5.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.qos": { - "version": "1.3.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.collections": "1.2.4", - "com.unity.services.core": "1.12.4", - "com.unity.nuget.newtonsoft-json": "3.0.2", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "2.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.wire": { - "version": "1.4.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.12.5", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.services.authentication": "2.7.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.settings-manager": { - "version": "2.1.0", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot": { - "version": "2.0.10", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot.linux-x86_64": { - "version": "2.0.9", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10" - }, - "url": "https://packages.unity.com" - }, - "com.unity.test-framework": { - "version": "1.6.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ext.nunit": "2.0.3", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.test-framework.performance": { - "version": "3.2.0", - "depth": 2, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.33", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.testtools.codecoverage": { - "version": "1.2.7", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.0.16", - "com.unity.settings-manager": "1.0.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.timeline": { - "version": "1.8.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.toolchain.win-x86_64-linux-x86_64": { - "version": "2.0.10", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10", - "com.unity.sysroot.linux-x86_64": "2.0.9" - }, - "url": "https://packages.unity.com" - }, - "com.unity.transport": { - "version": "2.6.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.24", - "com.unity.collections": "2.2.1", - "com.unity.mathematics": "1.3.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ugui": { - "version": "2.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0" - } - }, - "com.unity.visualscripting": { - "version": "1.9.7", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.xr.legacyinputhelpers": { - "version": "2.1.12", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.vr": "1.0.0", - "com.unity.modules.xr": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.modules.accessibility": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.ai": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.androidjni": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.animation": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.assetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.audio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.cloth": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.director": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.animation": "1.0.0" - } - }, - "com.unity.modules.hierarchycore": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imageconversion": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imgui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.jsonserialize": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.particlesystem": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics2d": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.screencapture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.subsystems": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.terrain": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.terrainphysics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.terrain": "1.0.0" - } - }, - "com.unity.modules.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics2d": "1.0.0" - } - }, - "com.unity.modules.ui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.uielements": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.hierarchycore": "1.0.0" - } - }, - "com.unity.modules.umbra": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unityanalytics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.unitywebrequest": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unitywebrequestassetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestaudio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.audio": "1.0.0" - } - }, - "com.unity.modules.unitywebrequesttexture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestwww": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.vehicles": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.video": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.vr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.xr": "1.0.0" - } - }, - "com.unity.modules.wind": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.xr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.subsystems": "1.0.0" - } - } - } -} diff --git a/Examples/CharacterControllerMovingBodies/ProjectSettings/PackageManagerSettings.asset b/Examples/CharacterControllerMovingBodies/ProjectSettings/PackageManagerSettings.asset index a31e2cf876..0d54f15594 100644 --- a/Examples/CharacterControllerMovingBodies/ProjectSettings/PackageManagerSettings.asset +++ b/Examples/CharacterControllerMovingBodies/ProjectSettings/PackageManagerSettings.asset @@ -18,23 +18,27 @@ MonoBehaviour: m_SeeAllPackageVersions: 0 m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 0 - oneTimeDeprecatedPopUpShown: 0 - m_Registries: - - m_Id: main + oneTimePackageErrorsPopUpShown: 0 + m_MainRegistry: + m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_IsUnityRegistry: 1 m_Capabilities: 7 m_ConfigSource: 0 m_Compliance: m_Status: 0 m_Violations: [] + m_ScopedRegistries: [] m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_Modified: 0 m_ErrorMessage: - m_UserModificationsInstanceId: -924 - m_OriginalInstanceId: -926 + m_UserModificationsEntityId: + m_rawData: 1099511629078 + m_OriginalEntityId: + m_rawData: 1099511629079 m_LoadAssets: 0 diff --git a/Examples/CharacterControllerMovingBodies/ProjectSettings/PhysicsCoreProjectSettings2D.asset b/Examples/CharacterControllerMovingBodies/ProjectSettings/PhysicsCoreProjectSettings2D.asset new file mode 100644 index 0000000000..049c7454b5 --- /dev/null +++ b/Examples/CharacterControllerMovingBodies/ProjectSettings/PhysicsCoreProjectSettings2D.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!176606843 &1 +PhysicsCoreProjectSettings2D: + m_ObjectHideFlags: 0 + m_PhysicsCoreSettings: {fileID: 0} diff --git a/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectSettings.asset b/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectSettings.asset index b12ab6ff29..7943ef7502 100644 --- a/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectSettings.asset +++ b/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 28 + serializedVersion: 30 productGUID: 6859c206e4885eb4d9a885722be3daa1 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -41,7 +41,6 @@ PlayerSettings: height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1920 defaultScreenHeight: 1080 defaultScreenWidthWeb: 960 @@ -49,12 +48,13 @@ PlayerSettings: m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 unsupportedMSAAFallback: 0 + m_SpriteBatchMaxVertexCount: 65535 m_SpriteBatchVertexThreshold: 300 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 numberOfMipsStrippedPerMipmapLimitGroup: {} - m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000i iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 @@ -65,10 +65,16 @@ PlayerSettings: useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 + adjustIOSFPSUsingThermalState: 1 + thermalStateSeriousIOSFPS: 30 + thermalStateCriticalIOSFPS: 15 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 + androidRequestedVisibleInsets: 0 + androidSystemBarsBehavior: 2 + androidDisplayOptions: 1 androidBlitType: 0 androidResizeableActivity: 1 androidDefaultWindowWidth: 1920 @@ -82,10 +88,11 @@ PlayerSettings: defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 0 - captureSingleScreen: 0 + callOnDisableOnAssetBundleUnload: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 @@ -112,6 +119,7 @@ PlayerSettings: xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 @@ -132,6 +140,7 @@ PlayerSettings: switchNVNMaxPublicSamplerIDCount: 0 switchMaxWorkerMultiple: 8 switchNVNGraphicsFirmwareMemory: 32 + switchGraphicsJobsSyncAfterKick: 1 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 vulkanEnablePreTransform: 0 @@ -144,18 +153,16 @@ PlayerSettings: preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 allowHDRDisplaySupport: 0 useHDRDisplay: 0 hdrBitDepth: 0 - m_ColorGamuts: 00000000 + m_ColorGamuts: 00000000i targetPixelDensity: 30 resolutionScalingMode: 0 resetResolutionOnWindowResize: 0 @@ -171,9 +178,10 @@ PlayerSettings: tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 23 + AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 @@ -188,13 +196,14 @@ PlayerSettings: VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSSimulatorArchitecture: 0 - iOSTargetOSVersionString: 13.0 + iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 13.0 + tvOSTargetOSVersionString: 15.0 VisionOSSdkVersion: 0 VisionOSTargetOSVersionString: 1.0 + xcodeProjectType: 0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -271,6 +280,9 @@ PlayerSettings: AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 + androidAppCategory: 3 + useAndroidAppCategory: 1 + androidAppCategoryOther: AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 @@ -292,8 +304,10 @@ PlayerSettings: m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobMode: [] - m_BuildTargetGraphicsAPIs: [] - m_BuildTargetVRSettings: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: WindowsStandaloneSupport + m_APIs: 0200000012000000i + m_Automatic: 0 m_DefaultShaderChunkSizeInMB: 16 m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 @@ -306,6 +320,7 @@ PlayerSettings: iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupHDRCubemapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetGroupLoadStoreDebugModeSettings: [] m_BuildTargetNormalMapEncoding: [] @@ -313,6 +328,7 @@ PlayerSettings: playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 + editorGfxJobOverride: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 @@ -320,7 +336,7 @@ PlayerSettings: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: - macOSTargetOSVersion: 11.0 + macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 @@ -466,6 +482,8 @@ PlayerSettings: switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 switchUpgradedPlayerSettingsToNMETA: 0 + switchCaStoreSource: 0 + switchCaStoreFilePath: ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -568,18 +586,20 @@ PlayerSettings: webGLMemoryLinearGrowthStep: 16 webGLMemoryGeometricGrowthStep: 0.2 webGLMemoryGeometricGrowthCap: 96 - webGLEnableWebGPU: 0 webGLPowerPreference: 2 webGLWebAssemblyTable: 0 webGLWebAssemblyBigInt: 0 webGLCloseOnQuit: 0 webWasm2023: 0 + webEnableSubmoduleStrippingCompatibility: 0 + webProgressiveAssetLoading: 0 scriptingDefineSymbols: {} additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {} il2cppCodeGeneration: {} + il2cppLTOMode: {} il2cppStacktraceInformation: {} managedStrippingLevel: {} incrementalIl2cppBuild: {} @@ -590,6 +610,7 @@ PlayerSettings: scriptingRuntimeVersion: 1 gcIncremental: 1 gcWBarrierValidation: 0 + managedCodeVariant: {} apiCompatibilityLevelPerPlatform: {} editorAssembliesCompatibilityLevel: 1 m_RenderingPath: 1 @@ -649,7 +670,6 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} cloudServicesEnabled: {} luminIcon: m_Name: @@ -672,6 +692,7 @@ PlayerSettings: captureStartupLogs: {} activeInputHandler: 0 windowsGamepadBackendHint: 0 + enableDirectStorage: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -685,3 +706,7 @@ PlayerSettings: insecureHttpOption: 0 androidVulkanDenyFilterList: [] androidVulkanAllowFilterList: [] + androidVulkanDeviceFilterListAsset: {fileID: 0} + d3d12DeviceFilterListAsset: {fileID: 0} + webGPUDeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectVersion.txt b/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectVersion.txt index 47df254537..02866a2258 100644 --- a/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectVersion.txt +++ b/Examples/CharacterControllerMovingBodies/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.0.61f1 -m_EditorVersionWithRevision: 6000.0.61f1 (74a0adb02c31) +m_EditorVersion: 6000.6.0b5 +m_EditorVersionWithRevision: 6000.6.0b5 (b5238eaafb35) diff --git a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/MoverScriptNoRigidbody.cs b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/MoverScriptNoRigidbody.cs index 6da9441570..6893bf3790 100644 --- a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/MoverScriptNoRigidbody.cs +++ b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/MoverScriptNoRigidbody.cs @@ -7,7 +7,7 @@ #region MoverScriptNoRigidbody Custom Editor #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/NetworkManagerBootstrapper.cs b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/NetworkManagerBootstrapper.cs index 172d819c27..2edccff2e6 100644 --- a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/NetworkManagerBootstrapper.cs +++ b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/NetworkManagerBootstrapper.cs @@ -21,7 +21,7 @@ #region NetworkManagerBootstrapperEditor #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/PlayerBallMotion.cs b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/PlayerBallMotion.cs index 7e525d6c33..9696d62f56 100644 --- a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/PlayerBallMotion.cs +++ b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/PlayerBallMotion.cs @@ -5,7 +5,7 @@ #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// @@ -17,7 +17,7 @@ public class PlayerBallMotionEditor : NetworkTransformEditor { private SerializedProperty m_RotationAxis; private SerializedProperty m_RotationSpeed; - + public override void OnEnable() { diff --git a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/RotatingBodyLogic.cs b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/RotatingBodyLogic.cs index eaa32a77d6..84977b59a4 100644 --- a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/RotatingBodyLogic.cs +++ b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/RotatingBodyLogic.cs @@ -3,7 +3,7 @@ using Unity.Netcode.Components; using UnityEngine; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/SceneBootstrapLoader.cs b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/SceneBootstrapLoader.cs index 256122618e..fbc0e71a62 100644 --- a/Examples/OverridingScenesAndPrefabs/Assets/Scripts/SceneBootstrapLoader.cs +++ b/Examples/OverridingScenesAndPrefabs/Assets/Scripts/SceneBootstrapLoader.cs @@ -6,7 +6,7 @@ using UnityEngine; using UnityEngine.SceneManagement; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// @@ -224,7 +224,7 @@ private void OnSessionOwnerPromoted(ulong sessionOwnerPromoted) if (sessionOwnerPromoted == m_NetworkManager.LocalClientId) { // When we set the client synchronization mode to additive, the session owner will include this setting - // setting when synchronizing a newly joining client and the client will use any already loaded scenes + // setting when synchronizing a newly joining client and the client will use any already loaded scenes // that the session owner determines should be synchronized. If a scene that is being synchronized is not // yet loaded, then the client will load that scene. m_NetworkManager.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive); diff --git a/Examples/OverridingScenesAndPrefabs/Packages/manifest.json b/Examples/OverridingScenesAndPrefabs/Packages/manifest.json index 0a8e3c4a4b..cbeca64318 100644 --- a/Examples/OverridingScenesAndPrefabs/Packages/manifest.json +++ b/Examples/OverridingScenesAndPrefabs/Packages/manifest.json @@ -2,25 +2,25 @@ "dependencies": { "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", - "com.unity.ads": "4.4.2", - "com.unity.ai.navigation": "2.0.9", - "com.unity.analytics": "3.8.1", - "com.unity.collab-proxy": "2.10.0", + "com.unity.ads": "4.19.0", + "com.unity.ai.navigation": "2.0.13", + "com.unity.analytics": "3.8.2", + "com.unity.collab-proxy": "2.12.4", "com.unity.feature.development": "1.0.2", "com.unity.ide.rider": "3.0.38", - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.multiplayer.center": "1.0.0", - "com.unity.netcode.gameobjects": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-2.0.0", - "com.unity.purchasing": "4.12.2", - "com.unity.services.multiplayer": "1.2.0", - "com.unity.test-framework": "1.6.0", - "com.unity.timeline": "1.8.9", - "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", - "com.unity.transport": "2.6.0", - "com.unity.ugui": "2.0.0", - "com.unity.visualscripting": "1.9.7", - "com.unity.xr.legacyinputhelpers": "2.1.12", + "com.unity.ide.visualstudio": "2.0.26", + "com.unity.multiplayer.center": "2.0.1", + "com.unity.netcode.gameobjects": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-3.x.x", + "com.unity.purchasing": "5.4.1", + "com.unity.services.multiplayer": "2.2.4", + "com.unity.test-framework": "1.8.0", + "com.unity.timeline": "6.6.0", + "com.unity.transport": "6.6.0", + "com.unity.ugui": "2.6.0", + "com.unity.visualscripting": "1.9.11", + "com.unity.xr.legacyinputhelpers": "3.0.1", "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -34,10 +34,13 @@ "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.physicscore2d": "1.0.0", "com.unity.modules.screencapture": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tetgen": "1.0.0", "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.timelinefoundation": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.modules.umbra": "1.0.0", @@ -47,9 +50,9 @@ "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", - "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" } diff --git a/Examples/OverridingScenesAndPrefabs/Packages/packages-lock.json b/Examples/OverridingScenesAndPrefabs/Packages/packages-lock.json deleted file mode 100644 index c19504fc77..0000000000 --- a/Examples/OverridingScenesAndPrefabs/Packages/packages-lock.json +++ /dev/null @@ -1,643 +0,0 @@ -{ - "dependencies": { - "com.unity.2d.sprite": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.2d.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.tilemap": "1.0.0", - "com.unity.modules.uielements": "1.0.0" - } - }, - "com.unity.ads": { - "version": "4.4.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ai.navigation": { - "version": "2.0.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.ai": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.analytics": { - "version": "3.8.1", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.analytics": "1.0.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.burst": { - "version": "1.8.25", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.collab-proxy": { - "version": "2.10.0", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.collections": { - "version": "2.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.23", - "com.unity.mathematics": "1.3.2", - "com.unity.test-framework": "1.4.6", - "com.unity.nuget.mono-cecil": "1.11.5", - "com.unity.test-framework.performance": "3.0.3" - }, - "url": "https://packages.unity.com" - }, - "com.unity.editorcoroutines": { - "version": "1.0.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.ext.nunit": { - "version": "2.0.5", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.feature.development": { - "version": "1.0.2", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.ide.rider": "3.0.38", - "com.unity.editorcoroutines": "1.0.1", - "com.unity.performance.profile-analyzer": "1.2.4", - "com.unity.test-framework": "1.6.0", - "com.unity.testtools.codecoverage": "1.2.7" - } - }, - "com.unity.ide.rider": { - "version": "3.0.38", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ext.nunit": "1.0.6" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ide.visualstudio": { - "version": "2.0.25", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.31" - }, - "url": "https://packages.unity.com" - }, - "com.unity.mathematics": { - "version": "1.3.2", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.multiplayer.center": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.uielements": "1.0.0" - } - }, - "com.unity.netcode.gameobjects": { - "version": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-2.0.0", - "depth": 0, - "source": "git", - "dependencies": { - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.6.0" - }, - "hash": "37bdf528127a9ae3e104d76b9a13343bffd653cb" - }, - "com.unity.nuget.mono-cecil": { - "version": "1.11.5", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.nuget.newtonsoft-json": { - "version": "3.2.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.performance.profile-analyzer": { - "version": "1.2.4", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.purchasing": { - "version": "4.12.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.12.5", - "com.unity.modules.androidjni": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.analytics": { - "version": "6.1.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.12.4", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.authentication": { - "version": "3.5.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.15.1", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.core": { - "version": "1.15.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.modules.androidjni": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment": { - "version": "1.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.15.1", - "com.unity.services.deployment.api": "1.1.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment.api": { - "version": "1.1.2", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.services.multiplayer": { - "version": "1.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.transport": "2.5.0", - "com.unity.collections": "2.2.1", - "com.unity.services.qos": "1.3.0", - "com.unity.services.core": "1.15.1", - "com.unity.services.wire": "1.4.0", - "com.unity.services.deployment": "1.6.2", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "3.5.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.qos": { - "version": "1.3.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.collections": "1.2.4", - "com.unity.services.core": "1.12.4", - "com.unity.nuget.newtonsoft-json": "3.0.2", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "2.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.wire": { - "version": "1.4.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.12.5", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.services.authentication": "2.7.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.settings-manager": { - "version": "2.1.0", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot": { - "version": "2.0.10", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot.linux-x86_64": { - "version": "2.0.9", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10" - }, - "url": "https://packages.unity.com" - }, - "com.unity.test-framework": { - "version": "1.6.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ext.nunit": "2.0.3", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.test-framework.performance": { - "version": "3.2.0", - "depth": 2, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.33", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.testtools.codecoverage": { - "version": "1.2.7", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.0.16", - "com.unity.settings-manager": "1.0.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.timeline": { - "version": "1.8.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.toolchain.win-x86_64-linux-x86_64": { - "version": "2.0.10", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10", - "com.unity.sysroot.linux-x86_64": "2.0.9" - }, - "url": "https://packages.unity.com" - }, - "com.unity.transport": { - "version": "2.6.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.24", - "com.unity.collections": "2.2.1", - "com.unity.mathematics": "1.3.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ugui": { - "version": "2.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0" - } - }, - "com.unity.visualscripting": { - "version": "1.9.7", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.xr.legacyinputhelpers": { - "version": "2.1.12", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.vr": "1.0.0", - "com.unity.modules.xr": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.modules.accessibility": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.ai": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.androidjni": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.animation": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.assetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.audio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.cloth": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.director": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.animation": "1.0.0" - } - }, - "com.unity.modules.hierarchycore": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imageconversion": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imgui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.jsonserialize": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.particlesystem": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics2d": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.screencapture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.subsystems": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.terrain": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.terrainphysics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.terrain": "1.0.0" - } - }, - "com.unity.modules.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics2d": "1.0.0" - } - }, - "com.unity.modules.ui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.uielements": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.hierarchycore": "1.0.0" - } - }, - "com.unity.modules.umbra": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unityanalytics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.unitywebrequest": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unitywebrequestassetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestaudio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.audio": "1.0.0" - } - }, - "com.unity.modules.unitywebrequesttexture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestwww": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.vehicles": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.video": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.vr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.xr": "1.0.0" - } - }, - "com.unity.modules.wind": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.xr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.subsystems": "1.0.0" - } - } - } -} diff --git a/Examples/OverridingScenesAndPrefabs/ProjectSettings/PackageManagerSettings.asset b/Examples/OverridingScenesAndPrefabs/ProjectSettings/PackageManagerSettings.asset index 29fcfeb5dc..0d54f15594 100644 --- a/Examples/OverridingScenesAndPrefabs/ProjectSettings/PackageManagerSettings.asset +++ b/Examples/OverridingScenesAndPrefabs/ProjectSettings/PackageManagerSettings.asset @@ -18,23 +18,27 @@ MonoBehaviour: m_SeeAllPackageVersions: 0 m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 0 - oneTimeDeprecatedPopUpShown: 0 - m_Registries: - - m_Id: main + oneTimePackageErrorsPopUpShown: 0 + m_MainRegistry: + m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_IsUnityRegistry: 1 m_Capabilities: 7 m_ConfigSource: 0 m_Compliance: m_Status: 0 m_Violations: [] + m_ScopedRegistries: [] m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_Modified: 0 m_ErrorMessage: - m_UserModificationsInstanceId: -892 - m_OriginalInstanceId: -894 + m_UserModificationsEntityId: + m_rawData: 1099511629078 + m_OriginalEntityId: + m_rawData: 1099511629079 m_LoadAssets: 0 diff --git a/Examples/OverridingScenesAndPrefabs/ProjectSettings/PhysicsCoreProjectSettings2D.asset b/Examples/OverridingScenesAndPrefabs/ProjectSettings/PhysicsCoreProjectSettings2D.asset new file mode 100644 index 0000000000..049c7454b5 --- /dev/null +++ b/Examples/OverridingScenesAndPrefabs/ProjectSettings/PhysicsCoreProjectSettings2D.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!176606843 &1 +PhysicsCoreProjectSettings2D: + m_ObjectHideFlags: 0 + m_PhysicsCoreSettings: {fileID: 0} diff --git a/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectSettings.asset b/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectSettings.asset index 7894df05ea..800c569e6e 100644 --- a/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectSettings.asset +++ b/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 28 + serializedVersion: 30 productGUID: 6859c206e4885eb4d9a885722be3daa1 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -41,7 +41,6 @@ PlayerSettings: height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1920 defaultScreenHeight: 1080 defaultScreenWidthWeb: 960 @@ -55,7 +54,7 @@ PlayerSettings: mipStripping: 0 numberOfMipsStripped: 0 numberOfMipsStrippedPerMipmapLimitGroup: {} - m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000i iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 @@ -66,10 +65,16 @@ PlayerSettings: useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 + adjustIOSFPSUsingThermalState: 1 + thermalStateSeriousIOSFPS: 30 + thermalStateCriticalIOSFPS: 15 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 + androidRequestedVisibleInsets: 0 + androidSystemBarsBehavior: 2 + androidDisplayOptions: 1 androidBlitType: 0 androidResizeableActivity: 1 androidDefaultWindowWidth: 1920 @@ -83,9 +88,11 @@ PlayerSettings: defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 + callOnDisableOnAssetBundleUnload: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 @@ -112,6 +119,7 @@ PlayerSettings: xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 @@ -132,6 +140,7 @@ PlayerSettings: switchNVNMaxPublicSamplerIDCount: 0 switchMaxWorkerMultiple: 8 switchNVNGraphicsFirmwareMemory: 32 + switchGraphicsJobsSyncAfterKick: 1 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 vulkanEnablePreTransform: 0 @@ -144,18 +153,16 @@ PlayerSettings: preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 allowHDRDisplaySupport: 0 useHDRDisplay: 0 hdrBitDepth: 0 - m_ColorGamuts: 00000000 + m_ColorGamuts: 00000000i targetPixelDensity: 30 resolutionScalingMode: 0 resetResolutionOnWindowResize: 0 @@ -171,9 +178,10 @@ PlayerSettings: tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 23 + AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 @@ -188,13 +196,14 @@ PlayerSettings: VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSSimulatorArchitecture: 0 - iOSTargetOSVersionString: 13.0 + iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 13.0 + tvOSTargetOSVersionString: 15.0 VisionOSSdkVersion: 0 VisionOSTargetOSVersionString: 1.0 + xcodeProjectType: 0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -271,6 +280,9 @@ PlayerSettings: AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 + androidAppCategory: 3 + useAndroidAppCategory: 1 + androidAppCategoryOther: AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 @@ -384,8 +396,10 @@ PlayerSettings: m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobMode: [] - m_BuildTargetGraphicsAPIs: [] - m_BuildTargetVRSettings: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: WindowsStandaloneSupport + m_APIs: 0200000012000000i + m_Automatic: 0 m_DefaultShaderChunkSizeInMB: 16 m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 @@ -398,6 +412,7 @@ PlayerSettings: iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupHDRCubemapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetGroupLoadStoreDebugModeSettings: [] m_BuildTargetNormalMapEncoding: [] @@ -413,7 +428,7 @@ PlayerSettings: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: - macOSTargetOSVersion: 11.0 + macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 @@ -559,6 +574,8 @@ PlayerSettings: switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 switchUpgradedPlayerSettingsToNMETA: 0 + switchCaStoreSource: 0 + switchCaStoreFilePath: ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -661,18 +678,20 @@ PlayerSettings: webGLMemoryLinearGrowthStep: 16 webGLMemoryGeometricGrowthStep: 0.2 webGLMemoryGeometricGrowthCap: 96 - webGLEnableWebGPU: 0 webGLPowerPreference: 2 webGLWebAssemblyTable: 0 webGLWebAssemblyBigInt: 0 webGLCloseOnQuit: 0 webWasm2023: 0 + webEnableSubmoduleStrippingCompatibility: 0 + webProgressiveAssetLoading: 0 scriptingDefineSymbols: {} additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {} il2cppCodeGeneration: {} + il2cppLTOMode: {} il2cppStacktraceInformation: {} managedStrippingLevel: {} incrementalIl2cppBuild: {} @@ -683,6 +702,7 @@ PlayerSettings: scriptingRuntimeVersion: 1 gcIncremental: 1 gcWBarrierValidation: 0 + managedCodeVariant: {} apiCompatibilityLevelPerPlatform: {} editorAssembliesCompatibilityLevel: 1 m_RenderingPath: 1 @@ -742,7 +762,6 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} cloudServicesEnabled: Purchasing: 0 Unity Ads: 0 @@ -767,6 +786,7 @@ PlayerSettings: captureStartupLogs: {} activeInputHandler: 0 windowsGamepadBackendHint: 0 + enableDirectStorage: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -780,3 +800,7 @@ PlayerSettings: insecureHttpOption: 0 androidVulkanDenyFilterList: [] androidVulkanAllowFilterList: [] + androidVulkanDeviceFilterListAsset: {fileID: 0} + d3d12DeviceFilterListAsset: {fileID: 0} + webGPUDeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectVersion.txt b/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectVersion.txt index 47df254537..02866a2258 100644 --- a/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectVersion.txt +++ b/Examples/OverridingScenesAndPrefabs/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.0.61f1 -m_EditorVersionWithRevision: 6000.0.61f1 (74a0adb02c31) +m_EditorVersion: 6000.6.0b5 +m_EditorVersionWithRevision: 6000.6.0b5 (b5238eaafb35) diff --git a/Examples/PingTool/Assets/Scripts/ExtendedNetworkManager.cs b/Examples/PingTool/Assets/Scripts/ExtendedNetworkManager.cs index 2c291bbe89..d2d9b29083 100644 --- a/Examples/PingTool/Assets/Scripts/ExtendedNetworkManager.cs +++ b/Examples/PingTool/Assets/Scripts/ExtendedNetworkManager.cs @@ -10,7 +10,7 @@ using SessionState = Unity.Services.Multiplayer.SessionState; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// diff --git a/Examples/PingTool/Assets/Scripts/PlayerMotion.cs b/Examples/PingTool/Assets/Scripts/PlayerMotion.cs index f746521c15..d8166c319c 100644 --- a/Examples/PingTool/Assets/Scripts/PlayerMotion.cs +++ b/Examples/PingTool/Assets/Scripts/PlayerMotion.cs @@ -2,7 +2,7 @@ using UnityEngine; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// /// The custom editor for the component. diff --git a/Examples/PingTool/Packages/manifest.json b/Examples/PingTool/Packages/manifest.json index 0c5e1bb258..e40d163ce8 100644 --- a/Examples/PingTool/Packages/manifest.json +++ b/Examples/PingTool/Packages/manifest.json @@ -2,27 +2,27 @@ "dependencies": { "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", - "com.unity.ads": "4.4.2", - "com.unity.ai.navigation": "2.0.9", - "com.unity.analytics": "3.8.1", - "com.unity.collab-proxy": "2.10.0", + "com.unity.ads": "4.19.0", + "com.unity.ai.navigation": "2.0.13", + "com.unity.analytics": "3.8.2", + "com.unity.collab-proxy": "2.12.4", "com.unity.feature.development": "1.0.2", "com.unity.ide.rider": "3.0.38", - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.multiplayer.center": "1.0.0", - "com.unity.multiplayer.playmode": "1.6.1", - "com.unity.multiplayer.tools": "2.2.6", - "com.unity.netcode.gameobjects": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-2.0.0", - "com.unity.purchasing": "4.12.2", - "com.unity.services.multiplayer": "1.2.0", - "com.unity.test-framework": "1.6.0", - "com.unity.timeline": "1.8.9", - "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", - "com.unity.transport": "2.6.0", - "com.unity.ugui": "2.0.0", - "com.unity.visualscripting": "1.9.7", - "com.unity.xr.legacyinputhelpers": "2.1.12", + "com.unity.ide.visualstudio": "2.0.26", + "com.unity.multiplayer.center": "2.0.1", + "com.unity.multiplayer.playmode": "3.0.0", + "com.unity.multiplayer.tools": "2.2.9", + "com.unity.netcode.gameobjects": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-3.x.x", + "com.unity.purchasing": "5.4.1", + "com.unity.services.multiplayer": "2.2.4", + "com.unity.test-framework": "1.8.0", + "com.unity.timeline": "6.6.0", + "com.unity.transport": "6.6.0", + "com.unity.ugui": "2.6.0", + "com.unity.visualscripting": "1.9.11", + "com.unity.xr.legacyinputhelpers": "3.0.1", "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -36,10 +36,13 @@ "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.physicscore2d": "1.0.0", "com.unity.modules.screencapture": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tetgen": "1.0.0", "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.timelinefoundation": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.modules.umbra": "1.0.0", @@ -49,9 +52,9 @@ "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", - "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" } diff --git a/Examples/PingTool/Packages/packages-lock.json b/Examples/PingTool/Packages/packages-lock.json deleted file mode 100644 index f76bd76778..0000000000 --- a/Examples/PingTool/Packages/packages-lock.json +++ /dev/null @@ -1,674 +0,0 @@ -{ - "dependencies": { - "com.unity.2d.sprite": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.2d.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.tilemap": "1.0.0", - "com.unity.modules.uielements": "1.0.0" - } - }, - "com.unity.ads": { - "version": "4.4.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ai.navigation": { - "version": "2.0.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.ai": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.analytics": { - "version": "3.8.1", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.analytics": "1.0.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.burst": { - "version": "1.8.25", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.collab-proxy": { - "version": "2.10.0", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.collections": { - "version": "2.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.23", - "com.unity.mathematics": "1.3.2", - "com.unity.test-framework": "1.4.6", - "com.unity.nuget.mono-cecil": "1.11.5", - "com.unity.test-framework.performance": "3.0.3" - }, - "url": "https://packages.unity.com" - }, - "com.unity.editorcoroutines": { - "version": "1.0.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.ext.nunit": { - "version": "2.0.5", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.feature.development": { - "version": "1.0.2", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.ide.rider": "3.0.38", - "com.unity.editorcoroutines": "1.0.1", - "com.unity.performance.profile-analyzer": "1.2.4", - "com.unity.test-framework": "1.6.0", - "com.unity.testtools.codecoverage": "1.2.7" - } - }, - "com.unity.ide.rider": { - "version": "3.0.38", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ext.nunit": "1.0.6" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ide.visualstudio": { - "version": "2.0.25", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.31" - }, - "url": "https://packages.unity.com" - }, - "com.unity.mathematics": { - "version": "1.3.2", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.multiplayer.center": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.uielements": "1.0.0" - } - }, - "com.unity.multiplayer.playmode": { - "version": "1.6.1", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.nuget.newtonsoft-json": "2.0.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.multiplayer.tools": { - "version": "2.2.6", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.18", - "com.unity.collections": "2.5.1", - "com.unity.mathematics": "1.3.2", - "com.unity.profiling.core": "1.0.2", - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.modules.uielements": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.netcode.gameobjects": { - "version": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git?path=com.unity.netcode.gameobjects#develop-2.0.0", - "depth": 0, - "source": "git", - "dependencies": { - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.6.0" - }, - "hash": "37bdf528127a9ae3e104d76b9a13343bffd653cb" - }, - "com.unity.nuget.mono-cecil": { - "version": "1.11.5", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.nuget.newtonsoft-json": { - "version": "3.2.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.performance.profile-analyzer": { - "version": "1.2.4", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.profiling.core": { - "version": "1.0.2", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.purchasing": { - "version": "4.12.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.12.5", - "com.unity.modules.androidjni": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.analytics": { - "version": "6.1.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.12.4", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.authentication": { - "version": "3.5.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.15.1", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.core": { - "version": "1.15.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.modules.androidjni": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment": { - "version": "1.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.15.1", - "com.unity.services.deployment.api": "1.1.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment.api": { - "version": "1.1.2", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.services.multiplayer": { - "version": "1.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.transport": "2.5.0", - "com.unity.collections": "2.2.1", - "com.unity.services.qos": "1.3.0", - "com.unity.services.core": "1.15.1", - "com.unity.services.wire": "1.4.0", - "com.unity.services.deployment": "1.6.2", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "3.5.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.qos": { - "version": "1.3.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.collections": "1.2.4", - "com.unity.services.core": "1.12.4", - "com.unity.nuget.newtonsoft-json": "3.0.2", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "2.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.wire": { - "version": "1.4.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.12.5", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.services.authentication": "2.7.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.settings-manager": { - "version": "2.1.0", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot": { - "version": "2.0.10", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot.linux-x86_64": { - "version": "2.0.9", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10" - }, - "url": "https://packages.unity.com" - }, - "com.unity.test-framework": { - "version": "1.6.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ext.nunit": "2.0.3", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.test-framework.performance": { - "version": "3.2.0", - "depth": 2, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.33", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.testtools.codecoverage": { - "version": "1.2.7", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.0.16", - "com.unity.settings-manager": "1.0.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.timeline": { - "version": "1.8.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.toolchain.win-x86_64-linux-x86_64": { - "version": "2.0.10", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10", - "com.unity.sysroot.linux-x86_64": "2.0.9" - }, - "url": "https://packages.unity.com" - }, - "com.unity.transport": { - "version": "2.6.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.24", - "com.unity.collections": "2.2.1", - "com.unity.mathematics": "1.3.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ugui": { - "version": "2.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0" - } - }, - "com.unity.visualscripting": { - "version": "1.9.7", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.xr.legacyinputhelpers": { - "version": "2.1.12", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.vr": "1.0.0", - "com.unity.modules.xr": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.modules.accessibility": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.ai": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.androidjni": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.animation": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.assetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.audio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.cloth": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.director": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.animation": "1.0.0" - } - }, - "com.unity.modules.hierarchycore": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imageconversion": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imgui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.jsonserialize": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.particlesystem": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics2d": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.screencapture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.subsystems": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.terrain": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.terrainphysics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.terrain": "1.0.0" - } - }, - "com.unity.modules.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics2d": "1.0.0" - } - }, - "com.unity.modules.ui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.uielements": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.hierarchycore": "1.0.0" - } - }, - "com.unity.modules.umbra": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unityanalytics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.unitywebrequest": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unitywebrequestassetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestaudio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.audio": "1.0.0" - } - }, - "com.unity.modules.unitywebrequesttexture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestwww": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.vehicles": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.video": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.vr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.xr": "1.0.0" - } - }, - "com.unity.modules.wind": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.xr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.subsystems": "1.0.0" - } - } - } -} diff --git a/Examples/PingTool/ProjectSettings/PackageManagerSettings.asset b/Examples/PingTool/ProjectSettings/PackageManagerSettings.asset index 9f91f86d32..fff2cea709 100644 --- a/Examples/PingTool/ProjectSettings/PackageManagerSettings.asset +++ b/Examples/PingTool/ProjectSettings/PackageManagerSettings.asset @@ -18,23 +18,27 @@ MonoBehaviour: m_SeeAllPackageVersions: 0 m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 0 - oneTimeDeprecatedPopUpShown: 0 - m_Registries: - - m_Id: main + oneTimePackageErrorsPopUpShown: 0 + m_MainRegistry: + m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_IsUnityRegistry: 1 m_Capabilities: 7 m_ConfigSource: 0 m_Compliance: m_Status: 0 m_Violations: [] + m_ScopedRegistries: [] m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_Modified: 0 m_ErrorMessage: - m_UserModificationsInstanceId: -916 - m_OriginalInstanceId: -918 + m_UserModificationsEntityId: + m_rawData: 1099511629079 + m_OriginalEntityId: + m_rawData: 1099511629080 m_LoadAssets: 0 diff --git a/Examples/PingTool/ProjectSettings/PhysicsCoreProjectSettings2D.asset b/Examples/PingTool/ProjectSettings/PhysicsCoreProjectSettings2D.asset new file mode 100644 index 0000000000..049c7454b5 --- /dev/null +++ b/Examples/PingTool/ProjectSettings/PhysicsCoreProjectSettings2D.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!176606843 &1 +PhysicsCoreProjectSettings2D: + m_ObjectHideFlags: 0 + m_PhysicsCoreSettings: {fileID: 0} diff --git a/Examples/PingTool/ProjectSettings/ProjectSettings.asset b/Examples/PingTool/ProjectSettings/ProjectSettings.asset index b9c336c7c7..b57e856858 100644 --- a/Examples/PingTool/ProjectSettings/ProjectSettings.asset +++ b/Examples/PingTool/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 28 + serializedVersion: 30 productGUID: e6cab765567b920448a2fb603cb43bb8 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -41,7 +41,6 @@ PlayerSettings: height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1280 defaultScreenHeight: 720 defaultScreenWidthWeb: 960 @@ -49,12 +48,13 @@ PlayerSettings: m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 unsupportedMSAAFallback: 0 + m_SpriteBatchMaxVertexCount: 65535 m_SpriteBatchVertexThreshold: 300 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 numberOfMipsStrippedPerMipmapLimitGroup: {} - m_StackTraceTypes: 020000000100000001000000010000000200000001000000 + m_StackTraceTypes: 020000000100000001000000010000000200000001000000i iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 @@ -65,10 +65,16 @@ PlayerSettings: useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 + adjustIOSFPSUsingThermalState: 1 + thermalStateSeriousIOSFPS: 30 + thermalStateCriticalIOSFPS: 15 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 + androidRequestedVisibleInsets: 0 + androidSystemBarsBehavior: 2 + androidDisplayOptions: 1 androidBlitType: 0 androidResizeableActivity: 0 androidDefaultWindowWidth: 1920 @@ -82,10 +88,11 @@ PlayerSettings: defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 - captureSingleScreen: 0 + callOnDisableOnAssetBundleUnload: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 @@ -112,6 +119,7 @@ PlayerSettings: xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 @@ -132,6 +140,7 @@ PlayerSettings: switchNVNMaxPublicSamplerIDCount: 0 switchMaxWorkerMultiple: 8 switchNVNGraphicsFirmwareMemory: 32 + switchGraphicsJobsSyncAfterKick: 1 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 vulkanEnablePreTransform: 0 @@ -144,18 +153,16 @@ PlayerSettings: preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 allowHDRDisplaySupport: 0 useHDRDisplay: 0 hdrBitDepth: 0 - m_ColorGamuts: 00000000 + m_ColorGamuts: 00000000i targetPixelDensity: 30 resolutionScalingMode: 0 resetResolutionOnWindowResize: 0 @@ -171,9 +178,10 @@ PlayerSettings: tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 23 + AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 @@ -188,13 +196,14 @@ PlayerSettings: VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSSimulatorArchitecture: 0 - iOSTargetOSVersionString: 13.0 + iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 13.0 + tvOSTargetOSVersionString: 15.0 VisionOSSdkVersion: 0 VisionOSTargetOSVersionString: 1.0 + xcodeProjectType: 0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -271,6 +280,9 @@ PlayerSettings: AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 + androidAppCategory: 3 + useAndroidAppCategory: 1 + androidAppCategoryOther: AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 @@ -292,8 +304,10 @@ PlayerSettings: m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobMode: [] - m_BuildTargetGraphicsAPIs: [] - m_BuildTargetVRSettings: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: WindowsStandaloneSupport + m_APIs: 0200000012000000i + m_Automatic: 0 m_DefaultShaderChunkSizeInMB: 16 m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 @@ -306,6 +320,7 @@ PlayerSettings: iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupHDRCubemapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetGroupLoadStoreDebugModeSettings: [] m_BuildTargetNormalMapEncoding: [] @@ -313,6 +328,7 @@ PlayerSettings: playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 + editorGfxJobOverride: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 @@ -320,7 +336,7 @@ PlayerSettings: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: - macOSTargetOSVersion: 11.0 + macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 @@ -466,6 +482,8 @@ PlayerSettings: switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 switchUpgradedPlayerSettingsToNMETA: 0 + switchCaStoreSource: 0 + switchCaStoreFilePath: ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -568,12 +586,13 @@ PlayerSettings: webGLMemoryLinearGrowthStep: 16 webGLMemoryGeometricGrowthStep: 0.2 webGLMemoryGeometricGrowthCap: 96 - webGLEnableWebGPU: 0 webGLPowerPreference: 2 webGLWebAssemblyTable: 0 webGLWebAssemblyBigInt: 0 webGLCloseOnQuit: 0 webWasm2023: 0 + webEnableSubmoduleStrippingCompatibility: 0 + webProgressiveAssetLoading: 0 scriptingDefineSymbols: Standalone: UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE additionalCompilerArguments: {} @@ -582,6 +601,7 @@ PlayerSettings: Standalone: 0 il2cppCompilerConfiguration: {} il2cppCodeGeneration: {} + il2cppLTOMode: {} il2cppStacktraceInformation: {} managedStrippingLevel: {} incrementalIl2cppBuild: {} @@ -592,6 +612,7 @@ PlayerSettings: scriptingRuntimeVersion: 1 gcIncremental: 1 gcWBarrierValidation: 0 + managedCodeVariant: {} apiCompatibilityLevelPerPlatform: {} editorAssembliesCompatibilityLevel: 1 m_RenderingPath: 1 @@ -651,7 +672,6 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} cloudServicesEnabled: Unity Ads: 0 luminIcon: @@ -675,6 +695,7 @@ PlayerSettings: captureStartupLogs: {} activeInputHandler: 0 windowsGamepadBackendHint: 0 + enableDirectStorage: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -688,3 +709,7 @@ PlayerSettings: insecureHttpOption: 0 androidVulkanDenyFilterList: [] androidVulkanAllowFilterList: [] + androidVulkanDeviceFilterListAsset: {fileID: 0} + d3d12DeviceFilterListAsset: {fileID: 0} + webGPUDeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/Examples/PingTool/ProjectSettings/ProjectVersion.txt b/Examples/PingTool/ProjectSettings/ProjectVersion.txt index 47df254537..02866a2258 100644 --- a/Examples/PingTool/ProjectSettings/ProjectVersion.txt +++ b/Examples/PingTool/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.0.61f1 -m_EditorVersionWithRevision: 6000.0.61f1 (74a0adb02c31) +m_EditorVersion: 6000.6.0b5 +m_EditorVersionWithRevision: 6000.6.0b5 (b5238eaafb35) diff --git a/Examples/PingTool/ProjectSettings/VirtualProjectsConfig.json b/Examples/PingTool/ProjectSettings/VirtualProjectsConfig.json index 4ea16eb5a7..d4b87f3461 100644 --- a/Examples/PingTool/ProjectSettings/VirtualProjectsConfig.json +++ b/Examples/PingTool/ProjectSettings/VirtualProjectsConfig.json @@ -1,4 +1,4 @@ { "PlayerTags": [], - "version": "1.6.1" + "version": "6000.6.0b5" } \ No newline at end of file diff --git a/Tools/CI/NGO.Cookbook.csproj b/Tools/CI/NGO.Cookbook.csproj index aa420f1382..e47057e13d 100644 --- a/Tools/CI/NGO.Cookbook.csproj +++ b/Tools/CI/NGO.Cookbook.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 9d1075d0b3..a9922a19b6 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,6 +13,11 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed +- All editor assembly definitions are renamed with `Unity.Netcode.GameObjects.x` variants + - `Unity.Netcode.Editor` → `Unity.Netcode.GameObjects.Editor` + - `Unity.Netcode.Editor.CodeGen` → `Unity.Netcode.GameObjects.Editor.CodeGen` + - `Unity.Netcode.Editor.PackageChecker` → `Unity.Netcode.GameObjects.Editor.PackageChecker` + - `Unity.Netcode.Editor.Tests` → `Unity.Netcode.GameObjects.Editor.Tests` ### Deprecated @@ -29,6 +34,53 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Obsolete +## [2.13.1] - 2026-07-19 + +### Added + +- Single player session section to provide users with information about `SinglePlayerTransport` and an example script of how to switch between single and multi player sessions. (#4062) + +### Fixed + +- Issue where `NetworkAnimator` did no bounds check on the parameter index read prior to obtaining a pointer to the location within the array. (#4090) +- Issue where scene migration in a distributed authority session would throw an exception on clients migrating their owned `NetworkObject` instances to a different scene.(#4086) +- Issue where migrating a dynamically spawned or in-scene placed `NetworkObject` into a different scene during awake could result in that spawned instance to not be synchronized. (#4086) +- Issue where a NullReferenceException was thrown when a non-authority failed to spawn a NetworkObject. (#4067) +- Issue where the active scene was not being serialized as the 1st scene which could result in various errors including a soft synchronization error if, on the client-side, other synchronized scenes had already been loaded prior to the active scene, which is always loaded as `LoadSceneMode.SingleMode` when client synchronization is set to `LoadSceneMode.SingleMode`, resulting in the previously loaded scene(s) to be unloaded. (#4065) +- Issue when FastBufferReader is attempting to read a string and all or a portion of the character count has already been read by user script, it could read a character length that results in a negative byte length which could result in an editor crash. (#4052) +- Issue where NetworkRigidbodyBase was not applying rotation correctly when using Rigidbody2D. (#4012) +- Issue where NetworkRigidbodyBase was always checking the 3D rigid body's interpolation mode when determining if it is kinematic and needs to put the rigid body to sleep and then switch to interpolation. (#4012) +- Fixed AnticipatedNetworkTransform not respecting the InLocalSpace flag. (#3995) + +## [2.13.0] - 2026-06-21 + +### Added + +- Added support for Unity's Fast Enter Play Mode with domain reload disabled. (#3956) +- RPC messages now log any time they are not processed. (#3994) + +### Changed + +- Changed replaced define usages of `DEVELOPMENT_BUILD || UNITY_EDITOR` and a few niche uses of `DEVELOPMENT_BUILD` with `DEBUG`. (#4006) + +### Deprecated + +- Deprecated the nullable boolean `NetworkObject.IsSceneObject` and introduced `NetworkObject.InScenePlaced`. (#4000) + +## [2.12.0] - 2026-05-24 + +### Added + +- Added a new variant of `UnityTransport.GetDefaultPipelineConfigurations` that takes a reference to the created `NetworkDriver`. This will register all pipeline stages that `UnityTransport` requires, removing the need to manually register them in your own custom driver constructor. (#3980) + +### Changed + +- `NetworkMetricsPipelineStage` is now defined even when the multiplayer tools package is not installed, removing the need to guard its registration behind a version define when using a custom driver in `UnityTransport`. (#3980) + +### Deprecated + +- Deprecated a number of methods that were no longer valid or being used. (#3987) + ### [2.11.2] - 2026-05-01 ### Fixed diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index e78e7192f6..24219a83e5 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -18,6 +18,7 @@ * [Connection approval](basics/connection-approval.md) * [Max players](basics/maxnumberplayers.md) * [Transports](advanced-topics/transports.md) + * [Single player sessions](advanced-topics/singleplayer.md) * [Unity Relay](relay/relay.md) * [Command-line arguments](command-line-arguments.md) * [Network components](network-components.md) @@ -70,15 +71,17 @@ * [Network update loop reference](advanced-topics/network-update-loop-system/network-update-loop-reference.md) * [Network time and ticks](advanced-topics/networktime-ticks.md) * [Serialization](serialization.md) + * [Serialization overview](advanced-topics/serialization/serialization-overview.md) * [C# primitives](advanced-topics/serialization/cprimitives.md) * [Unity primitives](advanced-topics/serialization/unity-primitives.md) * [Enum types](advanced-topics/serialization/enum-types.md) * [Arrays](advanced-topics/serialization/serialization-arrays.md) - * [INetworkSerializable](advanced-topics/serialization/inetworkserializable.md) - * [INetworkSerializeByMemcpy](advanced-topics/serialization/inetworkserializebymemcpy.md) - * [Custom serialization](advanced-topics/custom-serialization.md) - * [NetworkObject serialization](advanced-topics/serialization/networkobject-serialization.md) + * [Customize serializable types with INetworkSerializable](advanced-topics/serialization/inetworkserializable.md) + * [Serialize unmanaged structs with INetworkSerializeByMemcpy](advanced-topics/serialization/inetworkserializebymemcpy.md) + * [NetworkObject and NetworkBehaviour serialization](advanced-topics/serialization/networkobject-serialization.md) * [FastBufferWriter and FastBufferReader](advanced-topics/fastbufferwriter-fastbufferreader.md) + * [BufferSerializer](advanced-topics/bufferserializer.md) + * [Custom serialization](advanced-topics/custom-serialization.md) * [Scene management](scene-management.md) * [Scene management overview](basics/scenemanagement/scene-management-overview.md) * [Integrated management](integrated-management.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/bufferserializer.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/bufferserializer.md index 22375bccf5..0f5df51f15 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/bufferserializer.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/bufferserializer.md @@ -1,5 +1,8 @@ # BufferSerializer +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before learning how to use `BufferSerializer`. + `BufferSerializer` is the bi-directional serializer primarily used for serializing within [`INetworkSerializable`](serialization/inetworkserializable.md) types. It wraps [`FastBufferWriter` and `FastBufferReader`](fastbufferwriter-fastbufferreader.md) to provide high performance serialization, but has a couple of differences to make it more user-friendly: - Rather than writing separate methods for serializing and deserializing, `BufferSerializer` allows writing a single method that can handle both operations, which reduces the possibility of a mismatch between the two @@ -15,3 +18,7 @@ However, when those downsides are unreasonable, `BufferSerializer - For performance, you can use `PreCheck(int amount)` followed by `SerializeValuePreChecked()` to perform bounds checking for multiple fields at once. - For both performance and bandwidth usage, you can obtain the wrapped underlying reader/writer via `serializer.GetFastBufferReader()` when `serializer.IsReader` is `true`, and `serializer.GetFastBufferWriter()` when `serializer.IsWriter` is `true`. These provide micro-performance improvements by removing a level of indirection, and also give you a type you can use with `BytePacker` and `ByteUnpacker`. + +## Serializing custom types + +`BufferSerializer` can be extended via extension methods to handle serializing custom types. Refer to [customizing `BufferSerializer`](./custom-serialization.md#bufferserializer) for instructions on how to do this. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/custom-serialization.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/custom-serialization.md index 7a04e2f123..b7b23e4afc 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/custom-serialization.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/custom-serialization.md @@ -1,93 +1,71 @@ # Custom serialization -Netcode uses a default serialization pipeline when using `RPC`s, `NetworkVariable`s, or any other Netcode-related tasks that require serialization. The serialization pipeline looks like this: +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before learning how to customize serialization. -`` -Custom Types => Built In Types => INetworkSerializable -`` +Netcode for GameObjects provide support for serializing any unsupported types, and with the API provided, it can even be done with types that you haven't defined yourself, those who are behind a 3rd party wall, such as .NET types. However, the way custom serialization is implemented for RPCs and NetworkVariables is slightly different. -That is, when Netcode first gets hold of a type, it will check for any custom types that the user have registered for serialization, after that it will check if it's a built in type, such as a Vector3, float etc. These are handled by default. If not, it will check if the type inherits `INetworkSerializable`, if it does, it will call it's write methods. +Netcode for GameObjects supports custom serialization of unsupported types, including those you haven't defined yourself, such as third-party .NET types. You can also use custom serialization to override how an existing supported type is serialized. -By default, any type that satisfies the `unmanaged` generic constraint can be automatically serialized as RPC parameters. This includes all basic types (bool, byte, int, float, enum, etc) as well as any structs that has only these basic types. +Custom serialization is implemented slightly differently for RPCs and NetworkVariables. The examples on this page provide different ways to serialization a custom health struct. -With this flow, you can provide support for serializing any unsupported types, and with the API provided, it can even be done with types that you haven't defined yourself, those who are behind a 3rd party wall, such as .NET types. However, the way custom serialization is implemented for RPCs and NetworkVariables is slightly different. +[!code-cs[](../../Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs#HealthStruct)] -### Serialize a type in a Remote Procedure Call (RPC) +## FastBufferReader and FastBufferWriter -> [!NOTE] -> From versioln 1.7.0 Remote Procedure Calls (RPCs) can also use the Network Variable flow, but NetworkVariables can't use the RPC flow. The RPC flow is more efficient when RPCs serialize the type. Unity selects the RPC flow if you implement both the RPC and Network variable flows. When a type is used by both NetworkVariables and RPCs you can use the NetworkVariable flow to lower maintenance requirements. +[`FastBufferReader` and `FastBufferWriter`](./fastbufferwriter-fastbufferreader.md) are the main serialization tools in Netcode for GameObjects. To register serialization for a custom type, or override an already handled type, you need to create extension methods for `FastBufferReader.ReadValueSafe()` and `FastBufferWriter.WriteValueSafe()`. `FastBufferReader` and `FastBufferWriter` can read and write primitive types, and you can extend this functionality to serialize your custom type. -To register a custom type, or override an already handled type, you need to create extension methods for `FastBufferReader.ReadValueSafe()` and `FastBufferWriter.WriteValueSafe()`: +[!code-cs[](../../Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs#FastBuffer)] -```csharp -// Tells the Netcode how to serialize and deserialize Url in the future. -// The class name doesn't matter here. -public static class SerializationExtensions -{ - public static void ReadValueSafe(this FastBufferReader reader, out Url url) - { - reader.ReadValueSafe(out string val); - url = new Url(val); - } - - public static void WriteValueSafe(this FastBufferWriter writer, in Url url) - { - writer.WriteValueSafe(url.Value); - } -} -``` +You may also need to add extensions for `FastBufferReader.ReadValue()`, `FastBufferWriter.WriteValue()` if you want to serialize without [bounds checking](./fastbufferwriter-fastbufferreader.md#bounds-checking) -The code generation for RPCs will automatically pick up and use these functions, and they'll become available via `FastBufferWriter` and `FastBufferReader` directly. +## BufferSerializer -You can also optionally use the same method to add support for `BufferSerializer.SerializeValue()`, if you wish, which will make this type readily available within [`INetworkSerializable`](serialization/inetworkserializable.md) types: +You can also add custom serialization support to the bi-directional [`BufferSerializer`](./bufferserializer.md). This makes your custom type readily available within [`INetworkSerializable`](serialization/inetworkserializable.md) types and in the [`NetworkBehaviour.OnSynchronize()` method](../components/core/networkbehaviour-synchronize.md#prespawn-synchronization-with-onsynchronize): -```csharp -// The class name doesn't matter here. -public static class SerializationExtensions -{ - public static void SerializeValue(this BufferSerializer serializer, ref Url url) where TReaderWriter: IReaderWriter - { - if (serializer.IsReader) - { - url = new Url(); - } - serializer.SerializeValue(ref url.Value); - } -} -``` +[!code-cs[](../../Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs#BufferSerializer)] -Additionally, you can also add extensions for `FastBufferReader.ReadValue()`, `FastBufferWriter.WriteValue()`, and `BufferSerializer.SerializeValuePreChecked()` to provide more optimal implementations for manual serialization using `FastBufferReader.TryBeginRead()`, `FastBufferWriter.TryBeginWrite()`, and `BufferSerializer.PreCheck()`, respectively. However, none of these will be used for serializing RPCs - only `ReadValueSafe` and `WriteValueSafe` are used. +## Remote procedure call (RPCs) -### For NetworkVariable +> [!NOTE] +> RPCs can use the Network Variable flow, but NetworkVariables can't use the RPC flow. The RPC flow is more efficient when only RPCs need to serialize the type. When a type is used by both NetworkVariables and RPCs, you can implement just the NetworkVariable flow to lower maintenance requirements. Unity will select the RPC flow for RPCs if you have implemented both flows. -`NetworkVariable` goes through a slightly different pipeline than `RPC`s and relies on a different process for determining how to serialize its types. As a result, making a custom type available to the `RPC` pipeline doesn't automatically make it available to the `NetworkVariable` pipeline, and vice-versa. The same method can be used for both, but currently, `NetworkVariable` requires an additional runtime step to make it aware of the methods. +To serialize a custom type, or override an already handled type, you need to create extension methods for `FastBufferReader.ReadValueSafe()` and `FastBufferWriter.WriteValueSafe()` as [outlined above](#fastbufferreader-and-fastbufferwriter). -To add custom serialization support in `NetworkVariable`, follow the steps from the "For RPCs" section to write extension methods for `FastBufferReader` and `FastBufferWriter`; then, somewhere in your application startup (before any `NetworkVariable`s using the affected types will be serialized) add the following: +The code generation for RPCs will automatically pick up and use these functions, as they'll become available via `FastBufferWriter` and `FastBufferReader` directly. -```csharp -UserNetworkVariableSerialization.WriteValue = SerializationExtensions.WriteValueSafe; -UserNetworkVariableSerialization.ReadValue = SerializationExtensions.ReadValueSafe; -``` +## NetworkVariable + +Implementing [`INetworkSerializable`](./serialization/inetworkserializable.md) is the cleanest and most straightforward way to customize the serialization of a type within a [`NetworkVariable`](../basics/networkvariable.md). `UserNetworkVariableSerialization` provides runtime configuration to further override serialization of a type. -You can also use lambda expressions here: +First you will need to create extension methods for `FastBufferReader.ReadValueSafe()` and `FastBufferWriter.WriteValueSafe()` as [outlined above](#fastbufferreader-and-fastbufferwriter). + +Secondly, somewhere in your application startup (before any `NetworkVariable`s using the affected types will be serialized), add the following: ```csharp -UserNetworkVariableSerialization.WriteValue = (FastBufferWriter writer, in Url url) => -{ - writer.WriteValueSafe(url.Value); -}; - -UserNetworkVariableSerialization.ReadValue = (FastBufferReader reader, out Url url) -{ - reader.ReadValueSafe(out string val); - url = new Url(val); -}; +UserNetworkVariableSerialization.WriteValue = SerializationExtensions.WriteValueSafe; +UserNetworkVariableSerialization.ReadValue = SerializationExtensions.ReadValueSafe; +UserNetworkVariableSerialization.DuplicateValue = (in Health value, ref Health duplicatedValue) => duplicatedValue = value; ``` -When you create an extension method in `NetworkVariable` you need to implement the following values: +`DuplicateValue` should return a complete deep copy of the value that `NetworkVariable` compares to a previous value, which is used to check whether the value has changed. `DuplicateValue` avoids re-serializing it over the network every frame when it hasn't changed. + +> [!NOTE] +> `WriteValue`, `ReadValue` and `DuplicateValue` all need to be defined to customize your serialization. + +> [!NOTE] +> `WriteValue` and `ReadValue` will not be used if a type implements `INetworkSerializable` or [`INetworkSerializeByMemcpy`](./serialization/inetworkserializebymemcpy.md). + +### Serializing delta updates + +Reading and writing a value provides the minimal amount of `NetworkVariable` functionality. This will synchronize your whole type whenever any value within the type value changes. To support sending delta updates rather than a full update whenever your type has changed, implement the following functions: + +- `WriteDelta` +- `ReadDelta` + +> [!NOTE] +> Both `WriteDelta` and `ReadDelta` need to be defined for either to be used. -- `WriteValue` -- `ReadValue` -- `DuplicateValue` +Here is a full implementation of a custom type with the methods needed for `UserNetworkVariableSerialization` -`DuplicateValue` returns a complete deep copy of the value that `NetworkVariable` compares to a previous value to check whether or not that values has changed. This avoids reserializing it over the network every frame when it hasn't changed. +[!code-cs[](../../Tests/Runtime/DocumentationCodeSamples/NetworkVariable/NetworkVariableSerialization.cs#HealthExample)] diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/fastbufferwriter-fastbufferreader.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/fastbufferwriter-fastbufferreader.md index 1ac9b0f9d3..3f8327fc17 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/fastbufferwriter-fastbufferreader.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/fastbufferwriter-fastbufferreader.md @@ -1,5 +1,8 @@ # FastBufferWriter and FastBufferReader +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before learning about `FastBufferWriter` and `FastBufferReader`. + The serialization and deserialization is done via `FastBufferWriter` and `FastBufferReader`. These have methods for serializing individual types and methods for serializing packed numbers, but in particular provide a high-performance method called `WriteValue()/ReadValue()` (for Writers and Readers, respectively) that can extremely quickly write an entire unmanaged struct to a buffer. There's a trade-off of CPU usage vs bandwidth in using this: Writing individual fields is slower (especially when it includes operations on unaligned memory), but allows the buffer to be filled more efficiently, both because it avoids padding for alignment in structs, and because it allows you to use `BytePacker.WriteValuePacked()`/`ByteUnpacker.ReadValuePacked()` and `BytePacker.WriteValueBitPacked()`/`ByteUnpacker.ReadValueBitPacked()`. The difference between these two is that the BitPacked variants pack more efficiently, but they reduce the valid range of values. See the section below for details on packing. @@ -156,3 +159,7 @@ Packing values is done using the utility classes `BytePacker` and `ByteUnpacker` | uint | 30 bits (0 to 1,073,741,824) | | long | 60 bits + sign bit (-1,152,921,504,606,846,976 to 1,152,921,504,606,846,975) | | ulong | 61 bits (0 to 2,305,843,009,213,693,952) | + +## Serializing custom types + +`FastBufferReader` and `FastBufferWriter` can be extended via extension methods to handle serializing custom types. Refer to [customizing `FastBufferReader` and `FastBufferWriter`](./custom-serialization.md#fastbufferreader-and-fastbufferwriter) for instructions on how to do this. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md index 1fb5c3dcc4..b46a1b8b91 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md @@ -328,3 +328,4 @@ void Update() ## Additional resources * [RPC parameters](rpc-params.md) +* [Customizing serialization](../custom-serialization.md#remote-procedure-call-rpc) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializable.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializable.md index 36d83416ba..2733a0e373 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializable.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializable.md @@ -1,9 +1,14 @@ -# INetworkSerializable +# Customize serializable types with INetworkSerializable -You can use the `INetworkSerializable` interface to define custom serializable types. +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before customizing serializable types with `INetworkSerializable`. + +You can use the `INetworkSerializable` interface to define custom serializable types. This interface has one function: `NetworkSerialize(BufferSerializer serializer)`, which ingests a bi-directional [`BufferSerializer`](../bufferserializer.md) that you can use to implement bi-directional custom serialization. + +`INetworkSerializable` can be implemented on both managed and unmanaged types, although serializing managed types isn't recommended because it can reduce your game's performance. ```csharp -struct MyComplexStruct : INetworkSerializable +struct SpawnPoint : INetworkSerializable { public Vector3 Position; public Quaternion Rotation; @@ -18,24 +23,20 @@ struct MyComplexStruct : INetworkSerializable } ``` -Types implementing `INetworkSerializable` are supported by `NetworkSerializer`, `RPC` s and `NetworkVariable` s. +Types implementing `INetworkSerializable` are supported by [`FastBufferReader` and `FastBufferWriter`](./fastbufferwriter-fastbufferreader.md), [`RPC`s'](../message-system/rpc.md), and [`NetworkVariable`s](../../basics/networkvariable.md). ```csharp - [Rpc(SendTo.Server)] -void MyServerRpc(MyComplexStruct myStruct) { /* ... */ } +void SpawnAtPointRpc(SpawnPoint spawnPoint) { /* ... */ } -void Update() +void DoSpawnHere() { - if (Input.GetKeyDown(KeyCode.P)) + var spawnPoint = new SpawnPoint { - MyServerRpc( - new MyComplexStruct - { - Position = transform.position, - Rotation = transform.rotation - }); // Client -> Server - } + Position = transform.position, + Rotation = transform.rotation + }; + SpawnAtPointRpc(spawnPoint); // Client -> Server } ``` @@ -59,7 +60,7 @@ The following example explores a more advanced use case. ```csharp -public struct MyMoveStruct : INetworkSerializable +public struct SpawnWithMovement : INetworkSerializable { public Vector3 Position; public Quaternion Rotation; @@ -125,7 +126,7 @@ Review the following example: ```csharp -public struct MyStructA : INetworkSerializable +public struct SpawnPoint : INetworkSerializable { public Vector3 Position; public Quaternion Rotation; @@ -137,17 +138,17 @@ public struct MyStructA : INetworkSerializable } } -public struct MyStructB : INetworkSerializable +public struct SpawnInfo : INetworkSerializable { public int SomeNumber; public string SomeText; - public MyStructA StructA; + public SpawnPoint SpawnPoint; void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref SomeNumber); serializer.SerializeValue(ref SomeText); - StructA.NetworkSerialize(serializer); + SpawnPoint.NetworkSerialize(serializer); } } ``` @@ -167,7 +168,7 @@ While you can have nested `INetworkSerializable` implementations (an `INetworkSe ```csharp /// This isn't supported. -public struct MyStructB : MyStructA +public struct SpawnInfo : SpawnPoint { public int SomeNumber; public string SomeText; @@ -232,4 +233,4 @@ Then declare this network variable like so: ```csharp NetworkVariable myVar = new NetworkVariable(); -``` \ No newline at end of file +``` diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializebymemcpy.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializebymemcpy.md index 7a684dbaba..212bfbf339 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializebymemcpy.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/inetworkserializebymemcpy.md @@ -1,4 +1,7 @@ -# INetworkSerializeByMemcpy +# Serialize unmanaged structs with INetworkSerializeByMemcpy + +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before using `INetworkSerializeByMemcpy` to serialize unmanaged structs. The `INetworkSerializeByMemcpy` interface is used to mark an unmanaged struct type as being trivially serializable over the network by directly copying the whole struct, byte-for-byte, as it appears in memory, into and out of the buffer. This can offer some benefits for performance compared to serializing one field at a time, especially if the struct has many fields in it, but it may be less efficient from a bandwidth-usage perspective, as fields will often be padded for memory alignment and you won't be able to "pack" any of the fields to optimize for space usage. @@ -14,7 +17,7 @@ public struct MyStruct : INetworkSerializeByMemcpy } ``` -If you have a type you wish to serialize that you know is compatible with this method of serialization, but don't have access to modify the struct to add this interface, you can wrap your values in `ForceNetworkSerializeByMemcpy` to enable it to be serialized this way. This works in both `RPC`s and `NetworkVariables`, as well as in other contexts such as `BufferSerializer<>`, `FastBufferReader`, and `FastBufferWriter`. +If you have a type you wish to serialize that you know is compatible with this method of serialization, but don't have access to modify the struct to add this interface, you can wrap your values in `ForceNetworkSerializeByMemcpy` to enable it to be serialized this way. This works in both [`RPC`s](../message-system/rpc.md) and [`NetworkVariable`s](../../basics/networkvariable.md), as well as in other contexts such as with [`BufferSerializer<>`](../bufferserializer.md) or [`FastBufferReader`, and `FastBufferWriter`](../fastbufferwriter-fastbufferreader.md). ```csharp public NetworkVariable> GuidVar; diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md index 9ca778028d..466d545b4b 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/networkobject-serialization.md @@ -1,14 +1,18 @@ -# NetworkObject and NetworkBehaviour +# NetworkObject and NetworkBehaviour serialization -`GameObjects`, `NetworkObjects` and NetworkBehaviour aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default. +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before learning how to serialize `NetworkObjects` and `NetworkBehaviours`. + +`GameObject`, [`NetworkObject`](../../components/core/networkobject.md) and [`NetworkBehaviour`](../../components/core/networkbehaviour.md) aren't serializable types so they can't be used in [`RPC`s](../message-system/rpc.md) or [`NetworkVariable`s](../../basics/networkvariable.md) by default. -There are two convenience wrappers which can be used to send a reference to a NetworkObject or a NetworkBehaviour over RPCs or `NetworkVariables`. +There are two convenience wrappers which can be used to send a reference to a `NetworkObject` or a `NetworkBehaviour` over an `RPC` or in a `NetworkVariable`. ## NetworkObjectReference -`NetworkObjectReference` can be used to serialize a reference to a NetworkObject. It can only be used on already spawned `NetworkObjects`. +`NetworkObjectReference` can be used to serialize a reference to a `NetworkObject`. It can only be used on already spawned `NetworkObject`s. Here is an example of using NetworkObject reference to send a target NetworkObject over an RPC: + ```csharp public class Weapon : NetworkBehaviour { @@ -36,6 +40,7 @@ public class Weapon : NetworkBehaviour ### Implicit Operators There are also implicit operators which convert from/to `NetworkObject/GameObject` which can be used to simplify code. For instance the above example can also be written in the following way: + ```csharp public class Weapon : NetworkBehaviour { @@ -51,6 +56,7 @@ public class Weapon : NetworkBehaviour } } ``` + > [!NOTE] > The implicit conversion to NetworkObject / GameObject will result in `Null` if the reference can't be found. @@ -85,6 +91,6 @@ public class Weapon : NetworkBehaviour ## How NetworkObjectReference & NetworkBehaviourReference work -`NetworkObjectReference` and `NetworkBehaviourReference` are convenience wrappers which serialize the id of a NetworkObject when being sent and on the receiving end retrieve the corresponding ` ` with that id. `NetworkBehaviourReference` sends an additional index which is used to find the right NetworkBehaviour on the NetworkObject. +`NetworkObjectReference` and `NetworkBehaviourReference` are convenience wrappers which serialize the id of a `NetworkObject` when being sent and on the receiving end retrieve the corresponding `NetworkObject` with that id. `NetworkBehaviourReference` sends an additional index which is used to find the right `NetworkBehaviour` on the `NetworkObject`. -Both of them are structs implementing the `INetworkSerializable` interface. +Both of them are structs implementing the [`INetworkSerializable`](./inetworkserializable.md) interface. diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-arrays.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-arrays.md index c347f4c13e..5681e5952c 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-arrays.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-arrays.md @@ -1,6 +1,9 @@ # Arrays and native containers -Netcode for GameObjects has built-in serialization code for arrays of [C# value-type primitives](cprimitives.md), like `int[]`, and [Unity primitive types](unity-primitives.md). Any arrays of types that aren't handled by the built-in serialization code, such as `string[]`, need to be handled using a container class or structure that implements the [`INetworkSerializable`](inetworkserializable.md) interface. +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand the basics of serialization before learning how to serialize arrays and native containers. + +Netcode for GameObjects has built-in serialization code for arrays of [C# value-type primitives](cprimitives.md), like `int[]`, and [Unity primitive types](unity-primitives.md). Any arrays of types that aren't handled by the built-in serialization code, such as custom types, need to be handled using a container class or structure that implements the [`INetworkSerializable`](inetworkserializable.md) interface. ## Performance considerations @@ -17,37 +20,6 @@ Using built-in primitive types is fairly straightforward: void HelloServerRpc(int[] scores, Color[] colors) { /* ... */ } ``` -## INetworkSerializable implementation example - -There are many ways to handle sending an array of managed types. The following example is a simple `string` container class that implements `INetworkSerializable` and can be used as an array of "StringContainers": - -```csharp -[Rpc(SendTo.ClientsAndHost)] -void SendMessagesClientRpc(StringContainer[] messages) -{ - foreach (var stringContainer in stringContainers) - { - Debug.Log($"{stringContainer.SomeText}"); - } -} - -public class StringContainer : INetworkSerializable -{ - public string SomeText; - public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter - { - if (serializer.IsWriter) - { - serializer.GetFastBufferWriter().WriteValueSafe(SomeText); - } - else - { - serializer.GetFastBufferReader().ReadValueSafe(out SomeText); - } - } -} -``` - ## Native containers Netcode for GameObjects supports `NativeArray` and `NativeList` native containers with built-in serialization, RPCs, and NetworkVariables. However, you can't nest either of these containers without causing a crash. @@ -77,3 +49,7 @@ To serialize a `NativeList` container, you must: > [!NOTE] > When using `NativeLists` within `INetworkSerializable`, the list `ref` value must be a valid, initialized `NativeList`. > NetworkVariables are similar that the value must be initialized before it can receive updates. For example, `public NetworkVariable> ByteListVar = new NetworkVariable>{Value = new NativeList(Allocator.Persistent)};`. RPCs do this automatically. + +## Generic collections + +For performance reasons, Netcode for GameObjects does not have built-in serialization code for C# generic collections. However, `NetworkVariable` does support [synchronizing generic collection types](../../basics/networkvariable.md#using-collections-with-networkvariables). diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-overview.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-overview.md new file mode 100644 index 0000000000..fb52e42773 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/serialization/serialization-overview.md @@ -0,0 +1,36 @@ +# Serialization overview + +Netcode for GameObjects uses a default serialization pipeline when using [`RPC`](../../rpc-landing.md)'s, [`NetworkVariable`](../../networkvariables-landing.md)s, or any other Netcode-related tasks that require serialization. The serialization pipeline looks like this: + +`` +Custom types => Built-in types => INetworkSerializable +`` + +When Netcode for GameObjects first receives a type, it checks for any custom types that you have registered for serialization, then it checks if it's a built-in type, such as a Vector3 or a float. These are handled by default. Otherwise, it checks if the type inherits [`INetworkSerializable`](inetworkserializable.md), and if it does, it calls its write methods. + +By default, any type that satisfies the unmanaged generic constraint can be automatically serialized as RPC parameters. This includes all basic types (bool, byte, int, float, enum, for example), as well as any structs that contain only these basic types. + +Serialization and deserialization is done via the structs [`FastBufferWriter` and `FastBufferReader`](fastbufferwriter-fastbufferreader.md). These have methods for serializing individual types and methods for serializing packed numbers, but in particular provide a high-performance method called `WriteValue()/ReadValue()` (for Writers and Readers, respectively) that can extremely quickly write an entire unmanaged struct to a buffer. + +`FastBufferWriter` and `FastBufferReader` also contain the functions `FastBufferWriter.WriteNetworkSerializable()` and `FastBufferReader.ReadNetworkSerializable` for writing and reading values that use the `INetworkSerializable` interface. + +## Built-in serialization + +* [C# primitives](./cprimitives.md) +* [Unity primitives](./unity-primitives.md) +* [Enum types](./enum-types.md) +* [Arrays](./serialization-arrays.md) +* [Collections](../../basics/networkvariable.md#using-collections-with-networkvariables) + +## Custom serialization + +* [INetworkSerializable](./inetworkserializable.md) +* [INetworkSerializeByMemcpy](./inetworkserializebymemcpy.md) +* [Customizing serialization](../custom-serialization.md) +* [Custom NetworkVariable implementations](../../basics/custom-networkvariables.md) + +## Additional resources + +* [NetworkObject serialization](./networkobject-serialization.md) +* [FastBufferWriter and FastBufferReader](../fastbufferwriter-fastbufferreader.md) +* [BufferSerializer](../bufferserializer.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/singleplayer.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/singleplayer.md new file mode 100644 index 0000000000..e4f45a5ead --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/singleplayer.md @@ -0,0 +1,18 @@ +# Single player sessions + +Netcode for GameObjects provides a [SinglePlayerTransport](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.13/api/Unity.Netcode.Transports.SinglePlayer.SinglePlayerTransport.html) which derives from [NetworkTransport](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.13/api/Unity.Netcode.NetworkTransport.html). + +This provides the ability to run a hosted session using the single player transport without having to modify your primary netcode script. + +## Adding the single player transport + +- Add the `SinglePlayerTransport` to your NetworkManager. +- You can create a custom `MonoBehaviour` component to handle your connection flow or you can derive from `NetworkManager` and add additional methods/logic to handle starting a single player session or multiplayer session. + - When starting a single player session, prior to starting the NetworkManager as a host (_required_), you will want to assign the `SinglePlayerTransport` to the `NetworkManager.NetworkConfig.NetworkTransport`. + - When starting a multiplayer session, prior to starting the NetworkManager, you will want to assign the `UnityTransport` (_or any other `NetworkTransport` derived class that you might use for multiplayer sessions_) to the `NetworkManager.NetworkConfig.NetworkTransport`. + +## Example script + +Below is an example component script that provides a single method to start a single or multi player session: + +[!code-cs[](../../Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs#SinglePlayerTransportExample)] diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md b/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md index c2c1f97286..de160d26d2 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/custom-networkvariables.md @@ -2,6 +2,9 @@ In addition to the standard [`NetworkVariable`s](networkvariable.md) available in Netcode for GameObjects, you can also create custom `NetworkVariable`s for advanced implementations. The `NetworkVariable` and `NetworkList` classes were created as `NetworkVariableBase` class implementation examples. While the `NetworkVariable` class is considered production ready, you might run into scenarios where you have a more advanced implementation in mind. In this case, you can create your own custom implementation. +> [!NOTE] +> Read the [Serialization overview](./serialization/serialization-overview.md) page to understand how Netcode for GameObjects handles serialization. + To create your own `NetworkVariableBase`-derived container, you should: 1. Create a class deriving from `NetworkVariableBase`. @@ -16,11 +19,14 @@ To create your own `NetworkVariableBase`-derived container, you should: The way you read and write `NetworkVariable`s changes depending on the type you use. -* Known, non-generic types: Use `FastBufferReader.ReadValue` to read from and `FastBufferWriter.WriteValue` to write to the `NetworkVariable` value. -* Integer types: This type gives you the option to use `BytePacker` and `ByteUnpacker` to compress the `NetworkVariable` value. This process can save bandwidth but adds CPU processing time. +* Known, non-generic types can use [`FastBufferWriter` and `FastBufferReader`](../advanced-topics/fastbufferwriter-fastbufferreader.md) to serialize the `NetworkVariable` value. +* Integer types: This type gives you the option to use [`BytePacker` and `ByteUnpacker`](../advanced-topics/fastbufferwriter-fastbufferreader.md#packing) to compress the `NetworkVariable` value. This process can save bandwidth but adds CPU processing time. * Generic types: Use serializers that Unity generates based on types discovered during a compile-time code generation process. This means you need to tell Unity's code generation algorithm which types to generate serializers for. To tell Unity which types to serialize, use the following methods: * Use `GenerateSerializationForTypeAttribute` to serialize hard-coded types. * Use `GenerateSerializationForGenericParameterAttribute` to serialize generic types. +* Types implementing [`INetworkSerializable`](../advanced-topics/serialization/inetworkserializable.md) can also be serialized using `FastBufferWriter` and `FastBufferReader`. + +Read more about [custom serialization](../advanced-topics/custom-serialization.md) for more approaches to customizing serialization. ### Serialize a hard-coded type @@ -66,175 +72,22 @@ For dynamically-allocated types with a value that isn't `null` (for example, man You can use `AreEqual` to determine if a value is different from the value that `Duplicate` cached. This avoids sending the same value multiple times. You can also use the previous value that `Duplicate` cached to calculate deltas to use in `ReadDelta` and `WriteDelta`. -The type you use must be serializable according to the [support types list above](#supported-types). Each type needs its own serializer instantiated, so this step tells the codegen which types to create serializers for. Unity's code generator assumes that all `NetworkVariable` types exist as fields inside NetworkBehaviour types. This means that Unity only inspects fields inside NetworkBehaviour types to identify the types to create serializers for. +The type you use must be serializable according to the [supported types list](./networkvariable.md#supported-types). Each type needs its own serializer instantiated, so this step tells the codegen which types to create serializers for. Unity's code generator assumes that all `NetworkVariable` types exist as fields inside NetworkBehaviour types. This means that Unity only inspects fields inside NetworkBehaviour types to identify the types to create serializers for. -## Custom NetworkVariable example +> [!NOTE] +> These attributes won't generate delta serialization. If you would like to customize how deltas are sent when a custom value has partially changed, refer to [`UserNetworkVariableSerialization`](../advanced-topics/custom-serialization.md#networkvariable). + +## Custom NetworkVariableBase example This example shows a custom `NetworkVariable` type to help you understand how you might implement such a type. In the current version of Netcode for GameObjects, this example is possible without using a custom `NetworkVariable` type; however, for more complex situations that aren't natively supported, this basic example should help inform you of how to approach the implementation: - ```csharp - /// Using MyCustomNetworkVariable within a NetworkBehaviour - public class TestMyCustomNetworkVariable : NetworkBehaviour - { - public MyCustomNetworkVariable CustomNetworkVariable = new MyCustomNetworkVariable(); - public MyCustomGenericNetworkVariable CustomGenericNetworkVariable = new MyCustomGenericNetworkVariable(); - public override void OnNetworkSpawn() - { - if (IsServer) - { - for (int i = 0; i < 4; i++) - { - var someData = new SomeData(); - someData.SomeFloatData = (float)i; - someData.SomeIntData = i; - someData.SomeListOfValues.Add((ulong)i + 1000000); - someData.SomeListOfValues.Add((ulong)i + 2000000); - someData.SomeListOfValues.Add((ulong)i + 3000000); - CustomNetworkVariable.SomeDataToSynchronize.Add(someData); - CustomNetworkVariable.SetDirty(true); - - CustomGenericNetworkVariable.SomeDataToSynchronize.Add(i); - CustomGenericNetworkVariable.SetDirty(true); - } - } - } - } - - /// Bare minimum example of NetworkVariableBase derived class - [Serializable] - public class MyCustomNetworkVariable : NetworkVariableBase - { - /// Managed list of class instances - public List SomeDataToSynchronize = new List(); - - /// - /// Writes the complete state of the variable to the writer - /// - /// The stream to write the state to - public override void WriteField(FastBufferWriter writer) - { - // Serialize the data we need to synchronize - writer.WriteValueSafe(SomeDataToSynchronize.Count); - foreach (var dataEntry in SomeDataToSynchronize) - { - writer.WriteValueSafe(dataEntry.SomeIntData); - writer.WriteValueSafe(dataEntry.SomeFloatData); - writer.WriteValueSafe(dataEntry.SomeListOfValues.Count); - foreach (var valueItem in dataEntry.SomeListOfValues) - { - writer.WriteValueSafe(valueItem); - } - } - } - - /// - /// Reads the complete state from the reader and applies it - /// - /// The stream to read the state from - public override void ReadField(FastBufferReader reader) - { - // De-Serialize the data being synchronized - var itemsToUpdate = (int)0; - reader.ReadValueSafe(out itemsToUpdate); - SomeDataToSynchronize.Clear(); - for (int i = 0; i < itemsToUpdate; i++) - { - var newEntry = new SomeData(); - reader.ReadValueSafe(out newEntry.SomeIntData); - reader.ReadValueSafe(out newEntry.SomeFloatData); - var itemsCount = (int)0; - var tempValue = (ulong)0; - reader.ReadValueSafe(out itemsCount); - newEntry.SomeListOfValues.Clear(); - for (int j = 0; j < itemsCount; j++) - { - reader.ReadValueSafe(out tempValue); - newEntry.SomeListOfValues.Add(tempValue); - } - SomeDataToSynchronize.Add(newEntry); - } - } - - public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta) - { - // Do nothing for this example - } - - public override void WriteDelta(FastBufferWriter writer) - { - // Do nothing for this example - } - } - - /// Bare minimum example of generic NetworkVariableBase derived class - [Serializable] - [GenerateSerializationForGenericParameter(0)] - public class MyCustomGenericNetworkVariable : NetworkVariableBase - { - /// Managed list of class instances - public List SomeDataToSynchronize = new List(); - - /// - /// Writes the complete state of the variable to the writer - /// - /// The stream to write the state to - public override void WriteField(FastBufferWriter writer) - { - // Serialize the data we need to synchronize - writer.WriteValueSafe(SomeDataToSynchronize.Count); - for (var i = 0; i < SomeDataToSynchronize.Count; ++i) - { - var dataEntry = SomeDataToSynchronize[i]; - // NetworkVariableSerialization is used for serializing generic types - NetworkVariableSerialization.Write(writer, ref dataEntry); - } - } - - /// - /// Reads the complete state from the reader and applies it - /// - /// The stream to read the state from - public override void ReadField(FastBufferReader reader) - { - // De-Serialize the data being synchronized - var itemsToUpdate = (int)0; - reader.ReadValueSafe(out itemsToUpdate); - SomeDataToSynchronize.Clear(); - for (int i = 0; i < itemsToUpdate; i++) - { - T newEntry = default; - // NetworkVariableSerialization is used for serializing generic types - NetworkVariableSerialization.Read(reader, ref newEntry); - SomeDataToSynchronize.Add(newEntry); - } - } - - public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta) - { - // Do nothing for this example - } - - public override void WriteDelta(FastBufferWriter writer) - { - // Do nothing for this example - } - } - - /// Example managed class used as the item type in the - /// MyCustomNetworkVariable.SomeDataToSynchronize list - [Serializable] - public class SomeData - { - public int SomeIntData = default; - public float SomeFloatData = default; - public List SomeListOfValues = new List(); - } - ``` +[!code-cs[](../../Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs#TestMyCustomNetworkVariable)] While the above example isn't the recommended way to synchronize a list where the number or order of elements in the list often changes, it's an example of how you can define your own rules using `NetworkVariableBase`. You can test the code above by: -- Using the above code with a project that includes Netcode for GameObjects v1.0 (or higher). + +- Using the above code with a project that includes Netcode for GameObjects. - Adding the `TestMyCustomNetworkVariable` component to an in-scene placed NetworkObject. - Creating a stand alone build and running that as a host or server. - Running the same scene within the Editor and connecting as a client. diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md b/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md index 0ea6c445e8..cfdd037143 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/networkvariable.md @@ -148,43 +148,277 @@ The [synchronization and notification example](#synchronization-and-notification The `OnValueChanged` example shows a simple server-authoritative `NetworkVariable` being used to track the state of a door (open or closed) using an RPC that's sent to the server. Each time the door is used by a client, the `Door.ToggleStateRpc` is invoked and the server-side toggles the state of the door. When the `Door.State.Value` changes, all connected clients are synchronized to the (new) current `Value` and the `OnStateChanged` method is invoked locally on each client. ```csharp -public class Door : NetworkBehaviour +using System.Runtime.CompilerServices; +using Unity.Netcode; +using UnityEngine; + +/// +/// Example of using a to drive changes +/// in state. +/// +/// +/// This is a simple state driven door example. +/// This script was written with recommended usages patterns in mind. +/// +public class Door : NetworkBehaviour, INetworkUpdateSystem { - public NetworkVariable State = new NetworkVariable(); + /// + /// The two door states. + /// + public enum DoorStates + { + Closed, + Open + } + /// + /// Initializes the door to a specific state (server side) when first spawned. + /// + [Tooltip("Configures the door's initial state when 1st spawned.")] + public DoorStates InitialState = DoorStates.Closed; + + /// + /// Used for example purposes. + /// When true, only the server can open and close the door. + /// Clients will receive a console log saying they could not open the door. + /// + public bool IsLocked; + + /// + /// A simple door state where the server has write permissions and everyone has read permissions. + /// + private NetworkVariable m_State = new NetworkVariable(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server); + + /// + /// The current state of the door. + /// + public DoorStates CurrentState => m_State.Value; + + /// + /// Invoked while the is in the process of + /// being spawned. + /// public override void OnNetworkSpawn() { - State.OnValueChanged += OnStateChanged; + // The write authority (server) doesn't need to know about its + // own changes (for this example) since it's the "single point + // of truth" for the door instance. + if (IsServer) + { + // Host/Server: + // Applies the configurable state upon spawning. + m_State.Value = InitialState; + } + else + { + // Clients: + // Subscribe to changes in the door's state. + m_State.OnValueChanged += OnStateChanged; + } } - public override void OnNetworkDespawn() + /// + /// Invoked once the door and all associated components + /// have finished the spawn process. + /// + protected override void OnNetworkPostSpawn() { - State.OnValueChanged -= OnStateChanged; + // Everyone updates their door state when finished spawning the door + // to ensure the door reflects (visually) its current state. + UpdateFromState(); + + // Begin updating this NetworkBehaviour instance once all + // netcode related components have finished the spawn process. + NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.Update); + base.OnNetworkPostSpawn(); } - public void OnStateChanged(bool previous, bool current) + /// + /// Example of using the usage pattern + /// where it only updates while spawned. + /// + /// The current update stage being invoked. + public void NetworkUpdate(NetworkUpdateStage updateStage) { - // note: `State.Value` will be equal to `current` here - if (State.Value) + switch (updateStage) { - // door is open: - // - rotate door transform - // - play animations, sound etc. + case NetworkUpdateStage.Update: + { + if (Input.GetKeyDown(KeyCode.Space)) + { + Interact(); + } + break; + } + } + } + + /// + /// Invoked just before this instance runs through its despawn + /// sequence. A good time to unsubscribe from things. + /// + public override void OnNetworkPreDespawn() + { + if (!IsServer) + { + m_State.OnValueChanged -= OnStateChanged; + } + + // Stop updating this NetworkBehaviour instance prior to running + // through the despawn process. + NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.Update); + base.OnNetworkPreDespawn(); + } + + /// + /// Server makes changes to the state. + /// Clients receive the changes in state. + /// + /// + /// When the previous state equals the current state, we are a client + /// that is doing its first synchronization of this door instance. + /// + /// The previous state. + /// The current state. + public void OnStateChanged(DoorStates previous, DoorStates current) + { + UpdateFromState(); + } + + /// + /// Invoke when the state is updated to apply the change + /// in door state to the door asset itself. + /// + private void UpdateFromState() + { + switch(m_State.Value) + { + case DoorStates.Closed: + { + // door is open: + // - rotate door transform + // - play animations, sound etc. + /// + /// Override to apply specific checks (like a player having the right + /// key to open the door) or make it a non-virtual class and add logic + /// directly to this method. + /// + /// The player attempting to open the door. + /// + protected virtual bool CanPlayerToggleState(NetworkObject player) + { + // For this example, if the door "is locked" then clients will + // not be able to open the door but the host-client's player can. + return !IsLocked || player.IsOwnedByServer; + } + + /// + /// Invoked by either a host or clients to interact with the door. + /// + public void Interact() + { + // Optional: + // This is only if you want clients to be able to + // interact with doors. A dedicated server would not + // be able to do this since it does not have a player. + if (IsServer && !IsHost) + { + // Optional to log a warning about this. + return; + } + + if (IsHost) + { + ToggleState(NetworkManager.LocalClientId); + } + else + { + // Clients send an RPC to server (write authority) who applies the + // change in state that will be synchronized with all client observers. + ToggleStateRpc(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private DoorStates NextToggleState() + { + return m_State.Value == DoorStates.Open ? DoorStates.Closed : DoorStates.Open; + } + + /// + /// Invoked only server-side + /// Primary method to handle toggling the door state. + /// + /// The client toggling the door state. + private void ToggleState(ulong clientId) + { + // Get the server-side client player instance + var playerObject = NetworkManager.SpawnManager.GetPlayerNetworkObject(clientId); + if (playerObject != null) + { + var nextToggleState = NextToggleState(); + if (CanPlayerToggleState(playerObject)) + { + // Host toggles the state + m_State.Value = nextToggleState; + UpdateFromState(); + } + else + { + ToggleStateFailRpc(nextToggleState, RpcTarget.Single(clientId, RpcTargetUse.Temp)); + } } else { - // door is closed: - // - rotate door transform - // - play animations, sound etc. + // Optional as to how you handle this. Since ToggleState is only invoked by + // sever-side only script, this could mean many things depending upon whether + // or not a client could interact with something and not have a player object. + // If that is the case, then don't even bother checking for a player object. + // If that is not the case, then there could be a timing issue between when + // something can be "interacted with" and when a player is about to be de-spawned. + // For this example, we just log a warning as this example was built with + // the requirement that a client has a spawned player object that is used for + // reference to determine if the client's player can toggle the state of the + // door or not. + NetworkLog.LogWarningServer($"Client-{clientId} has no spawned player object!"); } } - [Rpc(SendTo.Server)] - public void ToggleStateRpc() + /// + /// Invoked by clients. + /// Re-directs to the common method. + /// + /// includes that is automatically populated for you. + [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] + private void ToggleStateRpc(RpcParams rpcParams = default) + { + ToggleState(rpcParams.Receive.SenderClientId); + } + + /// + /// Optional: + /// Handling when a player cannot open a door. + /// + /// includes that is automatically populated for you. + [Rpc(SendTo.SpecifiedInParams, InvokePermission = RpcInvokePermission.Server)] + private void ToggleStateFailRpc(DoorStates doorState, RpcParams rpcParams = default) { - // this will cause a replication over the network - // and ultimately invoke `OnValueChanged` on receivers - State.Value = !State.Value; + // Provide player feedback that toggling failed. + Debug.Log($"Failed to {doorState} the door!"); } } ``` @@ -318,7 +552,7 @@ public class PlayerState : NetworkBehaviour ## Complex types -Almost all of the examples on this page have been focused around numeric [value types](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types). Netcode for GameObjects also supports complex types and can support both unmanaged types *and* managed types (although avoiding managed types where possible will improve your game's performance). +Any type that implements the [`INetworkSerializable`](../advanced-topics/serialization/inetworkserializable.md) interface can be used inside a `NetworkVariable`. To synchronize a custom type that is not supported by the default serialization provided by Netcode for GameObjects, implement this interface to allow it to be used inside a `NetworkVariable`. ### Synchronizing complex types example @@ -348,7 +582,7 @@ public class PlayerState : NetworkBehaviour void Awake() { //NetworkList can't be initialized at declaration time like NetworkVariable. It must be initialized in Awake instead. - //If you do initialize at declaration, you will run into Memmory leak errors. + //If you do initialize at declaration, you will run into Memory leak errors. TeamAreaWeaponBoosters = new NetworkList(); } @@ -473,88 +707,12 @@ public struct AreaWeaponBooster : INetworkSerializable, System.IEquatable [!NOTE] -> The `NetworkVariable` and `NetworkList` classes were created as `NetworkVariableBase` class implementation examples. While the `NetworkVariable` class is considered production ready, you might run into scenarios where you have a more advanced implementation in mind. In this case, we encourage you to create your own custom implementation. - -To create your own `NetworkVariableBase` derived container, you should: - -- Create a class deriving from `NetworkVariableBase`. - -- Assure the the following methods are overridden: - - `void WriteField(FastBufferWriter writer)` - - `void ReadField(FastBufferReader reader)` - - `void WriteDelta(FastBufferWriter writer)` - - `void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)` -- Depdending upon your custom `NetworkVariableBase` container, you might look at `NetworkVariable` or `NetworkList` to see how those two examples were implemented. - - - -#### NetworkVariableSerialization<T> - -The way you read and write network variables changes depending on the type you use. - -* Known, non-generic types: Use `FastBufferReader.ReadValue` to read from and `FastBufferWriter.WriteValue` to write to the network variable value. -* Integer types: This type gives you the option to use `BytePacker` and `ByteUnpacker` to compress the network variable value. This process can save bandwidth but adds CPU processing time. -* Generic types: Use serializers that Unity generates based on types discovered during a compile-time code generation process. This means you need to tell Unity's code generation algorithm which types to generate serializers for. To tell Unity which types to serialize, use the following methods: - * Use `GenerateSerializationForTypeAttribute` to serialize hard-coded types. - * Use `GenerateSerializationForGenericParameterAttribute` to serialize generic types. - To learn how to use these methods, refer to [Network variable serialization](#network-variable-serialization). - -##### Tell Unity to serialize a hard-coded type -The following code example uses `GenerateSerializationForTypeAttribute` to generate serialization for a specific hard-coded type: -```csharp -[GenerateSerializationForType(typeof(Foo))] -public class MyNetworkVariableTypeUsingFoo : NetworkVariableBase {} -``` - -You can call a type that you know the name of with the `FastBufferReader` or `FastBufferWriter` methods. These methods don't work for Generic types because the name of the type is unknown. -##### Tell Unity to serialize a generic type -The following code example uses `GenerateSerializationForGenericParameterAttribute` to generate serialization for a specific Generic parameter in your `NetworkVariable` type: -```csharp -[GenerateSerializationForGenericParameter(0)] -public class MyNetworkVariableType : NetworkVariableBase {} -``` - -This attribute accepts an integer that indicates which parameter in the type to generate serialization for. This value is 0-indexed, which means that the first type is 0, the second type is 1, and so on. -The following code example places the attribute more than once on one class to generate serialization for multiple types, in this case,`TFirstType` and `TSecondType: - -```csharp -[GenerateSerializationForGenericParameter(0)] -[GenerateSerializationForGenericParameter(1)] -public class MyNetworkVariableType : NetworkVariableBase {} -``` - - -The `GenerateSerializationForGenericParameterAttribute` and `GenerateSerializationForTypeAttribute` attributes make Unity's code generation create the following methods: - -```csharp -NetworkVariableSerialization.Write(FastBufferWriter writer, ref T value); -NetworkVariableSerialization.Read(FastBufferWriter writer, ref T value); -NetworkVariableSerialization.Duplicate(in T value, ref T duplicatedValue); -NetworkVariableSerialization.AreEqual(in T a, in T b); -``` - -For dynamically allocated types with a value that isn't `null` (for example, managed types and collections like NativeArray and NativeList) call `Read` to read the value in the existing object and write data into it directy (in-place). This avoids more allocations. - -You can use `AreEqual` to determine if a value is different from the value that `Duplicate` cached. This avoids sending the same value multiple times. You can also use the previous value that `Duplicate` cached to calculate deltas to use in `ReadDelta` and `WriteDelta`. - -The type you use must be serializable according to the "Supported Types" list above. Each type needs its own serializer instantiated, so this step tells the codegen which types to create serializers for. - -> [!NOTE] Unity's code generator assumes that all `NetworkVariable` types exist as fields inside NetworkBehaviour types. This means that Unity only inspects fields inside NetworkBehaviour types to identify the types to create serializers for. - - ### Custom NetworkVariable Example - -This example shows a custom `NetworkVariable` type to help you understand how you might implement such a type. In the current version of Netcode for GameObjects, this example is possible without using a custom `NetworkVariable` type; however, for more complex situations that aren't natively supported, this basic example should help inform you of how to approach the implementation: - -Looking at the read and write segments of code within `AreaWeaponBooster.NetworkSerialize`, the nested complex type property `ApplyWeaponBooster` handles its own serialization and de-serialization. The `ApplyWeaponBooster`'s implemented `NetworkSerialize` method serializes and deserializes any `AreaWeaponBooster` type property. This design approach can help reduce code replication while providing a more modular foundation to build even more complex, nested types. - +Further information on customizing the serialization of complex types can be found in [custom serialization](../advanced-topics/custom-serialization.md#networkvariable). ## Strings -While `NetworkVariable` does support managed `INetworkSerializable` types, strings aren't in the list of supported types. This is because strings in C# are immutable types, preventing them from being deserialized in-place, so every update to a `NetworkVariable` would cause a Garbage Collected allocation to create the new string, which may lead to performance problems. +While `NetworkVariable` does support managed `INetworkSerializable` types, strings aren't in the [list of supported types](#supported-types). This is because strings in C# are immutable types, preventing them from being deserialized in-place, so every update to a `NetworkVariable` would cause a Garbage Collected allocation to create the new string, which may lead to performance problems. -While it's technically possible to support strings using custom serialization through `UserNetworkVariableSerialization`, it isn't recommended to do so due to the performance implications that come with it. Instead, we recommend using one of the `Unity.Collections.FixedString` value types. In the below example, we used a `FixedString128Bytes` as the `NetworkVariable` value type. On the server side, it changes the string value each time you press the space bar on the server or host instance. Joining clients will be synchronized with the current value applied on the server side, and each time you hit the space bar on the server side, the client synchronizes with the changed string. +While it's technically possible to support strings using custom serialization through [`UserNetworkVariableSerialization`](../advanced-topics/custom-serialization.md#networkvariable), it isn't recommended to do so due to the performance implications that come with it. Instead, it's recommended to use one of the `Unity.Collections.FixedString` value types. In the below example, a `FixedString128Bytes` is the `NetworkVariable` value type. On the server side, it changes the string value each time you press the space bar on the server or host instance. Joining clients will be synchronized with the current value applied on the server side, and each time you hit the space bar on the server side, the client synchronizes with the changed string. > [!NOTE] > `NetworkVariable` won't serialize the entire 128 bytes each time the `Value` is changed. Only the number of bytes that are actually used to store the string value will be sent, no matter which size of `FixedString` you use. @@ -621,3 +779,9 @@ public class TestFixedString : NetworkBehaviour > [!NOTE] > The above example uses a pre-set list of strings to cycle through for example purposes only. If you have a predefined set of text strings as part of your actual design then you would not want to use a FixedString to handle synchronizing the changes to `m_TextString`. Instead, you would want to use a `uint` for the type `T` where the `uint` was the index of the string message to apply to `m_TextString`. + +### Delta updates + +To save bandwidth, `NetworkVariables` can send delta updates. A delta is a compact description of what changed since the last sync. By default, [collection types](#using-collections-with-networkvariables) all support sending delta updates. This means adding a single item to a large list doesn't need to send the entire list over the network. For complex types, it is often worth considering whether sending deltas will improve bandwidth. Delta serialization can be configured via [`UserNetworkVariableSerialization`](../advanced-topics/custom-serialization.md#serializing-delta-updates). + +When a NetworkObject is spawned or a late-joining client first sees it, every `NetworkVariable` will be serialized in full. This allows game clients to start receiving deltas from a known state. diff --git a/com.unity.netcode.gameobjects/Documentation~/learn/distributed-authority-quick-start.md b/com.unity.netcode.gameobjects/Documentation~/learn/distributed-authority-quick-start.md index 9b292a4771..897d7e30e2 100644 --- a/com.unity.netcode.gameobjects/Documentation~/learn/distributed-authority-quick-start.md +++ b/com.unity.netcode.gameobjects/Documentation~/learn/distributed-authority-quick-start.md @@ -164,7 +164,7 @@ public class ConnectionManager : MonoBehaviour using Unity.Netcode.Components; using UnityEngine; #if UNITY_EDITOR -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEditor; /// /// The custom editor for the component. diff --git a/com.unity.netcode.gameobjects/Documentation~/serialization.md b/com.unity.netcode.gameobjects/Documentation~/serialization.md index fb39fad334..4dfd16f484 100644 --- a/com.unity.netcode.gameobjects/Documentation~/serialization.md +++ b/com.unity.netcode.gameobjects/Documentation~/serialization.md @@ -4,11 +4,14 @@ Netcode for GameObjects has built-in serialization support for C# and Unity prim | **Topic** | **Description** | | :------------------------------ | :------------------------------- | +| **[Serialization overview](advanced-topics/serialization/serialization-overview.md)** | An overview of how Netcode for GameObjects handles serialization. | | **[C# primitives](advanced-topics/serialization/cprimitives.md)** | C# primitive types are serialized by built-in serialization code. These types include `bool`, `char`, `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`, and `string`. | | **[Unity primitives](advanced-topics/serialization/unity-primitives.md)** | Unity Primitive `Color`, `Color32`, `Vector2`, `Vector3`, `Vector4`, `Quaternion`, `Ray`, `Ray2D` types will be serialized by built-in serialization code. | | **[Enum types](advanced-topics/serialization/enum-types.md)** | A user-defined enum type will be serialized by built-in serialization code (with underlying integer type). | | **[Arrays](advanced-topics/serialization/serialization-arrays.md)** | Netcode for GameObjects has built-in serialization code for arrays of [C# value-type primitives](advanced-topics/serialization/cprimitives.md), like `int[]`, and [Unity primitive types](advanced-topics/serialization/unity-primitives.md). Any arrays of types that aren't handled by the built-in serialization code, such as `string[]`, need to be handled using a container class or structure that implements the [`INetworkSerializable`](advanced-topics/serialization/inetworkserializable.md) interface. | -| **[INetworkSerializable](advanced-topics/serialization/inetworkserializable.md)** | You can use the `INetworkSerializable` interface to define custom serializable types. | +| **[Customize serializable types with INetworkSerializable](advanced-topics/serialization/inetworkserializable.md)** | You can use the `INetworkSerializable` interface to define custom serializable types. | +| **[Serialize unmanaged structs with INetworkSerializeByMemcpy](advanced-topics/serialization/inetworkserializebymemcpy.md)** | You can use the `INetworkSerializeByMemcpy` interface to mark an unmanaged struct type as being trivially serializable. | | **[Custom serialization](advanced-topics/custom-serialization.md)** | Create custom serialization types. | | **[NetworkObject serialization](advanced-topics/serialization/networkobject-serialization.md)** | `GameObjects`, `NetworkObjects` and NetworkBehaviour aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default. There are two convenience wrappers which can be used to send a reference to a NetworkObject or a NetworkBehaviour over RPCs or `NetworkVariables`. | -| **[FastBufferWriter and FastBufferReader](advanced-topics/fastbufferwriter-fastbufferreader.md)** | The serialization and deserialization is done via `FastBufferWriter` and `FastBufferReader`. These have methods for serializing individual types and methods for serializing packed numbers, but in particular provide a high-performance method called `WriteValue()/ReadValue()` (for Writers and Readers, respectively) that can extremely quickly write an entire unmanaged struct to a buffer. | \ No newline at end of file +| **[FastBufferWriter and FastBufferReader](advanced-topics/fastbufferwriter-fastbufferreader.md)** | The serialization and deserialization is done via `FastBufferWriter` and `FastBufferReader`. These have methods for serializing individual types and methods for serializing packed numbers, but in particular provide a high-performance method called `WriteValue()/ReadValue()` (for Writers and Readers, respectively) that can extremely quickly write an entire unmanaged struct to a buffer. | +| **[BufferSerializer](advanced-topics/bufferserializer.md)** | A bi-directional serializer primarily used for serializing within [`INetworkSerializable`](serialization/inetworkserializable.md) types. | diff --git a/com.unity.netcode.gameobjects/Editor/Analytics/AnalyticsHandler.cs b/com.unity.netcode.gameobjects/Editor/Analytics/AnalyticsHandler.cs index b366e225eb..6df5c2d822 100644 --- a/com.unity.netcode.gameobjects/Editor/Analytics/AnalyticsHandler.cs +++ b/com.unity.netcode.gameobjects/Editor/Analytics/AnalyticsHandler.cs @@ -2,7 +2,7 @@ using System; using UnityEngine.Analytics; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { internal class AnalyticsHandler : IAnalytic where T : IAnalytic.IData { diff --git a/com.unity.netcode.gameobjects/Editor/Analytics/NetcodeAnalytics.cs b/com.unity.netcode.gameobjects/Editor/Analytics/NetcodeAnalytics.cs index 0a18028c84..b1c4e69698 100644 --- a/com.unity.netcode.gameobjects/Editor/Analytics/NetcodeAnalytics.cs +++ b/com.unity.netcode.gameobjects/Editor/Analytics/NetcodeAnalytics.cs @@ -4,7 +4,7 @@ using UnityEngine; using UnityEngine.Analytics; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// Used to collection network session configuration information diff --git a/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalytics.cs b/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalytics.cs index 9ba7e46142..cd1ca3b1de 100644 --- a/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalytics.cs +++ b/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalytics.cs @@ -4,7 +4,7 @@ using UnityEngine; using UnityEngine.Analytics; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { [Serializable] internal struct NetworkManagerAnalytics : IAnalytic.IData, IEquatable diff --git a/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalyticsHandler.cs b/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalyticsHandler.cs index 7866df9621..18f9bec50f 100644 --- a/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalyticsHandler.cs +++ b/com.unity.netcode.gameobjects/Editor/Analytics/NetworkManagerAnalyticsHandler.cs @@ -1,7 +1,7 @@ #if UNITY_EDITOR using UnityEngine.Analytics; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { [AnalyticInfo("NGO_NetworkManager", "unity.netcode", 5, 100, 1000)] internal class NetworkManagerAnalyticsHandler : AnalyticsHandler diff --git a/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs b/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs index 0f4e7e2b1d..fce6c4572d 100644 --- a/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs +++ b/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs @@ -2,7 +2,7 @@ #if UNITY_INCLUDE_TESTS #if UNITY_EDITOR -[assembly: InternalsVisibleTo("Unity.Netcode.Editor.Tests")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor.Tests")] [assembly: InternalsVisibleTo("TestProject.Runtime.Tests")] #endif // UNITY_EDITOR #endif // UNITY_INCLUDE_TESTS diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs index 8d29e112f0..2b3b7d99f1 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs @@ -12,7 +12,7 @@ using UnityEngine; using Object = System.Object; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal static class CodeGenHelpers { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs index d74b737bde..03bea544bb 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs @@ -11,7 +11,7 @@ using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; using MethodAttributes = Mono.Cecil.MethodAttributes; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class INetworkMessageILPP : ILPPInterface { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs index abfc9a26ae..c4123d5b88 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs @@ -8,7 +8,7 @@ using Unity.CompilationPipeline.Common.ILPostProcessing; using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class INetworkSerializableILPP : ILPPInterface { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 04a72d7c9e..3918b03da8 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -16,7 +16,7 @@ using MethodAttributes = Mono.Cecil.MethodAttributes; using ParameterAttributes = Mono.Cecil.ParameterAttributes; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class NetworkBehaviourILPP : ILPPInterface { @@ -2150,9 +2150,10 @@ private bool GetReadMethodForParameter(TypeReference paramType, out MethodRefere { typeMethod = GetFastBufferReaderReadMethod(k_ReadValueInPlaceMethodName, paramType); } - else -#endif + else if (paramType.Resolve().FullName == "Unity.Collections.NativeArray`1") +#else if (paramType.Resolve().FullName == "Unity.Collections.NativeArray`1") +#endif { typeMethod = GetFastBufferReaderReadMethod(k_ReadValueTempMethodName, paramType); } diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs index 4adbbfd8ed..369fb04097 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs @@ -6,7 +6,7 @@ using Mono.Cecil; using Unity.CompilationPipeline.Common.ILPostProcessing; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal class PostProcessorAssemblyResolver : IAssemblyResolver { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs index f96ba3396c..7c904750f5 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs @@ -2,7 +2,7 @@ using System.Reflection; using Mono.Cecil; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal class PostProcessorReflectionImporter : DefaultReflectionImporter { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs index 81f80f04ed..1f7e41c3c8 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs @@ -1,6 +1,6 @@ using Mono.Cecil; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index 57295be52e..fa15a7251f 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -7,7 +7,7 @@ using Unity.CompilationPipeline.Common.ILPostProcessing; using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class RuntimeAccessModifiersILPP : ILPPInterface { @@ -97,17 +97,23 @@ private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assem { foreach (var fieldDefinition in typeDefinition.Fields) { +#pragma warning disable CS0618 // Type or member is obsolete if (fieldDefinition.Name == nameof(NetworkManager.__rpc_func_table)) +#pragma warning restore CS0618 // Type or member is obsolete { fieldDefinition.IsPublic = true; } +#pragma warning disable CS0618 // Type or member is obsolete if (fieldDefinition.Name == nameof(NetworkManager.RpcReceiveHandler)) +#pragma warning restore CS0618 // Type or member is obsolete { fieldDefinition.IsPublic = true; } +#pragma warning disable CS0618 // Type or member is obsolete if (fieldDefinition.Name == nameof(NetworkManager.__rpc_name_table)) +#pragma warning restore CS0618 // Type or member is obsolete { fieldDefinition.IsPublic = true; } @@ -115,7 +121,9 @@ private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assem foreach (var nestedTypeDefinition in typeDefinition.NestedTypes) { +#pragma warning disable CS0618 // Type or member is obsolete if (nestedTypeDefinition.Name == nameof(NetworkManager.RpcReceiveHandler)) +#pragma warning restore CS0618 // Type or member is obsolete { nestedTypeDefinition.IsNestedPublic = true; } @@ -151,7 +159,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) { fieldDefinition.IsFamilyOrAssembly = true; } -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_name_table)) { fieldDefinition.IsFamilyOrAssembly = true; diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef b/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef index 1accd5e5d7..1b93c60458 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef @@ -1,6 +1,6 @@ { - "name": "Unity.Netcode.Editor.CodeGen", - "rootNamespace": "Unity.Netcode.Editor.CodeGen", + "name": "Unity.Netcode.GameObjects.Editor.CodeGen", + "rootNamespace": "Unity.Netcode.GameObjects.Editor.CodeGen", "references": [ "Unity.Netcode.Runtime", "Unity.Collections" @@ -32,4 +32,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs index 48ddd77fde..05f83876bd 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs @@ -1,7 +1,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { /// /// A of type . diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs index 70dfc02021..6a43d62e7e 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs @@ -1,6 +1,6 @@ using UnityEditor; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { internal class NetcodeForGameObjectsEditorSettings { diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs index 22ed67e48c..a8b521117c 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs @@ -5,7 +5,7 @@ using Directory = UnityEngine.Windows.Directory; using File = UnityEngine.Windows.File; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { internal static class NetcodeSettingsProvider { diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs index f74530e60f..cab11b3be1 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs @@ -2,7 +2,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { /// /// Updates the default instance when prefabs are updated (created, moved, deleted) in the project. diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs index 68113ca32f..6b1128c829 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs @@ -2,7 +2,7 @@ using UnityEditorInternal; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The custom editor for the . diff --git a/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs b/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs index 2311da1477..98193cc631 100644 --- a/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs @@ -5,7 +5,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// Internal use. Hides the script field for the given component. diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers.meta b/com.unity.netcode.gameobjects/Editor/Icons.meta similarity index 77% rename from testproject/Legacy/MultiprocessRuntime/Helpers.meta rename to com.unity.netcode.gameobjects/Editor/Icons.meta index 2b9c70c07b..47f083a506 100644 --- a/testproject/Legacy/MultiprocessRuntime/Helpers.meta +++ b/com.unity.netcode.gameobjects/Editor/Icons.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ae0eb1e25098241b182babd91479ea26 +guid: 09ac49ca8d1df6347814a8a4760eb02c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png new file mode 100644 index 0000000000..c94cfb2e9b Binary files /dev/null and b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg.meta b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png.meta similarity index 51% rename from testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg.meta rename to com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png.meta index e675f9e356..2a11d94057 100644 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg.meta +++ b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png.meta @@ -1,9 +1,9 @@ fileFormatVersion: 2 -guid: 4d67ff4fda6ec4805b2fd16af441aa47 +guid: 04d779b185ebc8d488b5d25eeb21c611 TextureImporter: internalIDToNameTable: [] externalObjects: {} - serializedVersion: 11 + serializedVersion: 13 mipmaps: mipMapMode: 0 enableMipMap: 1 @@ -20,10 +20,12 @@ TextureImporter: externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 + flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 + ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 @@ -50,7 +52,7 @@ TextureImporter: spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 - alphaIsTransparency: 0 + alphaIsTransparency: 1 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 @@ -62,10 +64,12 @@ TextureImporter: textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 platformSettings: - - serializedVersion: 3 + - serializedVersion: 4 buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 + maxTextureSize: 64 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 @@ -73,12 +77,66 @@ TextureImporter: crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: iOS + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: WindowsStoreApps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] + customData: physicsShape: [] bones: [] spriteID: @@ -88,9 +146,11 @@ TextureImporter: edges: [] weights: [] secondaryTextures: [] - spritePackingTag: + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs new file mode 100644 index 0000000000..f7529de12c --- /dev/null +++ b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs @@ -0,0 +1,60 @@ +using Unity.Netcode.Logging; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Unity.Netcode.Editor +{ + /// + /// A that sets the property to true for all s in the scene. + /// Ensures that InScenePlaced is always true for all objects in the scene. + /// + /// + /// This will always run as the game enters the scene, + /// + internal class SetInScenePlaced : IProcessSceneWithReport + { + public int callbackOrder => 0; + public void OnProcessScene(Scene scene, BuildReport report) + { + var log = new ContextualLogger(); + log.AddInfo(scene.name, scene.handle); + foreach (var networkObject in FindObjects.FromSceneByType(scene, true)) + { + if (networkObject.SceneOrigin.IsValid() && networkObject.SceneOrigin.handle != scene.handle) + { + log.Warning(new Context(LogLevel.Developer, $"{nameof(NetworkObject)}'s SceneOrigin doesn't match current scene being processed! Skipping processing.").AddInfo("SceneOrigin", networkObject.SceneOriginHandle).AddNetworkObject(networkObject)); + continue; + } + + if (networkObject.HasBeenSpawned) + { + log.Error(new Context(LogLevel.Normal, $"Processing {nameof(NetworkObject)} that has already been spawned! This should not be possible. Skipping processing.").AddNetworkObject(networkObject)); + continue; + } + + networkObject.InScenePlaced = true; + } + } + } + + /// + /// An that sets the property to false for all s in prefabs. + /// Ensures that InScenePlaced is always false for all prefab objects. + /// This is important because when a prefab is instantiated in the scene, it should be treated as a dynamically spawned object. + /// + internal class InScenePlacedPrefabBuilder : AssetPostprocessor + { + public void OnPostprocessPrefab(GameObject root) + { + var networkObjects = root.GetComponentsInChildren(true); + foreach (var networkObject in networkObjects) + { + networkObject.InScenePlaced = false; + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs.meta b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs.meta new file mode 100644 index 0000000000..784aa38575 --- /dev/null +++ b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cda0fb70bdcd545a7983ca78f516bcff \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Editor/NetcodeEditorBase.cs b/com.unity.netcode.gameobjects/Editor/NetcodeEditorBase.cs index b61a77e6b6..fb10ebd25a 100644 --- a/com.unity.netcode.gameobjects/Editor/NetcodeEditorBase.cs +++ b/com.unity.netcode.gameobjects/Editor/NetcodeEditorBase.cs @@ -2,7 +2,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The base Netcode Editor helper class to display derived based components
diff --git a/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs b/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs index 9060b8993c..7fbf7789dd 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs @@ -4,7 +4,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { [CustomPropertyDrawer(typeof(NetworkAnimator.AnimatorParametersListContainer))] internal class NetworkAnimatorParameterEntryDrawer : PropertyDrawer diff --git a/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs index 1312ff69e6..3d85194ac8 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Reflection; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.GameObjects.Editor.Configuration; using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The for @@ -322,18 +322,13 @@ private void OnEnable() } /// - /// Recursively finds the root parent of a + /// Obsolete method to find root parent of a . + /// Use instead. /// /// The current we are inspecting for a parent /// the root parent for the first passed into the method - public static Transform GetRootParentTransform(Transform transform) - { - if (transform.parent == null || transform.parent == transform) - { - return transform; - } - return GetRootParentTransform(transform.parent); - } + [Obsolete("Use transform.root instead", true)] + public static Transform GetRootParentTransform(Transform transform) => transform.root; /// /// Used to determine if a GameObject has one or more NetworkBehaviours but @@ -358,7 +353,7 @@ public static void CheckForNetworkObject(GameObject gameObject, bool networkObje } // Now get the root parent transform to the current GameObject (or itself) - var rootTransform = GetRootParentTransform(gameObject.transform); + var rootTransform = gameObject.transform.root; if (!rootTransform.TryGetComponent(out var networkManager)) { networkManager = rootTransform.GetComponentInChildren(); diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs index a4e339f558..a140415e87 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.GameObjects.Editor.Configuration; using Unity.Netcode.Logging; using UnityEditor; using UnityEngine; @@ -10,7 +10,7 @@ using UnityEngine.Assemblies; #endif -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// This handles the translation between the and diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs index 5c7e554baf..715d15584d 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.Linq; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.GameObjects.Editor.Configuration; using Unity.Netcode.Logging; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { #if UNITY_EDITOR /// @@ -195,9 +195,9 @@ public bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool { return isParented; } - else // If we are no longer a child, then we can remove ourself from this list - if (transform.root == gameObject.transform) + else if (transform.root == gameObject.transform) { + // If we are no longer a child, then we can remove ourself from this list s_LastKnownNetworkManagerParents.Remove(networkManager); } } diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index 04e0d1f698..d1e3ab5b40 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -2,10 +2,14 @@ #if BYPASS_DEFAULT_ENUM_DRAWER && MULTIPLAYER_SERVICES_SDK_INSTALLED using System.Linq; #endif +#if UNIFIED_NETCODE +using Unity.NetCode; +using Unity.NetCode.Editor; +#endif using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The for @@ -23,6 +27,34 @@ public class NetworkObjectEditor : UnityEditor.Editor private static readonly string[] k_HiddenFields = { "m_Script" }; +#if UNIFIED_NETCODE + /// + /// Register for the GhostObject removal event. + /// + [InitializeOnLoadMethod] + private static void OnApplicationStart() + { + GhostObjectEditor.OnGhostObjectPreRemoval = OnGhostObjectPreRemoval; + } + + /// + /// Callback to remove the GhostBehaviours prior to removing GhostObject. + /// + /// The with the component being removed. + private static void OnGhostObjectPreRemoval(GameObject gameObject) + { + var ghostBehaviours = gameObject.GetComponentsInChildren(); + for (int i = ghostBehaviours.Length - 1; i >= 0; i--) + { + DestroyImmediate(ghostBehaviours[i], true); + } + var networkObject = gameObject.GetComponent(); + networkObject.GhostObject = null; + networkObject.HasGhost = false; + networkObject.HadBridge = true; + } +#endif + private void Initialize() { if (m_Initialized) @@ -32,6 +64,9 @@ private void Initialize() m_Initialized = true; m_NetworkObject = (NetworkObject)target; +#if UNIFIED_NETCODE + m_NetworkObject.UnifiedValidation(); +#endif } /// @@ -67,14 +102,10 @@ public override void OnInspectorGUI() EditorGUILayout.Toggle(nameof(NetworkObject.IsOwner), m_NetworkObject.IsOwner); EditorGUILayout.Toggle(nameof(NetworkObject.IsOwnedByServer), m_NetworkObject.IsOwnedByServer); EditorGUILayout.Toggle(nameof(NetworkObject.IsPlayerObject), m_NetworkObject.IsPlayerObject); - if (m_NetworkObject.IsSceneObject.HasValue) - { - EditorGUILayout.Toggle(nameof(NetworkObject.IsSceneObject), m_NetworkObject.IsSceneObject.Value); - } - else - { - EditorGUILayout.TextField(nameof(NetworkObject.IsSceneObject), "null"); - } +#pragma warning disable CS0618 // Type or member is obsolete + // TODO-3.x: Update name in 3.x branch + EditorGUILayout.Toggle(nameof(NetworkObject.IsSceneObject), m_NetworkObject.InScenePlaced); +#pragma warning restore CS0618 // Type or member is obsolete EditorGUILayout.Toggle(nameof(NetworkObject.DestroyWithScene), m_NetworkObject.DestroyWithScene); EditorGUILayout.TextField(nameof(NetworkObject.NetworkManager), m_NetworkObject.NetworkManager == null ? "null" : m_NetworkObject.NetworkManager.gameObject.name); GUI.enabled = guiEnabled; diff --git a/com.unity.netcode.gameobjects/Editor/NetworkRigidbodyBaseEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkRigidbodyBaseEditor.cs index 40c15fc4fc..46bb7c4f6d 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkRigidbodyBaseEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkRigidbodyBaseEditor.cs @@ -2,7 +2,7 @@ using Unity.Netcode.Components; using UnityEditor; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { [CustomEditor(typeof(NetworkRigidbodyBase), true)] [CanEditMultipleObjects] diff --git a/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs index 31f3314bea..1fcb77f709 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs @@ -1,8 +1,9 @@ +using System.Runtime.CompilerServices; using Unity.Netcode.Components; using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The for @@ -94,9 +95,40 @@ public override void OnEnable() base.OnEnable(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetGUIActive(bool active = true) + { +#if UNIFIED_NETCODE + if (Application.IsPlaying(m_NetworkObject) && m_NetworkObject.HasGhost) + { + GUI.enabled = false; + } + else + { + GUI.enabled = active; + } +#else + GUI.enabled = active; +#endif + + } + + private NetworkObject m_NetworkObject; + private void DisplayNetworkTransformProperties() { var networkTransform = target as NetworkTransform; + +#if UNIFIED_NETCODE + m_NetworkObject = networkTransform.GetComponent(); + var hasGhost = m_NetworkObject.HasGhost; + SetGUIActive(); +#else +#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D + var hasGhost = false; +#endif +#endif + EditorGUILayout.LabelField("Axis to Synchronize", EditorStyles.boldLabel); { GUILayout.BeginHorizontal(); @@ -190,7 +222,7 @@ private void DisplayNetworkTransformProperties() { networkTransform.UseUnreliableDeltas = false; } - GUI.enabled = !networkTransform.SwitchTransformSpaceWhenParented; + SetGUIActive(!networkTransform.SwitchTransformSpaceWhenParented); if (networkTransform.SwitchTransformSpaceWhenParented) { EditorGUILayout.BeginHorizontal(); @@ -202,11 +234,13 @@ private void DisplayNetworkTransformProperties() { EditorGUILayout.PropertyField(m_UseUnreliableDeltas); } - GUI.enabled = true; + + SetGUIActive(true); EditorGUILayout.Space(); EditorGUILayout.LabelField("Configurations", EditorStyles.boldLabel); - GUI.enabled = !networkTransform.UseUnreliableDeltas; + + SetGUIActive(!networkTransform.UseUnreliableDeltas); if (networkTransform.UseUnreliableDeltas) { EditorGUILayout.BeginHorizontal(); @@ -218,7 +252,7 @@ private void DisplayNetworkTransformProperties() { EditorGUILayout.PropertyField(m_SwitchTransformSpaceWhenParented); } - GUI.enabled = true; + SetGUIActive(true); if (m_SwitchTransformSpaceWhenParented.boolValue) { m_TickSyncChildren.boolValue = true; @@ -297,20 +331,23 @@ private void DisplayNetworkTransformProperties() #if COM_UNITY_MODULES_PHYSICS // if rigidbody is present but network rigidbody is not present - if (networkTransform.TryGetComponent(out _) && networkTransform.TryGetComponent(out _) == false) + if (hasGhost && networkTransform.TryGetComponent(out _) && networkTransform.TryGetComponent(out _) == false) { - EditorGUILayout.HelpBox("This GameObject contains a Rigidbody but no NetworkRigidbody.\n" + + EditorGUILayout.HelpBox("This GameObject contains a Rigidbody but no NetworkRigidbody.\n " + "Add a NetworkRigidbody component to improve Rigidbody synchronization.", MessageType.Warning); } #endif // COM_UNITY_MODULES_PHYSICS #if COM_UNITY_MODULES_PHYSICS2D - if (networkTransform.TryGetComponent(out _) && networkTransform.TryGetComponent(out _) == false) + if (!hasGhost && networkTransform.TryGetComponent(out _) && networkTransform.TryGetComponent(out _) == false) { EditorGUILayout.HelpBox("This GameObject contains a Rigidbody2D but no NetworkRigidbody2D.\n" + "Add a NetworkRigidbody2D component to improve Rigidbody2D synchronization.", MessageType.Warning); } #endif // COM_UNITY_MODULES_PHYSICS2D +#if UNIFIED_NETCODE + GUI.enabled = true; +#endif } /// diff --git a/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs b/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs index 0d109a0115..8f6ffd67a6 100644 --- a/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs +++ b/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs @@ -5,7 +5,7 @@ using UnityEditor.PackageManager; using UnityEditor.PackageManager.Requests; -namespace Unity.Netcode.Editor.PackageChecker +namespace Unity.Netcode.GameObjects.Editor.PackageChecker { [InitializeOnLoad] internal class UTPAdapterChecker diff --git a/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef b/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef index 9599c27b6c..b4a04f0564 100644 --- a/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef +++ b/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef @@ -1,6 +1,6 @@ { - "name": "Unity.Netcode.PackageChecker.Editor", - "rootNamespace": "Unity.Netcode.Editor.PackageChecker", + "name": "Unity.Netcode.GameObjects.PackageChecker.Editor", + "rootNamespace": "Unity.Netcode.GameObjects.Editor.PackageChecker", "references": [], "includePlatforms": [ "Editor" @@ -19,4 +19,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef b/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef index 926f85b604..9ef9dac45f 100644 --- a/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef +++ b/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef @@ -1,13 +1,15 @@ { - "name": "Unity.Netcode.Editor", - "rootNamespace": "Unity.Netcode.Editor", + "name": "Unity.Netcode.GameObjects.Editor", + "rootNamespace": "Unity.Netcode.GameObjects.Editor", "references": [ "Unity.Netcode.Runtime", "Unity.Netcode.Components", "Unity.Services.Relay", "Unity.Networking.Transport", "Unity.Services.Core", - "Unity.Services.Authentication" + "Unity.Services.Authentication", + "Unity.NetCode", + "Unity.NetCode.Editor" ], "includePlatforms": [ "Editor" @@ -43,6 +45,16 @@ "name": "com.unity.services.multiplayer", "expression": "0.2.0", "define": "MULTIPLAYER_SERVICES_SDK_INSTALLED" + }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" + }, + { + "name": "com.unity.multiplayer.playmode", + "expression": "0.1.0", + "define": "UNITY_MULTIPLAYER_PLAYMODE" } ], "noEngineReferences": false diff --git a/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs b/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs index 59d36853a3..221ae1431f 100644 --- a/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs +++ b/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs @@ -1,8 +1,8 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Unity.Netcode.Components")] #if UNITY_EDITOR -[assembly: InternalsVisibleTo("Unity.Netcode.Editor")] -[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor.CodeGen")] #endif // UNITY_EDITOR #if COM_UNITY_NETCODE_ADAPTER_UTP @@ -14,14 +14,14 @@ [assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")] [assembly: InternalsVisibleTo("TestProject.Runtime.Tests")] #if UNITY_EDITOR -[assembly: InternalsVisibleTo("Unity.Netcode.Editor.Tests")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor.Tests")] [assembly: InternalsVisibleTo("TestProject.Editor.Tests")] #endif // UNITY_EDITOR #if MULTIPLAYER_TOOLS [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.GameObjects.Tests")] [assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")] -[assembly: InternalsVisibleTo("TestProject.Netcode.GameObjejct.Runtime.Tests")] +[assembly: InternalsVisibleTo("TestProject.Netcode.GameObject.Runtime.Tests")] #endif // MULTIPLAYER_TOOLS #endif // UNITY_INCLUDE_TESTS // Should always be visible when multiplayer tools package is installed. diff --git a/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs index ce277533c7..00bd431cbb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/AnticipatedNetworkTransform.cs @@ -161,8 +161,18 @@ public void AnticipateMove(Vector3 newPosition) { return; } - transform.position = newPosition; + + if (InLocalSpace) + { + transform.localPosition = newPosition; + } + else + { + transform.position = newPosition; + } + m_AnticipatedTransform.Position = newPosition; + if (CanCommitToTransform) { m_AuthoritativeTransform.Position = newPosition; @@ -187,7 +197,15 @@ public void AnticipateRotate(Quaternion newRotation) { return; } - transform.rotation = newRotation; + + if (InLocalSpace) + { + transform.localRotation = newRotation; + } + else + { + transform.rotation = newRotation; + } m_AnticipatedTransform.Rotation = newRotation; if (CanCommitToTransform) { @@ -240,7 +258,14 @@ public void AnticipateState(TransformState newState) return; } var transform_ = transform; - transform_.SetPositionAndRotation(newState.Position, newState.Rotation); + if (InLocalSpace) + { + transform_.SetLocalPositionAndRotation(newState.Position, newState.Rotation); + } + else + { + transform_.SetPositionAndRotation(newState.Position, newState.Rotation); + } transform_.localScale = newState.Scale; m_AnticipatedTransform = newState; if (CanCommitToTransform) @@ -277,7 +302,17 @@ private void ProcessSmoothing() m_PreviousAnticipatedTransform = m_AnticipatedTransform; if (!CanCommitToTransform) { - transform_.SetPositionAndRotation(m_AnticipatedTransform.Position, m_AnticipatedTransform.Rotation); + if (InLocalSpace) + { + transform_.SetLocalPositionAndRotation(m_AnticipatedTransform.Position, + m_AnticipatedTransform.Rotation); + } + else + { + transform_.SetPositionAndRotation(m_AnticipatedTransform.Position, + m_AnticipatedTransform.Rotation); + } + transform_.localScale = m_AnticipatedTransform.Scale; } } @@ -320,12 +355,25 @@ public void SetupForRender() if (Transform.CanCommitToTransform) { var transform_ = Transform.transform; - Transform.m_AuthoritativeTransform = new TransformState + if (Transform.InLocalSpace) { - Position = transform_.position, - Rotation = transform_.rotation, - Scale = transform_.localScale - }; + Transform.m_AuthoritativeTransform = new TransformState + { + Position = transform_.localPosition, + Rotation = transform_.localRotation, + Scale = transform_.localScale + }; + } + else + { + Transform.m_AuthoritativeTransform = new TransformState + { + Position = transform_.position, + Rotation = transform_.rotation, + Scale = transform_.localScale + }; + } + if (Transform.m_CurrentSmoothTime >= Transform.m_SmoothDuration) { // If we've had a call to Smooth() we'll continue interpolating. @@ -334,7 +382,17 @@ public void SetupForRender() Transform.m_AnticipatedTransform = Transform.m_AuthoritativeTransform; } - transform_.SetPositionAndRotation(Transform.m_AnticipatedTransform.Position, Transform.m_AnticipatedTransform.Rotation); + if (Transform.InLocalSpace) + { + transform_.SetLocalPositionAndRotation(Transform.m_AnticipatedTransform.Position, + Transform.m_AnticipatedTransform.Rotation); + } + else + { + transform_.SetPositionAndRotation(Transform.m_AnticipatedTransform.Position, + Transform.m_AnticipatedTransform.Rotation); + } + transform_.localScale = Transform.m_AnticipatedTransform.Scale; } } @@ -344,7 +402,17 @@ public void SetupForUpdate() if (Transform.CanCommitToTransform) { var transform_ = Transform.transform; - transform_.SetPositionAndRotation(Transform.m_AuthoritativeTransform.Position, Transform.m_AuthoritativeTransform.Rotation); + if (Transform.InLocalSpace) + { + transform_.SetLocalPositionAndRotation(Transform.m_AuthoritativeTransform.Position, + Transform.m_AuthoritativeTransform.Rotation); + } + else + { + transform_.SetPositionAndRotation(Transform.m_AuthoritativeTransform.Position, + Transform.m_AuthoritativeTransform.Rotation); + } + transform_.localScale = Transform.m_AuthoritativeTransform.Scale; } } @@ -367,12 +435,25 @@ public void ResetAnticipation() private void ResetAnticipatedState() { var transform_ = transform; - m_AuthoritativeTransform = new TransformState + if (InLocalSpace) + { + m_AuthoritativeTransform = new TransformState + { + Position = transform_.localPosition, + Rotation = transform_.localRotation, + Scale = transform_.localScale + }; + } + else { - Position = transform_.position, - Rotation = transform_.rotation, - Scale = transform_.localScale - }; + m_AuthoritativeTransform = new TransformState + { + Position = transform_.position, + Rotation = transform_.rotation, + Scale = transform_.localScale + }; + } + m_AnticipatedTransform = m_AuthoritativeTransform; m_PreviousAnticipatedTransform = m_AnticipatedTransform; @@ -439,7 +520,7 @@ public override void OnNetworkSpawn() return; } m_OutstandingAuthorityChange = true; - ApplyAuthoritativeState(); + //ApplyAuthoritativeState(); ResetAnticipatedState(); m_AnticipatedObject = new AnticipatedObject { Transform = this }; @@ -491,7 +572,15 @@ public void Smooth(TransformState from, TransformState to, float durationSeconds { m_AnticipatedTransform = to; m_PreviousAnticipatedTransform = m_AnticipatedTransform; - transform_.SetPositionAndRotation(to.Position, to.Rotation); + if (InLocalSpace) + { + transform_.SetLocalPositionAndRotation(to.Position, to.Rotation); + } + else + { + transform_.SetPositionAndRotation(to.Position, to.Rotation); + } + transform_.localScale = to.Scale; m_SmoothDuration = 0; m_CurrentSmoothTime = 0; @@ -502,7 +591,15 @@ public void Smooth(TransformState from, TransformState to, float durationSeconds if (!CanCommitToTransform) { - transform_.SetPositionAndRotation(from.Position, from.Rotation); + if (InLocalSpace) + { + transform_.SetLocalPositionAndRotation(from.Position, from.Rotation); + } + else + { + transform_.SetPositionAndRotation(from.Position, from.Rotation); + } + transform_.localScale = from.Scale; } @@ -543,14 +640,30 @@ protected override void OnTransformUpdated() var previousAnticipatedTransform = m_AnticipatedTransform; // Update authority state to catch any possible interpolation data - m_AuthoritativeTransform.Position = transform_.position; - m_AuthoritativeTransform.Rotation = transform_.rotation; - m_AuthoritativeTransform.Scale = transform_.localScale; + if (InLocalSpace) + { + m_AuthoritativeTransform.Position = transform_.localPosition; + m_AuthoritativeTransform.Rotation = transform_.localRotation; + m_AuthoritativeTransform.Scale = transform_.localScale; + } + else + { + m_AuthoritativeTransform.Position = transform_.position; + m_AuthoritativeTransform.Rotation = transform_.rotation; + m_AuthoritativeTransform.Scale = transform_.localScale; + } if (!m_OutstandingAuthorityChange) { // Keep the anticipated value unchanged, we have no updates from the server at all. - transform_.SetPositionAndRotation(previousAnticipatedTransform.Position, previousAnticipatedTransform.Rotation); + if (InLocalSpace) + { + transform_.SetLocalPositionAndRotation(previousAnticipatedTransform.Position, previousAnticipatedTransform.Rotation); + } + else + { + transform_.SetPositionAndRotation(previousAnticipatedTransform.Position, previousAnticipatedTransform.Rotation); + } transform_.localScale = previousAnticipatedTransform.Scale; return; } @@ -558,7 +671,17 @@ protected override void OnTransformUpdated() if (StaleDataHandling == StaleDataHandling.Ignore && m_LastAnticipaionCounter > m_LastAuthorityUpdateCounter) { // Keep the anticipated value unchanged because it is more recent than the authoritative one. - transform_.SetPositionAndRotation(previousAnticipatedTransform.Position, previousAnticipatedTransform.Rotation); + if (InLocalSpace) + { + transform_.SetLocalPositionAndRotation(previousAnticipatedTransform.Position, + previousAnticipatedTransform.Rotation); + } + else + { + transform_.SetPositionAndRotation(previousAnticipatedTransform.Position, + previousAnticipatedTransform.Rotation); + } + transform_.localScale = previousAnticipatedTransform.Scale; return; } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs new file mode 100644 index 0000000000..69eb729d9a --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs @@ -0,0 +1,87 @@ +#if UNIFIED_NETCODE +using Unity.NetCode; +using UnityEngine; + +namespace Unity.Netcode +{ + /// + /// TODO-UNIFIED: Needs further peer review and exploring alternate ways of handling this. + /// This is a component that is added to the root of all N4E-spawned hybrid prefab instances. It is used to link + /// the N4E-spawned hybrid prefab instances to the incoming + /// specific to the N4E-spawned hybrid prefab instance that has the matching . + /// + + [DefaultExecutionOrder(GhostObject.ExecutionOrder + 1)] + //BREAK --- Fix this on UNIFIED side 1st + public partial class NetworkObjectBridge : GhostBehaviour + { + // DefaultExecutionOrder + // TODO: Define a const for the value used on GhostObject and use that value + // to set the execution order so if it changes on GhostObject it updates here. +#if UNITY_EDITOR + private void OnValidate() + { + hideFlags = HideFlags.HideInInspector; + + var ghostAdapter = GetComponent(); + if (ghostAdapter == null) + { + return; + } + + // Start users with just interpolation (they can adjust this if they want prediction) + // to make the initial transition less problematic for users. + ghostAdapter.SupportedGhostModes = GhostModeMask.Interpolated; + +#if COM_UNITY_MODULES_PHYSICS + var rigidBody = GetComponent(); + var ghostRigidBody = GetComponent(); + if (rigidBody != null) + { + // This must be enabled when replicating the rigid body. + + ghostAdapter.SingleWorldHostInterpolationSmoothing = SingleWorldHostInterpolationMode.Interpolate; + // TODO: Currently, this is added only if you enable replication of the rigid body. + // There is a bug where if you don't add this component it doesn't synchronize the transform. + // Remove this once the issue is resolved. + if (ghostRigidBody == null) + { + gameObject.AddComponent(); + } + } +#endif +#if COM_UNITY_MODULES_PHYSICS2D + // TODO: Fill out a similar script as above but for the 2D version +#endif + } +#endif + + /// + /// This is used to link data to + /// N4E-spawned hybrid prefab instances. + /// + internal GhostField NetworkObjectId = new GhostField(); + + /// + /// Currently, NGO provides the parenting event handling via . + /// Once can provide a form of event notification that + /// the value has changed, we can then invert this flow such that the change in the parent value + /// drives the event. + /// + /// We use NGO scale, delivered via ParentSyncMessage, that is applied to this + /// instance's entity's PostTransformMatrix. + internal void HybridParentUpdate(Vector3 scale) + { + var current = Ghost.GetPositionAndRotation(); + //Debug.Log($"---- Current LT: {current.Position} | {current.Rotation.eulerAngles}"); + //Debug.Log($"---- New LT: {transform.localPosition} | {transform.localRotation}"); + Ghost.ApplyPostTransformMatrixScale(scale); + } + + internal void ApplyScale(Vector3 scale) + { + Ghost.ApplyPostTransformMatrixScale(scale); + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs.meta similarity index 61% rename from com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs.meta index 48faa513f7..0e4dd44b97 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs.meta @@ -1,11 +1,11 @@ fileFormatVersion: 2 -guid: f33b2f298cbb7b248b2a76ba48ee1d53 +guid: 510c5bb08d2f5724e85aa4fb66a8a4ff MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: 04d779b185ebc8d488b5d25eeb21c611, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs new file mode 100644 index 0000000000..8b99dce5aa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -0,0 +1,85 @@ +#if UNIFIED_NETCODE +using System; +using Unity.Entities; +using Unity.NetCode; +using UnityEngine; + +namespace Unity.Netcode +{ + /// + /// TODO-UNIFIED: Needs furthery review and proper error messaging using the new logging context. + /// Handles the bootstrap process for both client and server in unified mode. This is used to create the world and set it on the + /// NetworkManager during initialization. + /// + internal class UnifiedBootstrap : ClientServerBootstrap + { + public static UnifiedBootstrap Instance { get; private set; } + public static Action OnInitialized; + public static ushort Port = 7979; + public static NetworkManager CurrentNetworkManagerForInitialization; + + public static World LastCreatedWorld { get; private set; } + + private static int s_WorldCounter = 0; + + public override bool Initialize(string defaultWorldName) + { + var networkManager = CurrentNetworkManagerForInitialization; + if (networkManager == NetworkManager.Singleton) + { + Instance = this; + } + + AutoConnectPort = Port; + if (base.Initialize(defaultWorldName)) + { + Debug.LogError($"[{nameof(UnifiedBootstrap)}] Auto-bootstrap is enabled!!! This will break the POC!"); + return true; + } + + if (networkManager != null) + { + Debug.Log($"Starting a world for {(networkManager.IsServer ? "Host" : "Client")}"); + s_WorldCounter++; + LastCreatedWorld = networkManager.IsServer ? CreateSingleWorldHost($"HostSingleWorld-{s_WorldCounter}") + : CreateClientWorld($"ClientWorld-{s_WorldCounter}"); + + if (LastCreatedWorld == null) + { + s_WorldCounter--; + Debug.LogError($"[{nameof(UnifiedBootstrap)}] World is null!"); + return false; + } + + if (!LastCreatedWorld.IsCreated) + { + s_WorldCounter--; + Debug.LogError($"[{nameof(UnifiedBootstrap)}] World was not created!"); + return false; + } + + if (networkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"[{nameof(UnifiedBootstrap)}] Created world: {LastCreatedWorld.Name} / {LastCreatedWorld.SequenceNumber}"); + } + + networkManager.NetcodeWorld = (NetcodeWorld)LastCreatedWorld; + } + else + { + LastCreatedWorld = CreateLocalWorld("LocalWorld"); + } + + OnInitialized?.Invoke(); + + return true; + } + + ~UnifiedBootstrap() + { + LastCreatedWorld = null; + Instance = null; + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs.meta new file mode 100644 index 0000000000..a9a3d5ee88 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0671ce785ad022344a6721d1be9559ad \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs new file mode 100644 index 0000000000..e9b66ba877 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs @@ -0,0 +1,129 @@ +#if UNIFIED_NETCODE +using System.Collections.Generic; +using Unity.Collections; +using Unity.Entities; +using Unity.NetCode; +using UnityEngine; + +namespace Unity.Netcode.Components +{ + + public struct NetcodeConnection + { + internal World World; + internal Entity Entity; + public int NetworkId; + + public bool IsServer => World.IsServer(); + public void GoInGame() + { + World.EntityManager.AddComponentData(Entity, default(NetworkStreamInGame)); + } + public void SendMessage(T message) where T : unmanaged, IRpcCommand + { + var req = World.EntityManager.CreateEntity(); + World.EntityManager.AddComponentData(req, new SendRpcCommandRequest { TargetConnection = Entity }); + World.EntityManager.AddComponentData(req, message); + } + } + + internal partial class UnifiedUpdateConnections : SystemBase + { + private List m_TempConnections = new List(); + + private Dictionary m_NewConnections = new Dictionary(); + + protected override void OnUpdate() + { + var isServer = World.IsServer(); + var commandBuffer = new EntityCommandBuffer(Allocator.Temp); + foreach (var networkManager in Object.FindObjectsByType()) + { + foreach (var (networkId, connectionState, entity) in SystemAPI.Query().WithNone().WithEntityAccess()) + { + commandBuffer.RemoveComponent(entity); + m_TempConnections.Add(new NetcodeConnection + { + World = World, + Entity = entity, + NetworkId = networkId.Value + }); + } + + foreach (var con in m_TempConnections) + { + NetworkManager.OnNetCodeDisconnect?.Invoke(con); + } + + m_TempConnections.Clear(); + + // TODO: We should figure out how to associate the N4E NetworkId with the NGO ClientId + foreach (var (networkId, entity) in SystemAPI.Query().WithAll() + .WithNone().WithEntityAccess()) + { + if (!m_NewConnections.ContainsKey(networkId.Value)) + { + var newConnection = new NetcodeConnection + { World = World, Entity = entity, NetworkId = networkId.Value }; + m_NewConnections.Add(networkId.Value, newConnection); + } + } + + // If we have any pending connections + if (m_NewConnections.Count > 0) + { + foreach (var entry in m_NewConnections) + { + // Server: always connect + // Client: wait until we have synchronized before announcing we are ready to receive snapshots + if (networkManager.IsServer || (!networkManager.IsServer && networkManager.IsConnectedClient)) + { + // Set the connection in-game + commandBuffer.AddComponent(entry.Value.Entity); + commandBuffer.AddComponent(entry.Value.Entity, default(ConnectionState)); + NetworkManager.OnNetCodeConnect?.Invoke(entry.Value); + m_TempConnections.Add(entry.Value); + } + } + + // Remove any connections that have "gone in-game". + foreach (var connection in m_TempConnections) + { + m_NewConnections.Remove(connection.NetworkId); + } + } + + m_TempConnections.Clear(); + + // If the local NetworkManager is shutting down or no longer connected, then + // make sure we have disconnected all known connections. + if (networkManager.ShutdownInProgress || !networkManager.IsListening) + { + foreach (var (networkId, entity) in SystemAPI.Query().WithEntityAccess()) + { + commandBuffer.RemoveComponent(entity); + NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection + { World = World, Entity = entity, NetworkId = networkId.Value }); + } + } + } + commandBuffer.Playback(EntityManager); + } + + /// + /// Always disconnect all known connections when being destroyed. + /// + protected override void OnDestroy() + { + var commandBuffer = new EntityCommandBuffer(Allocator.Temp); + foreach (var (networkId, entity) in SystemAPI.Query().WithEntityAccess()) + { + commandBuffer.RemoveComponent(entity); + NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value }); + } + commandBuffer.Playback(EntityManager); + base.OnDestroy(); + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs.meta new file mode 100644 index 0000000000..01feb655c4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d5f2f5fd179c39f43b68ec502cdec9c4 diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Interpolator/BufferedLinearInterpolator.cs b/com.unity.netcode.gameobjects/Runtime/Components/Interpolator/BufferedLinearInterpolator.cs index 24eb580402..8ee288b02f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Interpolator/BufferedLinearInterpolator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Interpolator/BufferedLinearInterpolator.cs @@ -467,8 +467,8 @@ internal T Update(float deltaTime, double tickLatencyAsTime, double minDeltaTime InterpolateState.CurrentValue = InterpolateState.NextValue; } } - else // If the target is reached and we have no more state updates, we want to check to see if we need to reset. - if (m_BufferQueue.Count == 0) + // If the target is reached and we have no more state updates, we want to check to see if we need to reset. + else if (m_BufferQueue.Count == 0) { // When the delta between the time sent and the current tick latency time-window is greater than the max delta time // plus the minimum delta time (a rough estimate of time to wait before we consider rate of change equal to zero), @@ -593,8 +593,8 @@ public T Update(float deltaTime, double renderTime, double serverTime) // Determine if we have reached our target InterpolateState.TargetReached = IsApproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, GetPrecision()); } - else // If the target is reached and we have no more state updates, we want to check to see if we need to reset. - if (InterpolateState.TargetReached && m_BufferQueue.Count == 0) + // If the target is reached and we have no more state updates, we want to check to see if we need to reset. + else if (InterpolateState.TargetReached && m_BufferQueue.Count == 0) { // When the delta between the time sent and the current tick latency time-window is greater than the max delta time // plus the minimum delta time (a rough estimate of time to wait before we consider rate of change equal to zero), diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index 7d82872890..c999c26e9f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -14,8 +14,8 @@ namespace Unity.Netcode.Components { internal class NetworkAnimatorStateChangeHandler : INetworkUpdateSystem { - private NetworkAnimator m_NetworkAnimator; - private bool m_IsServer; + private readonly NetworkAnimator m_NetworkAnimator; + private readonly bool m_IsServer; /// /// This removes sending RPCs from within RPCs when the @@ -132,14 +132,14 @@ private struct AnimationUpdate public NetworkAnimator.AnimationMessage AnimationMessage; } - private List m_SendAnimationUpdates = new List(); + private readonly List m_SendAnimationUpdates = new List(); /// /// Invoked when a server needs to forwarding an update to the animation state /// internal void SendAnimationUpdate(NetworkAnimator.AnimationMessage animationMessage, RpcParams rpcParams = default) { - m_SendAnimationUpdates.Add(new AnimationUpdate() { RpcParams = rpcParams, AnimationMessage = animationMessage }); + m_SendAnimationUpdates.Add(new AnimationUpdate { RpcParams = rpcParams, AnimationMessage = animationMessage }); } private struct ParameterUpdate @@ -148,17 +148,17 @@ private struct ParameterUpdate public NetworkAnimator.ParametersUpdateMessage ParametersUpdateMessage; } - private List m_SendParameterUpdates = new List(); + private readonly List m_SendParameterUpdates = new List(); /// /// Invoked when a server needs to forwarding an update to the parameter state /// internal void SendParameterUpdate(NetworkAnimator.ParametersUpdateMessage parametersUpdateMessage, RpcParams rpcParams = default) { - m_SendParameterUpdates.Add(new ParameterUpdate() { RpcParams = rpcParams, ParametersUpdateMessage = parametersUpdateMessage }); + m_SendParameterUpdates.Add(new ParameterUpdate { RpcParams = rpcParams, ParametersUpdateMessage = parametersUpdateMessage }); } - private List m_ProcessParameterUpdates = new List(); + private readonly List m_ProcessParameterUpdates = new List(); internal void ProcessParameterUpdate(NetworkAnimator.ParametersUpdateMessage parametersUpdateMessage) { m_ProcessParameterUpdates.Add(parametersUpdateMessage); @@ -171,31 +171,31 @@ private struct TriggerUpdate public NetworkAnimator.AnimationTriggerMessage AnimationTriggerMessage; } - private List m_SendTriggerUpdates = new List(); + private readonly List m_SendTriggerUpdates = new List(); /// /// Invoked when a server needs to forward an update to a Trigger state /// internal void QueueTriggerUpdateToClient(NetworkAnimator.AnimationTriggerMessage animationTriggerMessage, RpcParams clientRpcParams = default) { - m_SendTriggerUpdates.Add(new TriggerUpdate() { RpcParams = clientRpcParams, AnimationTriggerMessage = animationTriggerMessage }); + m_SendTriggerUpdates.Add(new TriggerUpdate { RpcParams = clientRpcParams, AnimationTriggerMessage = animationTriggerMessage }); } internal void QueueTriggerUpdateToServer(NetworkAnimator.AnimationTriggerMessage animationTriggerMessage) { - m_SendTriggerUpdates.Add(new TriggerUpdate() { AnimationTriggerMessage = animationTriggerMessage, SendToServer = true }); + m_SendTriggerUpdates.Add(new TriggerUpdate { AnimationTriggerMessage = animationTriggerMessage, SendToServer = true }); } internal void DeregisterUpdate() { - NetworkUpdateLoop.UnregisterNetworkUpdate(this, NetworkUpdateStage.PreUpdate); + this.UnregisterNetworkUpdate(NetworkUpdateStage.PreUpdate); } internal NetworkAnimatorStateChangeHandler(NetworkAnimator networkAnimator) { m_NetworkAnimator = networkAnimator; m_IsServer = networkAnimator.LocalNetworkManager.IsServer; - NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.PreUpdate); + this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate); } } @@ -213,7 +213,7 @@ public class NetworkAnimator : NetworkBehaviour, ISerializationCallbackReceiver #endif [Serializable] - internal class TransitionStateinfo + internal class TransitionStateInfo { public bool IsCrossFadeExit; public int Layer; @@ -260,11 +260,8 @@ public enum AuthorityModes /// public Animator Animator { - get { return m_Animator; } - set - { - m_Animator = value; - } + get => m_Animator; + set => m_Animator = value; } /// @@ -272,11 +269,11 @@ public Animator Animator /// [HideInInspector] [SerializeField] - internal List TransitionStateInfoList; + internal List TransitionStateInfoList; // Used to get the associated transition information required to synchronize late joining clients with transitions // [Layer][DestinationState][TransitionStateInfo] - private Dictionary> m_DestinationStateToTransitioninfo = new Dictionary>(); + private readonly Dictionary> m_DestinationStateToTransitionInfo = new Dictionary>(); // Named differently to avoid serialization conflicts with NetworkBehaviour internal NetworkManager LocalNetworkManager; @@ -284,21 +281,15 @@ public Animator Animator internal bool DistributedAuthorityMode; /// - /// Builds the m_DestinationStateToTransitioninfo lookup table + /// Builds the lookup table /// private void BuildDestinationToTransitionInfoTable() { foreach (var entry in TransitionStateInfoList) { - if (!m_DestinationStateToTransitioninfo.ContainsKey(entry.Layer)) - { - m_DestinationStateToTransitioninfo.Add(entry.Layer, new Dictionary()); - } - var destinationStateTransitionInfo = m_DestinationStateToTransitioninfo[entry.Layer]; - if (!destinationStateTransitionInfo.ContainsKey(entry.DestinationState)) - { - destinationStateTransitionInfo.Add(entry.DestinationState, entry); - } + m_DestinationStateToTransitionInfo.TryAdd(entry.Layer, new Dictionary()); + var destinationStateTransitionInfo = m_DestinationStateToTransitionInfo[entry.Layer]; + destinationStateTransitionInfo.TryAdd(entry.DestinationState, entry); } } @@ -323,14 +314,14 @@ internal class AnimatorParametersListContainer [SerializeField] internal AnimatorParametersListContainer AnimatorParameterEntries; - internal Dictionary AnimatorParameterEntryTable = new Dictionary(); + private readonly Dictionary m_AnimatorParameterEntryTable = new Dictionary(); #if UNITY_EDITOR [HideInInspector] [SerializeField] internal bool AnimatorParametersExpanded; - internal Dictionary ParameterToNameLookup = new Dictionary(); + private readonly Dictionary m_ParameterToNameLookup = new Dictionary(); private void ParseStateMachineStates(int layerIndex, ref AnimatorController animatorController, ref AnimatorStateMachine stateMachine) { @@ -371,7 +362,7 @@ private void ParseStateMachineStates(int layerIndex, ref AnimatorController anim } else if (transition.destinationState != null) { - var transitionInfo = new TransitionStateinfo() + var transitionInfo = new TransitionStateInfo() { Layer = layerIndex, OriginatingState = animatorState.nameHash, @@ -408,7 +399,7 @@ private void BuildTransitionStateInfoList() return; } - TransitionStateInfoList = new List(); + TransitionStateInfoList = new List(); var animControllerType = m_Animator.runtimeAnimatorController.GetType(); var animatorController = (AnimatorController)null; @@ -433,7 +424,7 @@ private void BuildTransitionStateInfoList() } } - internal void ProcessParameterEntries() + private void ProcessParameterEntries() { if (!Animator) { @@ -462,18 +453,18 @@ internal void ProcessParameterEntries() var parameters = animatorController.parameters; var parametersToRemove = new List(); - ParameterToNameLookup.Clear(); + m_ParameterToNameLookup.Clear(); foreach (var parameter in parameters) { - ParameterToNameLookup.Add(parameter.nameHash, parameter); + m_ParameterToNameLookup.Add(parameter.nameHash, parameter); } // Rebuild the parameter entry table for the inspector view - AnimatorParameterEntryTable.Clear(); + m_AnimatorParameterEntryTable.Clear(); foreach (var parameterEntry in AnimatorParameterEntries.ParameterEntries) { // Check for removed parameters. - if (!ParameterToNameLookup.ContainsKey(parameterEntry.NameHash)) + if (!m_ParameterToNameLookup.ContainsKey(parameterEntry.NameHash)) { parametersToRemove.Add(parameterEntry); // Skip this removed entry @@ -481,12 +472,9 @@ internal void ProcessParameterEntries() } // Build the list of known parameters - if (!AnimatorParameterEntryTable.ContainsKey(parameterEntry.NameHash)) - { - AnimatorParameterEntryTable.Add(parameterEntry.NameHash, parameterEntry); - } + m_AnimatorParameterEntryTable.TryAdd(parameterEntry.NameHash, parameterEntry); - var parameter = ParameterToNameLookup[parameterEntry.NameHash]; + var parameter = m_ParameterToNameLookup[parameterEntry.NameHash]; parameterEntry.name = parameter.name; parameterEntry.ParameterType = parameter.type; } @@ -498,9 +486,9 @@ internal void ProcessParameterEntries() } // Update any newly added parameters - foreach (var parameterLookUp in ParameterToNameLookup) + foreach (var parameterLookUp in m_ParameterToNameLookup) { - if (!AnimatorParameterEntryTable.ContainsKey(parameterLookUp.Value.nameHash)) + if (!m_AnimatorParameterEntryTable.ContainsKey(parameterLookUp.Value.nameHash)) { var animatorParameterEntry = new AnimatorParameterEntry() { @@ -510,7 +498,7 @@ internal void ProcessParameterEntries() Synchronize = true, }; AnimatorParameterEntries.ParameterEntries.Add(animatorParameterEntry); - AnimatorParameterEntryTable.Add(parameterLookUp.Value.nameHash, animatorParameterEntry); + m_AnimatorParameterEntryTable.Add(parameterLookUp.Value.nameHash, animatorParameterEntry); } } } @@ -602,7 +590,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade serializer.SerializeValue(ref NormalizedTime); serializer.SerializeValue(ref Weight); - // Cross fading includes the duration of the cross fade. + // Cross-fading includes the duration of the cross-fade. if (CrossFade) { serializer.SerializeValue(ref Duration); @@ -680,7 +668,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade /// Determines whether the is or based on the field. /// Optionally, you can still derive from and override the method. /// - /// or + /// True if this is server authoritative, false otherwise public bool IsServerAuthoritative() { return OnIsServerAuthoritative(); @@ -706,7 +694,6 @@ protected virtual bool OnIsServerAuthoritative() private int[] m_TransitionHash; private int[] m_AnimationHash; private float[] m_LayerWeights; - private static byte[] s_EmptyArray = new byte[] { }; private List m_ParametersToUpdate; private RpcParams m_RpcParams; private IGroupRpcTarget m_TargetGroup; @@ -788,7 +775,7 @@ protected virtual void Awake() foreach (var parameterEntry in AnimatorParameterEntries.ParameterEntries) { - AnimatorParameterEntryTable.TryAdd(parameterEntry.NameHash, parameterEntry); + m_AnimatorParameterEntryTable.TryAdd(parameterEntry.NameHash, parameterEntry); } int layers = m_Animator.layerCount; @@ -814,10 +801,7 @@ protected virtual void Awake() // AnimationMessage. m_AnimationMessage.AnimationStates.Add(new AnimationState()); float layerWeightNow = m_Animator.GetLayerWeight(layer); - if (layerWeightNow != m_LayerWeights[layer]) - { - m_LayerWeights[layer] = layerWeightNow; - } + m_LayerWeights[layer] = layerWeightNow; } // The total initialization size calculated for the m_ParameterWriter write buffer. @@ -835,9 +819,9 @@ protected virtual void Awake() { var parameter = parameters[i]; var synchronizeParameter = true; - if (AnimatorParameterEntryTable.ContainsKey(parameter.nameHash)) + if (m_AnimatorParameterEntryTable.TryGetValue(parameter.nameHash, out var entry)) { - synchronizeParameter = AnimatorParameterEntryTable[parameter.nameHash].Synchronize; + synchronizeParameter = entry.Synchronize; } var cacheParam = new AnimatorParamCache @@ -863,8 +847,6 @@ protected virtual void Awake() var valueBool = m_Animator.GetBool(cacheParam.Hash); UnsafeUtility.WriteArrayElement(cacheParam.Value, 0, valueBool); break; - default: - break; } } @@ -986,12 +968,12 @@ private void WriteSynchronizationData(ref BufferSerializer serializer) whe var normalizedTime = synchronizationStateInfo.normalizedTime; var isInTransition = m_Animator.IsInTransition(layer); - // Grab one of the available AnimationState entries so we can fill it with the current + // Grab one of the available AnimationState entries, so we can fill it with the current // layer's animation state. var animationState = m_AnimationMessage.AnimationStates[layer]; // Synchronizing transitions with trigger conditions for late joining clients is now - // handled by cross fading between the late joining client's current layer's AnimationState + // handled by cross-fading between the late joining client's current layer's AnimationState // and the transition's destination AnimationState. if (isInTransition) { @@ -1013,16 +995,14 @@ private void WriteSynchronizationData(ref BufferSerializer serializer) whe } stateHash = nextState.fullPathHash; - // Use the destination state to transition info lookup table to see if this is a transition we can - // synchronize using cross fading - if (m_DestinationStateToTransitioninfo.ContainsKey(layer)) + // Check if this transition can be synchronized using cross-fading + if (m_DestinationStateToTransitionInfo.TryGetValue(layer, out var layerTransitions)) { - if (m_DestinationStateToTransitioninfo[layer].ContainsKey(nextState.shortNameHash)) + if (layerTransitions.TryGetValue(nextState.shortNameHash, out var transitionInfo)) { - var destinationInfo = m_DestinationStateToTransitioninfo[layer][nextState.shortNameHash]; - stateHash = destinationInfo.OriginatingState; - // Set the destination state to cross fade to from the originating state - animationState.DestinationStateHash = destinationInfo.DestinationState; + stateHash = transitionInfo.OriginatingState; + // Set the destination state to cross-fade to from the originating state + animationState.DestinationStateHash = transitionInfo.DestinationState; } } } @@ -1070,7 +1050,7 @@ protected override void OnSynchronize(ref BufferSerializer serializer) /// /// Checks for animation state changes in: /// -Layer weights - /// -Cross fades + /// -Cross-fades /// -Transitions /// -Layer AnimationStates /// @@ -1104,7 +1084,7 @@ private void CheckForStateChange(int layer) { m_TransitionHash[layer] = nt.fullPathHash; m_AnimationHash[layer] = 0; - // Next state is the destination state for cross fade + // Next state is the destination state for cross-fade animState.DestinationStateHash = nt.fullPathHash; animState.CrossFade = true; animState.Transition = true; @@ -1113,12 +1093,13 @@ private void CheckForStateChange(int layer) stateChangeDetected = true; //Debug.Log($"[Cross-Fade] To-Hash: {nt.fullPathHash} | TI-Duration: ({tt.duration}) | TI-Norm: ({tt.normalizedTime}) | From-Hash: ({m_AnimationHash[layer]}) | SI-FPHash: ({st.fullPathHash}) | SI-Norm: ({st.normalizedTime})"); } - // If we are not transitioned into the "any state" and the animator transition isn't a full path hash (layer to layer) and our pre-built destination state to transition does not contain the - // current layer (i.e. transitioning into a state from another layer) =or= we do contain the layer and the layer contains state to transition to is contained within our pre-built destination - // state then we can handle this transition as a non-cross fade state transition between layers. - // Otherwise, if we don't enter into this then this is a "trigger transition to some state that is now being transitioned back to the Idle state via trigger" or "Dual Triggers" IDLE<-->State. - else if (!tt.anyState && tt.fullPathHash != m_TransitionHash[layer] && (!m_DestinationStateToTransitioninfo.ContainsKey(layer) || - (m_DestinationStateToTransitioninfo.ContainsKey(layer) && m_DestinationStateToTransitioninfo[layer].ContainsKey(nt.fullPathHash)))) + // Handle as a non-cross-fade transition when: + // - not an "any state" transition and this is a new transition on this layer + // - the layer is either absent from the lookup table (cross-layer transition) or its destination state is present + // Skipping this block means we are in a "dual trigger" scenario where a trigger transitions + // to a state that is immediately transitioned back via another trigger (e.g. IDLE <--> State). + else if (!tt.anyState && tt.fullPathHash != m_TransitionHash[layer] && (!m_DestinationStateToTransitionInfo.TryGetValue(layer, out var layerTransitions) || + layerTransitions.ContainsKey(nt.fullPathHash))) { // first time in this transition for this layer m_TransitionHash[layer] = tt.fullPathHash; @@ -1128,7 +1109,7 @@ private void CheckForStateChange(int layer) animState.CrossFade = false; animState.Transition = true; animState.NormalizedTime = tt.normalizedTime; - if (m_DestinationStateToTransitioninfo.ContainsKey(layer) && m_DestinationStateToTransitioninfo[layer].ContainsKey(nt.fullPathHash)) + if (layerTransitions != null) { animState.DestinationStateHash = nt.fullPathHash; } @@ -1189,9 +1170,6 @@ internal void CheckForAnimatorChanges() // This sends updates only if a layer's state has changed for (int layer = 0; layer < m_Animator.layerCount; layer++) { - AnimatorStateInfo st = m_Animator.GetCurrentAnimatorStateInfo(layer); - var totalSpeed = st.speed * st.speedMultiplier; - var adjustedNormalizedMaxTime = totalSpeed > 0.0f ? 1.0f / totalSpeed : 0.0f; CheckForStateChange(layer); } @@ -1202,8 +1180,7 @@ internal void CheckForAnimatorChanges() { SendAnimStateRpc(m_AnimationMessage); } - else - if (!IsServer && IsOwner) + else if (!IsServer && IsOwner) { SendServerAnimStateRpc(m_AnimationMessage); } @@ -1337,8 +1314,8 @@ private unsafe bool CheckParametersChanged() } /// - /// Writes all of the Animator's parameters - /// This uses the m_ParametersToUpdate list to write out only + /// Writes all the Animator's parameters + /// This uses the list to write out only /// the parameters that have changed /// private unsafe void WriteParameters(ref FastBufferWriter writer) @@ -1368,8 +1345,7 @@ private unsafe void WriteParameters(ref FastBufferWriter writer) BytePacker.WriteValuePacked(writer, (uint)valueInt); } } - else // Note: Triggers are treated like boolean values - if (cacheValue.Type == AnimationParamEnumWrapper.AnimatorControllerParameterBool) + else if (cacheValue.Type == AnimationParamEnumWrapper.AnimatorControllerParameterBool) { var valueBool = m_Animator.GetBool(hash); fixed (void* value = cacheValue.Value) @@ -1401,6 +1377,13 @@ private unsafe void ReadParameters(FastBufferReader reader) while (totalParametersRead < totalParametersToRead) { ByteUnpacker.ReadValuePacked(reader, out uint parameterIndex); + + // Do bounds check prior to getting the element as a reference at that index. + if (parameterIndex >= m_CachedAnimatorParameters.Length) + { + NetworkManager.Log.ErrorServer(new Logging.Context(LogLevel.Error, $"[{nameof(NetworkAnimator)}][{name}] Invalid index of {parameterIndex} was received when there are only {m_CachedAnimatorParameters.Length} parameters. Ignoring the remainger of this {nameof(ParametersUpdateMessage)}!")); + return; + } ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef(m_CachedAnimatorParameters.GetUnsafePtr(), (int)parameterIndex); var hash = cacheValue.Hash; if (cacheValue.Type == AnimationParamEnumWrapper.AnimatorControllerParameterInt) @@ -1453,7 +1436,7 @@ internal unsafe void UpdateParameters(ref ParametersUpdateMessage parametersUpda /// /// Applies the AnimationState state to the Animator /// - internal void UpdateAnimationState(AnimationState animationState) + private void UpdateAnimationState(AnimationState animationState) { // Handle updating layer weights first. if (animationState.Layer < m_LayerWeights.Length) @@ -1475,21 +1458,17 @@ internal void UpdateAnimationState(AnimationState animationState) // If it is a transition, then we are synchronizing transitions in progress when a client late joins if (animationState.Transition && !animationState.CrossFade) { - // We should have all valid entries for any animation state transition update - // Verify the AnimationState's assigned Layer exists - if (m_DestinationStateToTransitioninfo.ContainsKey(animationState.Layer)) + // At this point all entries in the lookup table should be valid. + // Look up the transition info for this layer and destination state. + if (m_DestinationStateToTransitionInfo.TryGetValue(animationState.Layer, out var layerTransitions)) { - // Verify the inner-table has the destination AnimationState name hash - if (m_DestinationStateToTransitioninfo[animationState.Layer].ContainsKey(animationState.DestinationStateHash)) + if (layerTransitions.TryGetValue(animationState.DestinationStateHash, out var transitionInfo)) { - // Make sure we are on the originating/starting state we are going to cross fade into + // Make sure we are on the originating/starting state we are going to cross-fade into if (currentState.shortNameHash == animationState.StateHash) { - // Get the transition state information - var transitionStateInfo = m_DestinationStateToTransitioninfo[animationState.Layer][animationState.DestinationStateHash]; - - // Cross fade from the current to the destination state for the transitions duration while starting at the server's current normalized time of the transition - m_Animator.CrossFade(transitionStateInfo.DestinationState, transitionStateInfo.TransitionDuration, transitionStateInfo.Layer, 0.0f, animationState.NormalizedTime); + // Cross-fade from the current to the destination state for the transitions duration while starting at the server's current normalized time of the transition + m_Animator.CrossFade(transitionInfo.DestinationState, transitionInfo.TransitionDuration, transitionInfo.Layer, 0.0f, animationState.NormalizedTime); } else if (LocalNetworkManager.LogLevel == LogLevel.Developer) { @@ -1526,7 +1505,7 @@ internal void UpdateAnimationState(AnimationState animationState) /// The server sets its local parameters and then forwards the message to the remaining clients /// [Rpc(SendTo.Server, AllowTargetOverride = true, InvokePermission = RpcInvokePermission.Owner)] - private unsafe void SendServerParametersUpdateRpc(ParametersUpdateMessage parametersUpdate, RpcParams rpcParams = default) + private void SendServerParametersUpdateRpc(ParametersUpdateMessage parametersUpdate, RpcParams rpcParams = default) { if (IsServerAuthoritative()) { @@ -1708,9 +1687,6 @@ internal void SendServerAnimTriggerRpc(AnimationTriggerMessage animationTriggerM } } - /// - /// See above - /// private void InternalSetTrigger(int hash, bool isSet = true) { m_Animator.SetBool(hash, isSet); @@ -1721,6 +1697,7 @@ private void InternalSetTrigger(int hash, bool isSet = true) /// to forward a trigger to a client /// /// the payload containing the trigger data to apply + /// Defined as it's used to send the RPC to be invoked on this client [Rpc(SendTo.NotAuthority, AllowTargetOverride = true, InvokePermission = RpcInvokePermission.Owner)] internal void SendAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage, RpcParams rpcParams = default) { @@ -1732,7 +1709,7 @@ internal void SendAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage /// a trigger to a client /// /// the payload containing the trigger data to apply - /// unused + /// used to send the RPC to be invoked on this client [Rpc(SendTo.NotServer, AllowTargetOverride = true)] internal void SendClientAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage, RpcParams rpcParams = default) { @@ -1775,13 +1752,11 @@ public void SetTrigger(int hash, bool setTrigger = true) { if (IsServer) { - /// as to why we queue m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animTriggerMessage); InternalSetTrigger(hash, setTrigger); } else { - /// as to why we queue m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToServer(animTriggerMessage); if (!IsServerAuthoritative()) { diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index 0d0de9afa8..bb6c6b54d8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using UnityEngine; + namespace Unity.Netcode.Components { /// @@ -20,6 +21,13 @@ public abstract class NetworkRigidbodyBase : NetworkBehaviour internal bool NetworkRigidbodyBaseExpanded; #endif + // TODO-UNIFIED: + // Provide an option to automatically remove the NetworkRigidbodyBase component at runtime if it is a hybrid prefab that is spawned since this + // component is primarily used for the Rigidbody interpolation and extrapolation features of NetworkTransform which are not relevant for a hybrid + // prefab that is spawned since it will be using N4E's built in interpolation and extrapolation features. This greatly improves performance on + // the client side. If using N4E prediction or distributed authority mode, then Rigibody and any component derived from this should always be used. + + /// /// When enabled, the associated will use the Rigidbody/Rigidbody2D to apply and synchronize changes in position, rotation, and /// allows for the use of Rigidbody interpolation/extrapolation. @@ -51,7 +59,6 @@ public abstract class NetworkRigidbodyBase : NetworkBehaviour private bool m_IsRigidbody2D = false; #endif - private NetworkManager m_LocalNetworkManager; // Used to cache the authority state of this Rigidbody during the last frame private bool m_IsAuthority; @@ -194,6 +201,11 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network #endif #if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D +#if UNIFIED_NETCODE + // Used to keep track of the original kinematic state upon awake. + // (see OnDestroy below) + private bool m_OriginalKinematicState; +#endif /// /// Initializes the networked Rigidbody based on the /// passed in as a parameter. @@ -247,9 +259,31 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network if (AutoUpdateKinematicState) { +#if UNIFIED_NETCODE + // Keep track of the original kinematic state. (see OnDestroy) + m_OriginalKinematicState = IsKinematic(); +#endif SetIsKinematic(true); } } + +#if UNIFIED_NETCODE + public override void OnDestroy() + { + base.OnDestroy(); + // If the user has left this component on their prefab and this is a hybrid prefab, + // then we want to set the rigid body back to its original kinematic settings since + // we are automatically destroying these components at runtime when it is a hybrid + // prefab that is spawned. + if (NetworkObject && NetworkObject.HasGhost) + { + if (m_InternalRigidbody || m_InternalRigidbody2D) + { + SetIsKinematic(m_OriginalKinematicState); + } + } + } +#endif #endif internal Vector3 GetAdjustedPositionThreshold() { @@ -571,7 +605,7 @@ private void InternalMoveRotation2D(Quaternion rotation) { var quaternion = Quaternion.identity; var angles = quaternion.eulerAngles; - angles.z = m_InternalRigidbody2D.rotation; + angles.z = rotation.z; quaternion.eulerAngles = angles; m_InternalRigidbody2D.MoveRotation(quaternion); } @@ -845,6 +879,28 @@ public void SetIsKinematic(bool isKinematic) PostSetIsKinematic(); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool HasInterpolationTypeSet(InterpolationTypes interpolationType) + { +#if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D + if (m_IsRigidbody2D) + { + return interpolationType == InterpolationTypes.Extrapolate ? m_InternalRigidbody2D.interpolation == RigidbodyInterpolation2D.Extrapolate : m_InternalRigidbody2D.interpolation == RigidbodyInterpolation2D.Interpolate; + } + else + { + return interpolationType == InterpolationTypes.Extrapolate ? m_InternalRigidbody.interpolation == RigidbodyInterpolation.Extrapolate : m_InternalRigidbody.interpolation == RigidbodyInterpolation.Interpolate; + } +#endif +#if COM_UNITY_MODULES_PHYSICS && !COM_UNITY_MODULES_PHYSICS2D + return interpolationType == InterpolationTypes.Extrapolate ? m_InternalRigidbody.interpolation == RigidbodyInterpolation.Extrapolate : m_InternalRigidbody.interpolation == RigidbodyInterpolation.Interpolate; +#endif +#if !COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D + return interpolationType == InterpolationTypes.Extrapolate ? m_InternalRigidbody2D.interpolation == RigidbodyInterpolation2D.Extrapolate : m_InternalRigidbody2D.interpolation == RigidbodyInterpolation2D.Interpolate; +#endif + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void PostSetIsKinematic() { @@ -853,26 +909,29 @@ private void PostSetIsKinematic() { return; } + if (UseRigidBodyForMotion) { - // Only if the NetworkTransform is set to interpolate do we need to check for extrapolation - if (NetworkTransform.Interpolate && m_OriginalInterpolation == InterpolationTypes.Extrapolate) + // Exit early if the original interpolation type is not set to extrapolate or NetworkTransform interpolate is disabled + if (m_OriginalInterpolation != InterpolationTypes.Extrapolate || !NetworkTransform.Interpolate) { - if (IsKinematic()) - { - // If not already set to interpolate then set the Rigidbody to interpolate - if (m_InternalRigidbody.interpolation == RigidbodyInterpolation.Extrapolate) - { - // Sleep until the next fixed update when switching from extrapolation to interpolation - SleepRigidbody(); - SetInterpolation(InterpolationTypes.Interpolate); - } - } - else - { - // Switch it back to the original interpolation if non-kinematic (doesn't require sleep). - SetInterpolation(m_OriginalInterpolation); - } + return; + } + + // Otherwise, if this is the active physics object + if (!IsKinematic()) + { + // switch it back to the original interpolation and exit early + SetInterpolation(m_OriginalInterpolation); + return; + } + + // If the Rigidbody or Rigidbody2D is currently configured to extrapolate + if (HasInterpolationTypeSet(InterpolationTypes.Extrapolate)) + { + // sleep until the next fixed update when switching from extrapolation to interpolation + SleepRigidbody(); + SetInterpolation(InterpolationTypes.Interpolate); } } else diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody.cs index 590fad77b7..ffd82ff5f4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody.cs @@ -9,7 +9,10 @@ namespace Unity.Netcode.Components /// mode of the and disabling it on all peers but the authoritative one. /// [RequireComponent(typeof(NetworkTransform))] + // TODO-UNIFIED: We should not require this for unified and come up with a different way of handling the dependency +#if !UNIFIED_NETCODE [RequireComponent(typeof(Rigidbody))] +#endif [AddComponentMenu("Netcode/Network Rigidbody")] [HelpURL(HelpUrls.NetworkRigidbody)] public class NetworkRigidbody : NetworkRigidbodyBase diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody2D.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody2D.cs index dd84d52252..8912a2cff0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody2D.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidbody2D.cs @@ -9,7 +9,10 @@ namespace Unity.Netcode.Components /// mode of the rigidbody and disabling it on all peers but the authoritative one. /// [RequireComponent(typeof(NetworkTransform))] + // TODO-UNIFIED: We should not require this for unified and come up with a different way of handling the dependency +#if !UNIFIED_NETCODE [RequireComponent(typeof(Rigidbody2D))] +#endif [AddComponentMenu("Netcode/Network Rigidbody 2D")] [HelpURL(HelpUrls.NetworkRigidbody2D)] public class NetworkRigidbody2D : NetworkRigidbodyBase diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 3ca9093b34..de4e86999d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -24,6 +24,18 @@ public class NetworkTransform : NetworkBehaviour [HideInInspector] [SerializeField] internal bool NetworkTransformExpanded; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + CurrentTick = 0; + TrackStateUpdateId = false; + AssignDefaultInterpolationType = false; + DefaultInterpolationType = default; + s_NetworkTickRegistration = new Dictionary(); + InterpolationBufferTickOffset = 0; + s_TickSynchPosition = 0; + } #endif internal enum Axis { X, Y, Z } @@ -1361,7 +1373,7 @@ public enum AuthorityModes /// /// When set each state update will contain a state identifier /// - internal static bool TrackStateUpdateId = false; + internal static bool TrackStateUpdateId; /// /// Enabled by default. @@ -1793,6 +1805,17 @@ internal void RegisterRigidbody(NetworkRigidbodyBase networkRigidbody) m_UseRigidbodyForMotion = m_NetworkRigidbodyInternal.UseRigidBodyForMotion; } } + +#if UNIFIED_NETCODE + internal void UnregisterRigidbody() + { + if (m_NetworkRigidbodyInternal) + { + m_NetworkRigidbodyInternal = null; + m_UseRigidbodyForMotion = false; + } + } +#endif #endif #if DEBUG_NETWORKTRANSFORM || UNITY_INCLUDE_TESTS @@ -2062,19 +2085,6 @@ private void TryCommitTransform(bool synchronize = false, bool settingState = fa childNetworkTransform.OnNetworkTick(true); } } - - // Synchronize any parented children with the parent's motion - foreach (var child in m_ParentedChildren) - { - // Synchronize any nested NetworkTransforms of the child with the parent's - foreach (var childNetworkTransform in child.NetworkTransforms) - { - if (childNetworkTransform.CanCommitToTransform) - { - childNetworkTransform.OnNetworkTick(true); - } - } - } } } } @@ -2221,17 +2231,15 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, bool is // values are applied. var hasParentNetworkObject = false; - var parentNetworkObject = (NetworkObject)null; - // If the NetworkObject belonging to this NetworkTransform instance has a parent // (i.e. this handles nested NetworkTransforms under a parent at some layer above) if (NetworkObject.transform.parent != null) { - parentNetworkObject = NetworkObject.transform.parent.GetComponent(); + var parentNetworkObject = NetworkObject.transform.parent.GetComponent(); // In-scene placed NetworkObjects parented under a GameObject with no // NetworkObject preserve their lossyScale when synchronizing. - if (parentNetworkObject == null && NetworkObject.IsSceneObject != false) + if (parentNetworkObject == null && NetworkObject.InScenePlaced) { hasParentNetworkObject = true; } @@ -2518,8 +2526,8 @@ private bool CheckForStateChange(ref NetworkTransformState networkState, bool is } } } - else // Just apply the full local scale when synchronizing - if (SynchronizeScale) + // Just apply the full local scale when synchronizing + else if (SynchronizeScale) { var localScale = CachedTransform.localScale; if (!UseHalfFloatPrecision) @@ -3363,19 +3371,6 @@ private void OnNetworkStateChanged(NetworkTransformState oldState, NetworkTransf childNetworkTransform.OnNetworkTick(true); } } - - // Synchronize any parented children with the parent's motion - foreach (var child in m_ParentedChildren) - { - // Synchronize any nested NetworkTransforms of the child with the parent's - foreach (var childNetworkTransform in child.NetworkTransforms) - { - if (childNetworkTransform.CanCommitToTransform) - { - childNetworkTransform.OnNetworkTick(true); - } - } - } } // Provide notifications when the state has been updated @@ -3504,8 +3499,8 @@ private void NonAuthorityFinalizeSynchronization() child.InternalInitialization(); } } - else // Otherwise, just run through standard synchronization of this instance - if (!CanCommitToTransform) + // Otherwise, just run through standard synchronization of this instance + else if (!CanCommitToTransform) { ApplySynchronization(); InternalInitialization(); @@ -3634,8 +3629,15 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag /// public override void OnNetworkSpawn() { - m_ParentedChildren.Clear(); - +#if UNIFIED_NETCODE + // TODO-UNIFIED: + // Provide a notification to users that NetworkTransform component will be removed at runtime if it is a hybrid prefab that is spawned since + // it will be using N4E's built in interpolation and extrapolation features. + if (NetworkObject.HasGhost) + { + return; + } +#endif Initialize(); if (CanCommitToTransform && !SwitchTransformSpaceWhenParented) @@ -3647,7 +3649,6 @@ public override void OnNetworkSpawn() private void CleanUpOnDestroyOrDespawn() { - m_ParentedChildren.Clear(); #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D var forUpdate = !m_UseRigidbodyForMotion; #else @@ -3726,8 +3727,17 @@ private void ResetInterpolatedStateToCurrentAuthoritativeState() /// The internal initialization method to allow for internal API adjustments /// /// - private void InternalInitialization(bool isOwnershipChange = false) + internal virtual void InternalInitialization(bool isOwnershipChange = false) { +#if UNIFIED_NETCODE + // TODO-UNIFIED: + // Provide a notification to users that NetworkTransform component will be removed at runtime if it is a hybrid prefab that is spawned since + // it will be using N4E's built in interpolation and extrapolation features. + if (NetworkObject.HasGhost) + { + return; + } +#endif if (!IsSpawned) { return; @@ -3861,7 +3871,6 @@ protected override void OnOwnershipChanged(ulong previous, ulong current) } internal bool IsNested; - private List m_ParentedChildren = new List(); private bool m_IsFirstNetworkTransform; @@ -4694,7 +4703,7 @@ internal static void UpdateNetworkTick(NetworkManager networkManager) /// The default value is 1 tick (plus the tick latency). When running on a local network, reducing this to 0 is recommended.
/// /// - public static int InterpolationBufferTickOffset = 0; + public static int InterpolationBufferTickOffset; internal static float GetTickLatency(NetworkManager networkManager) { if (networkManager.IsListening) @@ -4799,6 +4808,7 @@ public NetworkTransformTickRegistration(NetworkManager networkManager) } } } + private static int s_TickSynchPosition; private int m_NextTickSync; diff --git a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs index f4d9b25dd2..3c338a2a84 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/QuaternionCompressor.cs @@ -4,7 +4,7 @@ namespace Unity.Netcode { /// - /// A Smallest Three Quaternion Compressor Implementation + /// The Smallest Three Quaternion Compressor Implementation /// /// /// Explanation of why "The smallest three": @@ -26,14 +26,14 @@ public static class QuaternionCompressor // We can further improve the encoding compression by dividing k_SqrtTwoOverTwo into 1.0f and multiplying that // by the precision mask (minor reduction of runtime calculations) - private const float k_CompressionEcodingMask = (1.0f / k_SqrtTwoOverTwoEncoding) * k_PrecisionMask; + private const float k_CompressionEncodingMask = (1.0f / k_SqrtTwoOverTwoEncoding) * k_PrecisionMask; // Used to shift the negative bit to the 10th bit position when compressing and encoding private const ushort k_ShiftNegativeBit = 9; // We can do the same for our decoding and decompression by dividing k_PrecisionMask into 1.0 and multiplying // that by k_SqrtTwoOverTwo (minor reduction of runtime calculations) - private const float k_DcompressionDecodingMask = (1.0f / k_PrecisionMask) * k_SqrtTwoOverTwoEncoding; + private const float k_DecompressionDecodingMask = (1.0f / k_PrecisionMask) * k_SqrtTwoOverTwoEncoding; // The sign bit position (10th bit) used when decompressing and decoding private const ushort k_NegShortBit = 0x200; @@ -42,9 +42,6 @@ public static class QuaternionCompressor private const ushort k_True = 1; private const ushort k_False = 0; - // Used to store the absolute value of the 4 quaternion elements - private static Quaternion s_QuatAbsValues = Quaternion.identity; - /// /// Compresses a Quaternion into an unsigned integer /// @@ -54,38 +51,31 @@ public static class QuaternionCompressor public static uint CompressQuaternion(ref Quaternion quaternion) { // Store off the absolute value for each Quaternion element - s_QuatAbsValues[0] = Mathf.Abs(quaternion[0]); - s_QuatAbsValues[1] = Mathf.Abs(quaternion[1]); - s_QuatAbsValues[2] = Mathf.Abs(quaternion[2]); - s_QuatAbsValues[3] = Mathf.Abs(quaternion[3]); + var quatAbsValue0 = Mathf.Abs(quaternion[0]); + var quatAbsValue1 = Mathf.Abs(quaternion[1]); + var quatAbsValue2 = Mathf.Abs(quaternion[2]); + var quatAbsValue3 = Mathf.Abs(quaternion[3]); // Get the largest element value of the quaternion to know what the remaining "Smallest Three" values are - var quatMax = Mathf.Max(s_QuatAbsValues[0], s_QuatAbsValues[1], s_QuatAbsValues[2], s_QuatAbsValues[3]); + var quatMax = Mathf.Max(quatAbsValue0, quatAbsValue1, quatAbsValue2, quatAbsValue3); - // Find the index of the largest element so we can skip that element while compressing and decompressing - var indexToSkip = (ushort)(s_QuatAbsValues[0] == quatMax ? 0 : s_QuatAbsValues[1] == quatMax ? 1 : s_QuatAbsValues[2] == quatMax ? 2 : 3); + // Find the index of the largest element, so we can skip that element while compressing and decompressing + var indexToSkip = (ushort)(quatAbsValue0 == quatMax ? 0 : quatAbsValue1 == quatMax ? 1 : quatAbsValue2 == quatMax ? 2 : 3); // Get the sign of the largest element which is all that is needed when calculating the sum of squares of a normalized quaternion. - var quatMaxSign = (quaternion[indexToSkip] < 0 ? k_True : k_False); // Start with the index to skip which will be shifted to the highest two bits var compressed = (uint)indexToSkip; - // Step 1: Start with the first element - var currentIndex = 0; - - // Step 2: If we are on the index to skip preserve the current compressed value, otherwise proceed to step 3 and 4 - // Step 3: Get the sign of the element we are processing. If it is the not the same as the largest value's sign bit then we set the bit - // Step 4: Get the compressed and encoded value by multiplying the absolute value of the current element by k_CompressionEcodingMask and round that result up - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; - currentIndex++; - // Repeat the last 3 steps for the remaining elements - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; - currentIndex++; - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; - currentIndex++; - compressed = currentIndex != indexToSkip ? (compressed << 10) | (uint)((quaternion[currentIndex] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEcodingMask * s_QuatAbsValues[currentIndex]) : compressed; + // Step 1: If we are on the index to skip, preserve the current compressed value, otherwise proceed to step 2 and 3 + // Step 2: Get the sign of the element we are processing. If it is not the same as the largest value's sign bit then we set the bit + // Step 3: Get the compressed and encoded value by multiplying the absolute value of the current element by k_CompressionEncodingMask and round that result up + compressed = 0 != indexToSkip ? (compressed << 10) | (uint)((quaternion[0] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEncodingMask * quatAbsValue0) : compressed; + // Repeat the 3 steps for the remaining elements + compressed = 1 != indexToSkip ? (compressed << 10) | (uint)((quaternion[1] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEncodingMask * quatAbsValue1) : compressed; + compressed = 2 != indexToSkip ? (compressed << 10) | (uint)((quaternion[2] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEncodingMask * quatAbsValue2) : compressed; + compressed = 3 != indexToSkip ? (compressed << 10) | (uint)((quaternion[3] < 0 ? k_True : k_False) != quatMaxSign ? k_True : k_False) << k_ShiftNegativeBit | (ushort)Mathf.Round(k_CompressionEncodingMask * quatAbsValue3) : compressed; // Return the compress quaternion return compressed; @@ -111,7 +101,7 @@ public static void DecompressQuaternion(ref Quaternion quaternion, uint compress continue; } // Check the negative bit and multiply that result with the decompressed and decoded value - quaternion[i] = ((compressed & k_NegShortBit) > 0 ? -1.0f : 1.0f) * ((compressed & k_PrecisionMask) * k_DcompressionDecodingMask); + quaternion[i] = ((compressed & k_NegShortBit) > 0 ? -1.0f : 1.0f) * ((compressed & k_PrecisionMask) * k_DecompressionDecodingMask); sumOfSquaredMagnitudes += quaternion[i] * quaternion[i]; compressed = compressed >> 10; } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs index bf54c4d964..3c94eae424 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/RigidbodyContactEventManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Jobs; +using Unity.Netcode.Logging; using Unity.Netcode.Runtime; using UnityEngine; @@ -20,7 +21,7 @@ public struct ContactEventHandlerInfo ///
public bool ProvideNonRigidBodyContactEvents; /// - /// When set to true, the will prioritize invoking

+ /// When set to true, the will prioritize invoking
/// if it is the 2nd colliding body in the contact pair being processed. With distributed authority, setting this value to true when a is owned by the local client
/// will assure is only invoked on the authoritative side. ///
@@ -105,14 +106,16 @@ private struct JobResultStruct private readonly Dictionary m_HandlerInfo = new Dictionary(); #endif + private ContextualLogger m_Log; private void OnEnable() { + m_Log = new ContextualLogger(this); m_ResultsArray = new NativeArray(16, Allocator.Persistent); Physics.ContactEvent += Physics_ContactEvent; - if (Instance != null) + if (Instance != null && Instance != this) { - NetworkLog.LogError($"[Invalid][Multiple Instances] Found more than one instance of {nameof(RigidbodyContactEventManager)}: {name} and {Instance.name}"); - NetworkLog.LogError($"[Disable][Additional Instance] Disabling {name} instance!"); + m_Log.Error(new Context(LogLevel.Error, $"Found more than one instance of {nameof(RigidbodyContactEventManager)}").AddTag("Invalid").AddTag("Multiple Instances").AddInfo("Instance 1", Instance.name).AddInfo("Instance 2", name)); + m_Log.Error(new Context(LogLevel.Error, $"Disabling instance: ").AddTag("Disable").AddTag("Additional Instance").AddInfo("Instance", name)); gameObject.SetActive(false); return; } diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs index c6f9b4a222..67878b125a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/CommandLineOptions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using UnityEngine; namespace Unity.Netcode { @@ -13,6 +12,7 @@ public class CommandLineOptions /// /// Command-line options singleton /// + [Obsolete("Not used anymore replaced by TryGetArg")] public static CommandLineOptions Instance { get @@ -30,34 +30,41 @@ private set } private static CommandLineOptions s_Instance; - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void RuntimeInitializeOnLoad() => Instance = new CommandLineOptions(); - // Contains the current application instance domain's command line arguments - internal static List CommandLineArguments = new List(); - - // Invoked upon application start, after scene load - [RuntimeInitializeOnLoadMethod] - private static void ParseCommandLineArguments() - { - // Get all the command line arguments to be parsed later and/or modified - // prior to being parsed (for testing purposes). - CommandLineArguments = new List(Environment.GetCommandLineArgs()); - } + private static readonly List k_CommandLineArguments = new List(Environment.GetCommandLineArgs()); /// - /// Returns the value of an argument or null if there the argument is not present + /// Returns the value of an argument or null if the argument is not present /// /// The name of the argument /// Value of the command line argument passed in. + [Obsolete("Not used anymore replaced by TryGetArg")] public string GetArg(string arg) { - var argIndex = CommandLineArguments.IndexOf(arg); - if (argIndex >= 0 && argIndex < CommandLineArguments.Count - 1) + var argIndex = k_CommandLineArguments.IndexOf(arg); + if (argIndex >= 0 && argIndex < k_CommandLineArguments.Count - 1) { - return CommandLineArguments[argIndex + 1]; + return k_CommandLineArguments[argIndex + 1]; } return null; } + + /// + /// Returns true if the argument was found. + /// + /// The name of the argument to look up. + /// The argument's value, or if not found. + /// true if the argument was found; otherwise false. + public static bool TryGetArg(string arg, out string argValue) + { + var argIndex = k_CommandLineArguments.IndexOf(arg); + if (argIndex >= 0 && argIndex < k_CommandLineArguments.Count - 1) + { + argValue = k_CommandLineArguments[argIndex + 1]; + return true; + } + argValue = null; + return false; + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs index c6f30d2835..a873404b3c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs @@ -57,6 +57,14 @@ public class NetworkPrefab ///
public GameObject OverridingTargetPrefab; +#if UNIFIED_NETCODE + /// + /// Used to determine if this prefab needs to be registered + /// via the unified API. + /// + internal bool HasGhost { get; private set; } +#endif + /// /// Compares this NetworkPrefab with another to determine equality /// @@ -166,6 +174,26 @@ public bool Validate(int index = -1) return false; } +#if UNIFIED_NETCODE + // Mark this network prefab as having to be registered via the unified API + HasGhost = networkObject.HasGhost; +#endif + if (networkObject.InScenePlaced) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"{NetworkPrefabHandler.PrefabDebugHelper(this)} InScenePlaced {nameof(NetworkObject)} is being registered as a Prefab. This can cause issues!"); + } + } + + if (networkObject.IsSpawned) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"{NetworkPrefabHandler.PrefabDebugHelper(this)} Currently spawned {nameof(NetworkObject)} is being registered as a Prefab. This can cause issues!"); + } + } + return true; } @@ -183,7 +211,6 @@ public bool Validate(int index = -1) return false; } - break; } case NetworkPrefabOverride.Prefab: diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs index 45d93ad9d7..9fdfffbd38 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs @@ -277,6 +277,11 @@ public bool Contains(NetworkPrefab prefab) return false; } +#if UNIFIED_NETCODE + internal bool HasGhostPrefabs { get; private set; } +#endif + + /// /// Configures for the given /// @@ -295,6 +300,13 @@ private bool AddPrefabRegistration(NetworkPrefab networkPrefab) uint source = networkPrefab.SourcePrefabGlobalObjectIdHash; uint target = networkPrefab.TargetPrefabGlobalObjectIdHash; +#if UNIFIED_NETCODE + if (networkPrefab.HasGhost) + { + HasGhostPrefabs = true; + } +#endif + // Make sure the prefab isn't already registered. if (NetworkPrefabOverrideLinks.ContainsKey(source)) { diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs index eefe269ca4..b584544245 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkClient.cs @@ -52,7 +52,7 @@ public class NetworkClient /// /// The ClientId of the NetworkClient /// - public ulong ClientId; + public ulong ClientId { get; internal set; } /// /// The PlayerObject of the Client diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index aaeb67f4db..3ff131131f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -96,7 +96,7 @@ public struct ConnectionEventData /// public sealed class NetworkConnectionManager { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG private static ProfilerMarker s_TransportPollMarker = new ProfilerMarker($"{nameof(NetworkManager)}.TransportPoll"); private static ProfilerMarker s_TransportConnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportConnect"); private static ProfilerMarker s_HandleIncomingData = new ProfilerMarker($"{nameof(NetworkManager)}.{nameof(NetworkMessageManager.HandleIncomingData)}"); @@ -438,7 +438,7 @@ private ulong GetServerTransportId() internal void PollAndHandleNetworkEvents() { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_TransportPollMarker.Begin(); #endif NetworkEvent networkEvent; @@ -453,7 +453,7 @@ internal void PollAndHandleNetworkEvents() // Only do another iteration if: there are no more messages AND (there is no limit to max events or we have processed less than the maximum) } while (NetworkManager.IsListening && networkEvent != NetworkEvent.Nothing); -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_TransportPollMarker.End(); #endif } @@ -501,7 +501,7 @@ internal void HandleNetworkEvent(NetworkEvent networkEvent, ulong transportClien ///
internal void ConnectEventHandler(ulong transportId) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_TransportConnect.Begin(); #endif // Assumptions: @@ -522,6 +522,9 @@ internal void ConnectEventHandler(ulong transportId) { NetworkLog.LogError($"[TransportApproval][Server] TransportId {transportId} is already connected to this server!"); } +#if DEBUG + s_TransportConnect.End(); +#endif return; } @@ -536,6 +539,9 @@ internal void ConnectEventHandler(ulong transportId) { NetworkLog.LogError("[TransportApproval][Client] Client received a transport connection event after already connecting!"); } +#if DEBUG + s_TransportConnect.End(); +#endif return; } @@ -571,7 +577,7 @@ internal void ConnectEventHandler(ulong transportId) StartClientApprovalCoroutine(clientId); } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_TransportConnect.End(); #endif } @@ -581,7 +587,7 @@ internal void ConnectEventHandler(ulong transportId) ///
internal void DataEventHandler(ulong transportClientId, ref ArraySegment payload, float receiveTime) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_HandleIncomingData.Begin(); #endif var (clientId, isConnectedClient) = TransportIdToClientId(transportClientId); @@ -590,7 +596,7 @@ internal void DataEventHandler(ulong transportClientId, ref ArraySegment p MessageManager.HandleIncomingData(clientId, payload, receiveTime); } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_HandleIncomingData.End(); #endif } @@ -633,7 +639,7 @@ internal void DisconnectEventHandler(ulong transportClientId) return; } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_TransportDisconnect.Begin(); #endif @@ -692,7 +698,7 @@ internal void DisconnectEventHandler(ulong transportClientId) NetworkManager.Shutdown(true); } } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_TransportDisconnect.End(); #endif } @@ -961,8 +967,7 @@ internal void ProcessClientsToDisconnect() { return (true, playerPrefabHash.Value); } - else - if (NetworkManager.NetworkConfig.PlayerPrefab != null) + else if (NetworkManager.NetworkConfig.PlayerPrefab != null) { var networkObject = NetworkManager.NetworkConfig.PlayerPrefab.GetComponent(); if (networkObject != null) @@ -1184,7 +1189,11 @@ internal void CreateAndSpawnPlayer(ulong ownerId) return; } - networkObject.IsSceneObject = false; +#pragma warning disable CS0618 // Type or member is obsolete + // Obsolete with warning means we need the underlying behaviour to keep existing + // TODO: remove in the 3.x branch + networkObject.SetSceneObjectStatus(false); +#pragma warning restore CS0618 // Type or member is obsolete networkObject.NetworkManagerOwner = NetworkManager; networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene); } @@ -1208,7 +1217,7 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) var message = new CreateObjectMessage { - ObjectInfo = ConnectedClients[clientId].PlayerObject.Serialize(clientPair.Key), + ObjectInfo = ConnectedClients[clientId].PlayerObject.SerializeSpawnedObject(clientPair.Key), IncludesSerializedObject = true, }; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs index 52945c4e46..6733429ee2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/ComponentFactory.cs @@ -14,6 +14,10 @@ internal static class ComponentFactory internal delegate object CreateObjectDelegate(NetworkManager networkManager); private static Dictionary s_Delegates = new Dictionary(); +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => s_Delegates = new Dictionary(); +#endif /// /// Instantiates an instance of a given interface diff --git a/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs b/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs index a283cabe52..59329f8a81 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs @@ -1,7 +1,8 @@ -#if NGO_FINDOBJECTS_NOSORTING using System; -#endif +using System.Collections; +using System.Collections.Generic; using System.Runtime.CompilerServices; +using UnityEngine.SceneManagement; using Object = UnityEngine.Object; namespace Unity.Netcode @@ -11,14 +12,14 @@ namespace Unity.Netcode /// /// /// It is intentional that we do not include the UnityEngine namespace in order to avoid - /// over-complicatd define wrapping between versions that do or don't support FindObjectsSortMode. + /// over-complicated define wrapping between versions that do or don't support FindObjectsSortMode. /// internal static class FindObjects { /// /// Replaces to have one place where these changes are applied. /// - /// + /// The type of object to find. Must be a reference type derived from /// When true, inactive objects will be included. /// When true, the array returned will be sorted by identifier. /// Results as an of type T @@ -39,5 +40,90 @@ public static T[] ByType(bool includeInactive = false, bool orderByIdentifier #endif return results; } + + /// + /// Returns an enumerator that enumerates over all the components of a given type in a scene. + /// + /// The scene to use for searching + /// When true, inactive objects will be included. + /// Type of to get from the scene + /// a generator that yields successive NetworkObjects in the current scene + public static IEnumerable FromSceneByType(Scene scene, bool includeInactive) where T : UnityEngine.Component + { + return new ObjectsInSceneEnumerator(scene, includeInactive); + } + + /// + /// An Enumerator that enumerates over each component of type in the given scene. + /// + /// Type of to get from the scene + private struct ObjectsInSceneEnumerator : IEnumerable, IEnumerator where T : UnityEngine.Component + { + private readonly UnityEngine.GameObject[] m_RootObjects; + private int m_RootIndex; + private T[] m_CurrentChildObjects; + private int m_CurrentChildIndex; + + private readonly bool m_IncludeInactive; + + internal ObjectsInSceneEnumerator(Scene scene, bool includeInactive) + { + m_IncludeInactive = includeInactive; + + m_RootObjects = scene.GetRootGameObjects(); + m_RootIndex = 0; + m_CurrentChildObjects = null; + m_CurrentChildIndex = 0; + Current = null; + } + + public void Dispose() { } + + public bool MoveNext() + { + while (m_CurrentChildObjects == null && m_RootIndex < m_RootObjects.Length) + { + m_CurrentChildObjects = m_RootObjects[m_RootIndex].GetComponentsInChildren(m_IncludeInactive); + m_RootIndex++; + + if (m_CurrentChildObjects.Length == 0) + { + m_CurrentChildObjects = null; + } + } + + if (m_CurrentChildObjects != null && m_CurrentChildIndex < m_CurrentChildObjects.Length) + { + Current = m_CurrentChildObjects[m_CurrentChildIndex]; + m_CurrentChildIndex++; + + if (m_CurrentChildIndex >= m_CurrentChildObjects.Length) + { + m_CurrentChildIndex = 0; + m_CurrentChildObjects = null; + } + return true; + } + + Current = null; + return false; + } + + public void Reset() + { + m_RootIndex = 0; + m_CurrentChildObjects = null; + m_CurrentChildIndex = 0; + Current = null; + } + + object IEnumerator.Current => Current; + + public T Current { get; private set; } + + public IEnumerator GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 59f9e637b6..e024765a72 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -1,8 +1,10 @@ #pragma warning disable IDE0005 using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Unity.Collections; +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +using System.Runtime.CompilerServices; +#endif using UnityEngine; #pragma warning restore IDE0005 @@ -43,7 +45,7 @@ public abstract class NetworkBehaviour : MonoBehaviour internal static readonly Dictionary> __rpc_func_table = new Dictionary>(); internal static readonly Dictionary> __rpc_permission_table = new Dictionary>(); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) // RuntimeAccessModifiersILPP will make this `public` internal static readonly Dictionary> __rpc_name_table = new Dictionary>(); #endif @@ -145,7 +147,7 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth bufferWriter.Dispose(); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) TrackRpcMetricsSend(ref serverRpcMessage, rpcMethodId, rpcWriteSize); #endif } @@ -269,7 +271,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth } bufferWriter.Dispose(); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) if (!ValidateRpcMessageMetrics(GetType())) { return; @@ -1002,12 +1004,12 @@ internal void __registerRpc(uint hash, RpcReceiveHandler handler, string rpcMeth var rpcType = GetType(); __rpc_func_table[rpcType][hash] = handler; __rpc_permission_table[rpcType][hash] = permission; -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) __rpc_name_table[rpcType][hash] = rpcMethodName; #endif } -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ValidateRpcMessageMetrics(Type type) { @@ -1125,7 +1127,7 @@ internal void InitializeVariables() { __rpc_func_table[GetType()] = new Dictionary(); __rpc_permission_table[GetType()] = new Dictionary(); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) __rpc_name_table[GetType()] = new Dictionary(); #endif __initializeRpcs(); @@ -1299,8 +1301,6 @@ internal void NetworkVariableUpdate(ulong targetClientId, bool forceSend = false } } - internal static bool LogSentVariableUpdateMessage; - private bool CouldHaveDirtyNetworkVariables() { // TODO: There should be a better way by reading one dirty variable vs. 'n' diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs index 4f12bca033..871c8afb7d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs @@ -27,7 +27,7 @@ public class NetworkBehaviourUpdater ///
private HashSet m_PendingDirtyNetworkObjects = new HashSet(); -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG private ProfilerMarker m_NetworkBehaviourUpdate = new ProfilerMarker($"{nameof(NetworkBehaviour)}.{nameof(NetworkBehaviourUpdate)}"); #endif @@ -190,7 +190,7 @@ internal void ProcessDirtyObject(NetworkObject networkObject, bool forceSend) /// Refer to the definition. internal void NetworkBehaviourUpdate(bool forceSend = false) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG m_NetworkBehaviourUpdate.Begin(); #endif try @@ -214,7 +214,7 @@ internal void NetworkBehaviourUpdate(bool forceSend = false) } finally { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG m_NetworkBehaviourUpdate.End(); #endif } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index b9a2c15789..1b702e76fc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1,10 +1,22 @@ using System; using System.Collections.Generic; -using Unity.Collections; using System.Linq; +using Unity.Collections; +#if UNIFIED_NETCODE +using Unity.Entities; +using Unity.NetCode; +#endif using Unity.Netcode.Components; using Unity.Netcode.Logging; using Unity.Netcode.Runtime; +// TODO-UNIFIED: When: +// - N4E is a dependency of Netcode for GameObjects. +// - TestProject has been updated to include N4E. +// - TestProject and Runtime tests have been updated to use UnifiedHost. +// Remove the conditional compilation and just use the namespace. +#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +using Unity.Netcode.Unified; +#endif using UnityEngine; #if UNITY_EDITOR using UnityEditor; @@ -12,6 +24,8 @@ #endif using UnityEngine.SceneManagement; + + namespace Unity.Netcode { /// @@ -21,6 +35,20 @@ namespace Unity.Netcode [HelpURL(HelpUrls.NetworkManager)] public class NetworkManager : MonoBehaviour, INetworkUpdateSystem { +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + Singleton = null; + OnInstantiated = null; + OnDestroying = null; + OnSingletonReady = null; + OnNetworkManagerReset = null; + IsDistributedAuthority = false; + s_SerializedType = new List(); + DisableNotOptimizedSerializedType = false; + } +#endif /// /// Subscribe to this static event to get notifications when a instance has been instantiated. /// @@ -31,7 +59,6 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem /// public static event Action OnDestroying; - #if UNITY_EDITOR // Inspector view expand/collapse settings for this derived child class [HideInInspector] @@ -44,17 +71,20 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem #pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `public` + [Obsolete("This field is no longer used and will be removed in a future version.")] internal delegate void RpcReceiveHandler(NetworkBehaviour behaviour, FastBufferReader reader, __RpcParams parameters); // RuntimeAccessModifiersILPP will make this `public` + [Obsolete("This field is no longer used and will be removed in a future version.")] internal static readonly Dictionary __rpc_func_table = new Dictionary(); // RuntimeAccessModifiersILPP will make this `public` (legacy table should be removed in v3.x.x) + [Obsolete("This field is no longer used and will be removed in a future version.")] internal static readonly Dictionary __rpc_name_table = new Dictionary(); #pragma warning restore IDE1006 // restore naming rule violation check -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG private static List s_SerializedType = new List(); // This is used to control the serialized type not optimized messaging for integration test purposes internal static bool DisableNotOptimizedSerializedType; @@ -462,9 +492,14 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) // This should be invoked just prior to the MessageManager processes its outbound queue. SceneManager.CheckForAndSendNetworkObjectSceneChanged(); +#if UNIFIED_NETCODE + if (!NetworkConfig.Prefabs.HasGhostPrefabs) +#endif + { - // Process outbound messages - MessageManager.ProcessSendQueues(); + // Process outbound messages + MessageManager.ProcessSendQueues(); + } // Metrics update needs to be driven by NetworkConnectionManager's update to assure metrics are dispatched after the send queue is processed. MetricsManager.UpdateMetrics(); @@ -1009,11 +1044,13 @@ internal bool NetworkManagerCheckForParent(bool ignoreNetworkManagerCache = fals { #if UNITY_EDITOR var isParented = NetworkManagerHelper.NotifyUserOfNestedNetworkManager(this, ignoreNetworkManagerCache); + + #else - var isParented = transform.root != transform; + var isParented = transform.parent != null; if (isParented) { - throw new Exception(GenerateNestedNetworkManagerMessage(transform)); + Log.Error(new Context(LogLevel.Error, GenerateNestedNetworkManagerMessage(transform))); } #endif return isParented; @@ -1029,7 +1066,17 @@ internal static string GenerateNestedNetworkManagerMessage(Transform transform) ///
private void OnTransformParentChanged() { +#if UNITY_EDITOR + // During editor playmode, we log the message as a dialog box + // and that script sets the parent back to root/null. NetworkManagerCheckForParent(); +#else + if (NetworkManagerCheckForParent()) + { + // During runtime, we log the message and set our parent back to root/null. + transform.parent = null; + } +#endif } /// @@ -1160,7 +1207,7 @@ public int MaximumFragmentedMessageSize internal void Initialize(bool server) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (!DisableNotOptimizedSerializedType) { s_SerializedType.Clear(); @@ -1216,6 +1263,23 @@ internal void Initialize(bool server) // UnityTransport dependencies are then initialized RealTimeProvider = ComponentFactory.Create(this); + +#if UNIFIED_NETCODE && OUT_OF_BAND_RPC + // TODO-FixMe: + // We assign transport at this point to preceed the NetworkConnectionManager + // being initialized. However, HasGhostPrefabs might not be set at this point + // if the prefabs list was set after instantiating the NetworkManager. + // Integration tests do this, but user code could do this too. + // To-Investigate: + // Determine if this really impacts anything having prefabs initialze/register + // at this point versus last. + NetworkConfig.InitializePrefabs(); + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + NetworkConfig.NetworkTransport = gameObject.AddComponent(); + } +#endif + MetricsManager.Initialize(this); { @@ -1223,7 +1287,7 @@ internal void Initialize(bool server) MessageManager.Hook(new NetworkManagerHooks(this)); -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (NetworkConfig.NetworkProfilingMetrics) { MessageManager.Hook(new ProfilingHooks()); @@ -1262,8 +1326,9 @@ internal void Initialize(bool server) BehaviourUpdater = new NetworkBehaviourUpdater(); BehaviourUpdater.Initialize(this); - +#if !UNIFIED_NETCODE NetworkConfig.InitializePrefabs(); +#endif PrefabHandler.RegisterPlayerPrefab(); #if UNITY_EDITOR BeginNetworkSession(); @@ -1310,6 +1375,55 @@ private bool CanStart(StartType type) return true; } +#if UNIFIED_NETCODE + /// + /// The world instance assigned to this NetworkManager instance. + /// + public NetcodeWorld NetcodeWorld { get; internal set; } + internal void InitializeNetcodeWorld() + { + if (NetcodeWorld != null) + { + return; + } + + if (this == Singleton) + { + if (NetCode.Netcode.IsActive) + { + Log.Info(new Context(LogLevel.Normal, "Netcode is not active but has an instance at this point.")); + } + /// !! Important !! + /// Clear out any pre-existing configuration in the event this applicatioin instance has already been connected to a session. + NetCode.Netcode.Reset(); + } + + /// !! Initialize worlds here !! + /// Worlds are created here: + UnifiedBootstrap.CurrentNetworkManagerForInitialization = this; + DefaultWorldInitialization.Initialize("Default World", false); + } + + /// + /// Checks to make sure the NetcodeConfig is configured correctly for hybrid mode. Hybrid mode requires a single world to be used for the NetcodeConfig. + /// + /// True if the configuration is correct; otherwise, false. + private bool UnifiedIsConfiguredCorrectly() + { + if (NetCodeConfig.Global == null) + { + Log.Error(new Context(LogLevel.Error, "You must create a {nameof(NetCodeConfig)} and set it to a single world in order to run in hybrid mode!").AddTag("Unified")); + return false; + } + if (NetCodeConfig.Global.HostWorldModeSelection != NetCodeConfig.HostWorldMode.SingleWorld) + { + Log.Error(new Context(LogLevel.Error, "You must configure {nameof(NetCodeConfig)} to only use a single world in order to run in hybrid mode!").AddTag("Unified")); + return false; + } + return true; + } +#endif + /// /// Starts a server /// @@ -1341,6 +1455,28 @@ public bool StartServer() return false; } +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + if (!UnifiedIsConfiguredCorrectly()) + { + m_ShuttingDown = true; + ShutdownInternal(); + return false; + } + if (LogLevel <= LogLevel.Developer) + { + Log.Info(new Context(LogLevel.Developer, "Creating world: Default world")); + } + InitializeNetcodeWorld(); + } +#endif + return InternalStartServer(); + } + + internal bool InternalStartServer() + { try { IsListening = NetworkConfig.NetworkTransport.StartServer(); @@ -1366,7 +1502,6 @@ public bool StartServer() ShutdownInternal(); IsListening = false; } - return IsListening; } @@ -1399,6 +1534,25 @@ public bool StartClient() return false; } +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + if (!UnifiedIsConfiguredCorrectly()) + { + m_ShuttingDown = true; + ShutdownInternal(); + return false; + } + Log.Info(new Context(LogLevel.Developer, "Creating world: Default world")); + InitializeNetcodeWorld(); + } +#endif + return InternalStartClient(); + } + + internal bool InternalStartClient() + { try { IsListening = NetworkConfig.NetworkTransport.StartClient(); @@ -1423,6 +1577,7 @@ public bool StartClient() return IsListening; } + /// /// Starts a Host /// @@ -1453,6 +1608,25 @@ public bool StartHost() return false; } +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + if (!UnifiedIsConfiguredCorrectly()) + { + m_ShuttingDown = true; + ShutdownInternal(); + return false; + } + Log.Info(new Context(LogLevel.Developer, "Creating world: Default world")); + InitializeNetcodeWorld(); + } +#endif + return InternalStartHost(); + } + + internal bool InternalStartHost() + { try { IsListening = NetworkConfig.NetworkTransport.StartServer(); @@ -1641,7 +1815,7 @@ internal void ShutdownInternal() // place (i.e. sending any last state updates or the like). SpawnManager?.DespawnAndDestroyNetworkObjects(); - SpawnManager?.ServerResetShudownStateForSceneObjects(); + SpawnManager?.ServerResetShutdownStateForSceneObjects(); //// RpcTarget?.Dispose(); @@ -1661,6 +1835,25 @@ internal void ShutdownInternal() IsListening = false; m_ShuttingDown = false; + +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig != null && NetworkConfig.Prefabs != null && NetworkConfig.Prefabs.HasGhostPrefabs) + { + try + { + // Dispose of all worlds + World.DisposeAllWorlds(); + // Clear the world assigned from previous session + NetcodeWorld = null; + } + catch (Exception ex) + { + Log.Exception(ex); + } + } +#endif + // Generate a local notification that the host client is disconnected if (IsHost) { @@ -1689,7 +1882,6 @@ internal void ShutdownInternal() NetworkTimeSystem?.Shutdown(); NetworkTickSystem = null; - if (localClient.IsClient) { // If we were a client, we want to know if we were a host @@ -1797,6 +1989,10 @@ internal abstract class NetcodeAnalytics internal static ResetNetworkManagerDelegate OnNetworkManagerReset; + + /// + /// This is called by the Unity Editor reset button. See which is handled in "NetworkManagerHelper.cs". + /// private void Reset() { OnNetworkManagerReset?.Invoke(this); @@ -1965,5 +2161,12 @@ internal static void OnOneTimeTearDown() } #endif +#if UNIFIED_NETCODE + // TODO-UNIFIED: We might not need all of this (i.e. UnifiedUpdateConnections might be handled differently in unified) + public delegate void OnConnectDelegate(NetcodeConnection connection); + public delegate void OnDisconnectDelegate(NetcodeConnection connection); + public static OnConnectDelegate OnNetCodeConnect; + public static OnDisconnectDelegate OnNetCodeDisconnect; +#endif } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index d2f5aa5869..d493b65d00 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1,17 +1,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using Unity.Netcode.Components; +using Unity.Netcode.Logging; using Unity.Netcode.Runtime; +#if UNIFIED_NETCODE +using Unity.NetCode; +#endif + #if UNITY_EDITOR using UnityEditor; -#if UNITY_2021_2_OR_NEWER using UnityEditor.SceneManagement; -#else -using UnityEditor.Experimental.SceneManagement; -#endif #endif using UnityEngine; using UnityEngine.SceneManagement; @@ -105,6 +107,9 @@ public uint PrefabIdHash /// public NetworkObject CurrentParent { get; private set; } + private int m_SpawnCount; + internal bool HasBeenSpawned => m_SpawnCount > 0; + #if UNITY_EDITOR private const string k_GlobalIdTemplate = "GlobalObjectId_V1-{0}-{1}-{2}-{3}"; @@ -128,9 +133,6 @@ public uint PrefabIdHash // The InContext or InIsolation edit mode network prefab scene instance of the prefab asset (s_PrefabAsset). private static NetworkObject s_PrefabInstance; - private static bool s_DebugPrefabIdGeneration; - - [ContextMenu("Refresh In-Scene Prefab Instances")] internal void RefreshAllPrefabInstances() { @@ -287,11 +289,15 @@ internal void OnValidate() // Always check for in-scene placed to assure any previous version scene assets with in-scene place NetworkObjects gets updated. CheckForInScenePlaced(); +#if UNIFIED_NETCODE + UnifiedValidation(); +#endif + // If the GlobalObjectIdHash value changed, then mark the asset dirty. if (GlobalObjectIdHash != oldValue) { // Check if this is an in-scnee placed NetworkObject (Special Case for In-Scene Placed). - if (IsSceneObject.HasValue && IsSceneObject.Value) + if (InScenePlaced) { // Sanity check to make sure this is a scene placed object. if (globalId.identifierType != k_SceneObjectType) @@ -327,7 +333,7 @@ internal void OnValidate() /// private void CheckForInScenePlaced() { - if (gameObject.scene.IsValid() && gameObject.scene.isLoaded && gameObject.scene.buildIndex >= 0) + if (gameObject.scene.IsValid() && gameObject.scene.buildIndex >= 0) { if (PrefabUtility.IsPartOfAnyPrefab(this)) { @@ -340,7 +346,15 @@ private void CheckForInScenePlaced() EditorUtility.SetDirty(this); } } - IsSceneObject = true; + +#pragma warning disable CS0618 // Type or member is obsolete + // Obsolete with warning means we need the underlying behaviour to keep existing + // TODO-3.x: remove in the 3.x branch + SetSceneObjectStatus(true); +#pragma warning restore CS0618 // Type or member is obsolete + + // We go ahead and set this for "typical in-scene placed" usage patterns so this is serialized + InScenePlaced = true; // Default scene migration synchronization to false for in-scene placed NetworkObjects SceneMigrationSynchronization = false; @@ -348,6 +362,68 @@ private void CheckForInScenePlaced() } #endif // UNITY_EDITOR +#if UNIFIED_NETCODE + [HideInInspector] + [SerializeField] + internal GhostObject GhostObject; + + [HideInInspector] + [SerializeField] + internal bool HasGhost; + + [HideInInspector] + [SerializeField] + internal bool HadBridge; +#if UNITY_EDITOR + private void OnApplicationUpdate() + { + NetworkObjectBridge = gameObject.AddComponent(); + HadBridge = true; + // Transform synchronization is handled by unified netcode + SynchronizeTransform = false; + + EditorApplication.update -= OnApplicationUpdate; + } + + internal void UnifiedValidation() + { + NetworkObjectBridge = GetComponent(); + GhostObject = GetComponent(); + + HasGhost = GhostObject != null; + if (HasGhost) + { + //TODO: Needs to be validated once develop-2.0.0 is merged. + if (InScenePlaced) + { + Debug.LogError($"This experimental version of NGO does not support hybrid in-scene placed objects."); + Destroy(GhostObject); + HasGhost = false; + return; + } + + if (NetworkObjectBridge == null) + { + EditorApplication.update -= OnApplicationUpdate; + EditorApplication.update += OnApplicationUpdate; + } + } + else if (HadBridge && !HasGhost && !NetworkObjectBridge) + { + HadBridge = false; + SynchronizeTransform = true; + } + } +#endif + + public void ApplyScale(Vector3 scale) + { + if (HasGhost) + { + GhostObject.ApplyPostTransformMatrixScale(scale); + } + } +#endif /// /// Gets the NetworkManager that owns this NetworkObject instance /// @@ -411,7 +487,7 @@ public void DeferDespawn(int tickOffset, bool destroy = true) return; } - if (!HasAuthority) + if (!m_HasAuthority) { if (NetworkManagerOwner.LogLevel <= LogLevel.Error) { @@ -628,7 +704,7 @@ public bool SetOwnershipLock(bool lockOwnership = true) } // If we don't have authority exit early - if (!HasAuthority) + if (!m_HasAuthority) { if (NetworkManager.LogLevel <= LogLevel.Error) { @@ -904,7 +980,7 @@ internal void OwnershipRequest(ulong clientRequestingOwnership) // This action is always authorized as long as the client still has authority. // We need to pass in that this is a request approval ownership change. - NetworkManagerOwner.SpawnManager.ChangeOwnership(this, clientRequestingOwnership, HasAuthority, true); + NetworkManagerOwner.SpawnManager.ChangeOwnership(this, clientRequestingOwnership, m_HasAuthority, true); } else { @@ -1150,14 +1226,9 @@ public bool HasOwnershipStatus(OwnershipStatus status) /// /// When in client-server mode, authority should is not considered the same as ownership. /// - public bool HasAuthority => InternalHasAuthority(); + public bool HasAuthority => IsSpawned ? m_HasAuthority : !NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool InternalHasAuthority() - { - var networkManager = NetworkManager; - return networkManager.DistributedAuthorityMode ? OwnerClientId == networkManager.LocalClientId : networkManager.IsServer; - } + private bool m_HasAuthority; /// /// The NetworkManager that owns this NetworkObject. @@ -1225,15 +1296,44 @@ private bool InternalHasAuthority() public bool IsSpawned { get; internal set; } /// - /// Gets if the object is a SceneObject, null if it's not yet spawned but is a scene object. + /// Gets if the object is a SceneObject. /// + /// + /// This method is marked for deprecation.
+ /// Use instead. + ///
+ [Obsolete("Use InScenePlaced instead")] public bool? IsSceneObject { get; internal set; } - //DANGOEXP TODO: Determine if we want to keep this + + /// + /// The serialized value. + /// + [field: HideInInspector] + [field: SerializeField] + private bool m_InScenePlaced; + + /// + /// True if this object is placed in a scene; false otherwise. + /// + public bool InScenePlaced + { + get + { + return m_InScenePlaced; + } + internal set + { + m_InScenePlaced = value; + } + } + /// /// Sets whether this NetworkObject was instantiated as part of a scene /// + /// Only use this when using custom scene loading /// When true, marks this as a scene-instantiated object; when false, marks it as runtime-instantiated + [Obsolete("SetSceneObjectStatus is now calculated during the build.")] public void SetSceneObjectStatus(bool isSceneObject = false) { IsSceneObject = isSceneObject; @@ -1434,19 +1534,20 @@ public bool IsNetworkVisibleTo(ulong clientId) ///
internal Scene SceneOrigin { - get - { - return m_SceneOrigin; - } + get => m_SceneOrigin; set { + if (SceneOriginHandle.IsEmpty() && value.IsValid() && value.isLoaded) + { + SceneOriginHandle = value.handle; + } + // The scene origin should only be set once. // Once set, it should never change. - if (SceneOriginHandle.IsEmpty() && value.IsValid() && value.isLoaded) + if (!m_SceneOrigin.IsValid()) { m_SceneOrigin = value; - SceneOriginHandle = value.handle; } } } @@ -1457,13 +1558,8 @@ internal Scene SceneOrigin ///
internal NetworkSceneHandle GetSceneOriginHandle() { - if (SceneOriginHandle.IsEmpty() && IsSpawned && IsSceneObject != false) - { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogErrorServer($"{nameof(GetSceneOriginHandle)} called when {nameof(SceneOriginHandle)} is still zero but the {nameof(NetworkObject)} is already spawned!"); - } - } + NetworkLog.InternalAssert(!(IsSpawned && InScenePlaced && SceneOriginHandle.IsEmpty()), $"Spawned in scene placed NetworkObject {name} should always have a valid SceneOriginHandle"); + return !SceneOriginHandle.IsEmpty() ? SceneOriginHandle : gameObject.scene.handle; } @@ -1492,7 +1588,7 @@ public void NetworkShow(ulong clientId) return; } - if (!HasAuthority) + if (!m_HasAuthority) { if (NetworkManagerOwner.DistributedAuthorityMode) { @@ -1587,7 +1683,7 @@ public void NetworkHide(ulong clientId) return; } - if (!HasAuthority) + if (!m_HasAuthority) { if (NetworkManagerOwner.DistributedAuthorityMode) { @@ -1622,7 +1718,7 @@ public void NetworkHide(ulong clientId) var message = new DestroyObjectMessage { NetworkObjectId = NetworkObjectId, - DestroyGameObject = !IsSceneObject.Value, + DestroyGameObject = !InScenePlaced, IsDistributedAuthority = NetworkManagerOwner.DistributedAuthorityMode, IsTargetedDestroy = NetworkManagerOwner.DistributedAuthorityMode, TargetClientId = clientId, // Just always populate this value whether we write it or not @@ -1727,6 +1823,8 @@ internal void SetIsDestroying() private void OnDestroy() { + SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged; + // Apply the is destroying flag SetIsDestroying(); @@ -1737,20 +1835,27 @@ private void OnDestroy() return; } + var spawnManager = NetworkManager.SpawnManager; + // Always attempt to remove from scene changed updates - networkManager.SpawnManager?.RemoveNetworkObjectFromSceneChangedUpdates(this); + spawnManager?.MarkNetworkObjectAsDestroying(this); +#if UNIFIED_NETCODE + // N4E controls this on the client, allow this if there is a ghost + if (IsSpawned && !HasGhost && !networkManager.ShutdownInProgress) +#else if (IsSpawned && !networkManager.ShutdownInProgress) +#endif { // An authorized destroy is when done by the authority instance or done due to a scene event and the NetworkObject // was marked as destroy pending scene event (which means the destroy with scene property was set). - var isAuthorityDestroy = HasAuthority || NetworkManager.DAHost || DestroyPendingSceneEvent; + var isAuthorityDestroy = m_HasAuthority || NetworkManager.DAHost || DestroyPendingSceneEvent; // If the NetworkObject's GameObject is still valid and the scene is still valid and loaded, then we are still valid var isStillValid = gameObject != null && gameObject.scene.IsValid() && gameObject.scene.isLoaded; // If we're not the authority and everything is valid and dynamically spawned, then the destroy is not valid. - if (!isAuthorityDestroy && IsSceneObject == false && isStillValid) + if (!isAuthorityDestroy && !InScenePlaced && isStillValid) { if (networkManager.LogLevel <= LogLevel.Error) { @@ -1768,17 +1873,17 @@ private void OnDestroy() } } - if (networkManager.SpawnManager != null && networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject)) + if (spawnManager != null && spawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject)) { if (this == networkObject) { - networkManager.SpawnManager.OnDespawnObject(networkObject, false); + spawnManager.OnDespawnObject(networkObject, false); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool playerObject) + private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool playerObject) { if (NetworkManagerOwner == null) { @@ -1849,7 +1954,28 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla } } - if (!NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene)) + + // Calculate the legacy IsSceneObject value as the public field is obsolete with warning + // We can't break the public behavior of the field. +#pragma warning disable CS0618 // Type or member is obsolete + var legacyIsSceneObject = IsSceneObject.HasValue && IsSceneObject.Value; +#pragma warning restore CS0618 // Type or member is obsolete + + // If SpawnInternal is being called on an object that is marked as InScenePlaced, + // The scene object was never automatically spawned when the scene was loaded. + // Count this object as a dynamically spawned object. + // TODO-[MTT-15388]: Actually support disabled/not spawned InScenePlaced NetworkObjects + if (InScenePlaced && !HasBeenSpawned) + { + if (NetworkManagerOwner.NetworkConfig.EnableSceneManagement && NetworkManagerOwner.LogLevel <= LogLevel.Developer) + { + Debug.LogWarning($"[{name}][SceneOrigin={SceneOriginHandle}] Dynamically spawning InScenePlaced network object. This can cause issues!", this); + } + + InScenePlaced = false; + } + + if (!NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), legacyIsSceneObject, playerObject, ownerClientId, destroyWithScene)) { if (NetworkManagerOwner.LogLevel <= LogLevel.Normal) { @@ -1989,8 +2115,7 @@ public NetworkObject InstantiateAndSpawn(NetworkManager networkManager, ulong ow /// Should the object be destroyed when the scene is changed public void Spawn(bool destroyWithScene = false) { - var networkManager = NetworkManager; - var clientId = networkManager.DistributedAuthorityMode ? networkManager.LocalClientId : NetworkManager.ServerClientId; + var clientId = NetworkManager.DistributedAuthorityMode ? NetworkManager.LocalClientId : NetworkManager.ServerClientId; SpawnInternal(destroyWithScene, clientId, false); } @@ -2026,6 +2151,12 @@ public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false) /// (true) the will be destroyed (false) the will persist after being despawned public void Despawn(bool destroy = true) { +#if UNIFIED_NETCODE + if (HasGhost && destroy == false) + { + throw new NotSupportedException("Despawn without destroy is not supported for hybrid objects."); + } +#endif if (!IsSpawned) { if (NetworkManager.LogLevel <= LogLevel.Error) @@ -2042,10 +2173,72 @@ public void Despawn(bool destroy = true) NetworkManagerOwner.SpawnManager.DespawnObject(this, destroy); } + internal void SetupOnSpawn(ulong networkId, bool isPlayerObject, ulong ownerClientId, bool destroyWithScene) + { + NetworkObjectId = networkId; + IsPlayerObject = isPlayerObject; + OwnerClientId = ownerClientId; + // When spawned, previous owner is always the first assigned owner + PreviousOwnerId = ownerClientId; + m_HasAuthority = NetworkManagerOwner.DistributedAuthorityMode ? OwnerClientId == NetworkManagerOwner.LocalClientId : NetworkManagerOwner.IsServer; + m_SpawnCount++; + IsSpawned = true; + + // If this is the player, and the client is the owner, then lock ownership by default + if (NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.LocalClientId == ownerClientId && isPlayerObject) + { + AddOwnershipExtended(OwnershipStatusExtended.Locked); + } + + if (IsSpawnAuthority) + { + SetupObservers(); + } + + /* + * Setup scene related settings + */ + DestroyWithScene = InScenePlaced || destroyWithScene; + if (InScenePlaced) + { + // Always check to make sure our scene of origin is properly set for in-scene placed NetworkObjects + // Note: Always check SceneOriginHandle directly at this specific location. + if (SceneOriginHandle.IsEmpty()) + { + SceneOrigin = gameObject.scene; + } + + // If we are an in-scene placed NetworkObject and our InScenePlacedSourceGlobalObjectIdHash is set + // then assign this to the PrefabGlobalObjectIdHash + if (InScenePlacedSourceGlobalObjectIdHash != 0) + { + PrefabGlobalObjectIdHash = InScenePlacedSourceGlobalObjectIdHash; + } + } + else if (ActiveSceneSynchronization) + { + // Just in case it is a recycled NetworkObject, unsubscribe first + SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged; + SceneManager.activeSceneChanged += CurrentlyActiveSceneChanged; + } + } + + /// + /// Resets this NetworkObject at the end of a session. + /// Ensures scene objects are ready to be reused + /// + internal void ResetOnShutdown() + { + NetworkLog.InternalAssert(NetworkManager.ShutdownInProgress, "This method should only be called while the NetworkManager is shutting down"); + m_SpawnCount = 0; + ResetOnDespawn(); + } + internal void ResetOnDespawn() { // Always clear out the observers list when despawned Observers.Clear(); + m_HasAuthority = false; IsSpawnAuthority = false; IsSpawned = false; DeferredDespawnTick = 0; @@ -2053,6 +2246,62 @@ internal void ResetOnDespawn() RemoveOwnershipExtended(OwnershipStatusExtended.Locked | OwnershipStatusExtended.Requested); } + internal void SetupObservers() + { + NetworkLog.InternalAssert(IsSpawnAuthority, "This function should only be called on the authority."); + + if (!SpawnWithObservers) + { + if (NetworkManagerOwner.DistributedAuthorityMode) + { + // Always add the owner/authority in DA mode even if SpawnWithObservers is false + // (authority should not take into consideration networkObject.CheckObjectVisibility when SpawnWithObservers is false) + AddObserver(OwnerClientId); + } + + return; + } + + // If running as a server only, then make sure to always add the server's client identifier + if (NetworkManagerOwner.IsServer && !NetworkManagerOwner.IsHost) + { + AddObserver(NetworkManager.ServerClientId); + } + + // If SpawnWithObservers is set, + // then add all connected clients as observers + foreach (var clientId in NetworkManagerOwner.ConnectedClientsIds) + { + // If CheckObjectVisibility has a callback, then allow that method determine who the observers are. + if (CheckObjectVisibility != null && !CheckObjectVisibility(clientId)) + { + continue; + } + AddObserver(clientId); + } + + // Intentionally checking as opposed to just assigning in order to generate notification. + if (!Observers.Contains(OwnerClientId)) + { + // The owner only needs to always be included in DA mode. + if (NetworkManagerOwner.DistributedAuthorityMode) + { + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Client-{OwnerClientId} is the owner of {name} but is not an observer! Adding owner as an observer!"); + } + AddObserver(OwnerClientId); + } + else + { + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"Client-{OwnerClientId} is the owner of {name} but is not an observer! This may cause issues"); + } + } + } + } + /// /// Removes all ownership of an object from any client. Can only be called from server /// @@ -2083,7 +2332,7 @@ public void ChangeOwnership(ulong newOwnerClientId) } return; } - NetworkManagerOwner.SpawnManager.ChangeOwnership(this, newOwnerClientId, HasAuthority); + NetworkManagerOwner.SpawnManager.ChangeOwnership(this, newOwnerClientId, m_HasAuthority); } /// @@ -2092,20 +2341,18 @@ public void ChangeOwnership(ulong newOwnerClientId) /// internal void InvokeBehaviourOnOwnershipChanged(ulong originalOwnerClientId, ulong newOwnerClientId) { - if (!IsSpawned) - { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogErrorServer($"[{name}][Attempted behavior invoke on ownership changed before {nameof(NetworkObject)} was spawned]"); - } - return; - } + NetworkLog.InternalAssert(IsSpawned, "[{name}][Attempted behavior invoke on ownership changed before {nameof(NetworkObject)} was spawned]"); var distributedAuthorityMode = NetworkManagerOwner.DistributedAuthorityMode; var isServer = NetworkManagerOwner.IsServer; var isPreviousOwner = originalOwnerClientId == NetworkManagerOwner.LocalClientId; var isNewOwner = newOwnerClientId == NetworkManagerOwner.LocalClientId; + if (distributedAuthorityMode) + { + m_HasAuthority = isNewOwner; + } + if (distributedAuthorityMode || isPreviousOwner) { NetworkManagerOwner.SpawnManager.UpdateOwnershipTable(this, originalOwnerClientId, true); @@ -2318,7 +2565,7 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true) // DANGO-TODO: Do we want to worry about ownership permissions here? // It wouldn't make sense to not allow parenting, but keeping this note here as a reminder. - var isAuthority = HasAuthority || (AllowOwnerToParent && IsOwner); + var isAuthority = m_HasAuthority || (AllowOwnerToParent && IsOwner); // If we don't have authority and we are not shutting down, then don't allow any parenting. // If we are shutting down and don't have authority then allow it. @@ -2393,7 +2640,7 @@ private void OnTransformParentChanged() // With distributed authority, we need to track "valid authoritative" parenting changes. // So, either the authority or AuthorityAppliedParenting is considered a "valid parenting change". - var isParentingAuthority = HasAuthority || AuthorityAppliedParenting || (AllowOwnerToParent && IsOwner); + var isParentingAuthority = m_HasAuthority || AuthorityAppliedParenting || (AllowOwnerToParent && IsOwner); // If we are spawned and don't have authority; reset the parent back to the cached parent and exit if (!isParentingAuthority) { @@ -2506,6 +2753,10 @@ private void OnTransformParentChanged() // If you couldn't find your parent, we put you into OrphanChildren set and every time we spawn another NetworkObject locally due to replication, // we call CheckOrphanChildren() method and quickly iterate over OrphanChildren set and see if we can reparent/adopt one. internal static HashSet OrphanChildren = new HashSet(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => OrphanChildren = new HashSet(); +#endif internal bool ApplyNetworkParenting(bool removeParent = false, bool ignoreNotSpawned = false, bool orphanedChildPass = false, bool enableNotification = true) { @@ -2528,8 +2779,7 @@ internal bool ApplyNetworkParenting(bool removeParent = false, bool ignoreNotSpa // Handle the first in-scene placed NetworkObject parenting scenarios. Once the m_LatestParent // has been set, this will not be entered into again (i.e. the later code will be invoked and // users will get notifications when the parent changes). - var isInScenePlaced = IsSceneObject.HasValue && IsSceneObject.Value; - if (transform.parent != null && !removeParent && !m_LatestParent.HasValue && isInScenePlaced) + if (transform.parent != null && !removeParent && !m_LatestParent.HasValue && InScenePlaced) { var parentNetworkObject = transform.parent.GetComponent(); @@ -2544,8 +2794,8 @@ internal bool ApplyNetworkParenting(bool removeParent = false, bool ignoreNotSpa m_CachedWorldPositionStays = false; return true; } - else // If the parent still isn't spawned add this to the orphaned children and return false - if (!parentNetworkObject.IsSpawned) + // If the parent still isn't spawned add this to the orphaned children and return false + else if (!parentNetworkObject.IsSpawned) { OrphanChildren.Add(this); return false; @@ -2644,8 +2894,6 @@ internal void InvokeBehaviourNetworkPreSpawn() internal void InvokeBehaviourNetworkSpawn() { - NetworkManagerOwner.SpawnManager.UpdateOwnershipTable(this, OwnerClientId); - // Always invoke all InternalOnNetworkSpawn methods on each child NetworkBehaviour // ** before ** invoking OnNetworkSpawn. // This assures all NetworkVariables and RPC related tables have been initialized @@ -2665,7 +2913,7 @@ internal void InvokeBehaviourNetworkSpawn() childBehaviour.InternalOnNetworkSpawn(); } - // After initialization, we can then invoke OnNetworkSpawn on each child NetworkBehaviour. + // After internally spawning, we can then invoke OnNetworkSpawn on each child NetworkBehaviour. foreach (var childBehaviour in ChildNetworkBehaviours.Values) { if (!childBehaviour.gameObject.activeInHierarchy) @@ -2740,6 +2988,14 @@ internal string GenerateDisabledNetworkBehaviourWarning(NetworkBehaviour network } internal Dictionary ChildNetworkBehaviours; + + /// + /// TODO-UNIFIED: + /// We should pre-calculate the index id's in the editor and save out two lists: + /// - All derived components in a pre-determined order. + /// - All of the identifiers aligned with the above list + /// Then construct the dictionar during awake. + /// internal bool InitializeChildNetworkBehaviours() { ChildNetworkBehaviours = new Dictionary(); @@ -2770,9 +3026,7 @@ internal bool InitializeChildNetworkBehaviours() networkTransform.IsNested = networkTransform.gameObject != gameObject; NetworkTransforms.Add(networkTransform); } - #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D - var rigidbodyBase = behaviour as NetworkRigidbodyBase; if (rigidbodyBase != null) { @@ -2780,7 +3034,72 @@ internal bool InitializeChildNetworkBehaviours() } #endif } +#if UNIFIED_NETCODE + // For now, cycle through all known NetworkRigidbodyBase derived components + // and destroy them all if this is a hybrid prefab instance. + // This allows a user to not have to make direct adjustments until trying out their NGO prefab + // as a hybrid spawned prefab. + if (HasGhost && !NetworkManager.DistributedAuthorityMode) + { +#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D + // TODO-UNIFIED: This needs to be updated to make it "opt-in". + // If the GhostObject is not configured for prediction but is still using a Rigidbody, then go ahead and remove it on + // the client side to improve performance by default. + // TODO-UNIFIED: Determine if recent unified physics updates does not require checking for prediction. + if (NetworkRigidbodies != null) + { + var isServer = NetworkManager.IsServer; + for (int i = NetworkRigidbodies.Count - 1; i >= 0; i--) + { + var currenObject = NetworkRigidbodies[i].gameObject; + var currentHasGhostRigidBody = currenObject.GetComponent() != null; + if (!isServer) + { +#if COM_UNITY_MODULES_PHYSICS + var rigidBody = currenObject.GetComponent(); + if (rigidBody != null && !currentHasGhostRigidBody) + { + Destroy(rigidBody); + } +#endif +#if COM_UNITY_MODULES_PHYSICS2D + var rigidBody2D = currenObject.GetComponent(); + if (rigidBody2D != null && !currentHasGhostRigidBody) + { + Destroy(rigidBody2D); + } +#endif + } + // Both the server and clients will still remove and destroy the NetworkRigidbody + // since there is no point in synchronizing these when it is handled via unified. + var networkRigidbody = NetworkRigidbodies[i]; + NetworkRigidbodies.Remove(networkRigidbody); + ChildNetworkBehaviours.Remove(networkRigidbody.NetworkBehaviourId); + Destroy(networkRigidbody); + } + } +#endif + // This is defined out since users might have derived NetworkTransforms +#if UNIFIED_NETCODE_DESTROY + // When hybrid spawning, the transform is synchronized by the GhostObject. + // As a convenience, we remove and destroy all NetworkTransforms. + // TODO-Parenting-Related-Area: We need to replicate this functionality in a GhostObject + // Possibly use a "Synchronize" property and display only on children of a root parent GhostObject. + if (NetworkTransforms != null) + { + NetworkManager.Log.Warning(new Logging.Context(LogLevel.Developer, $"[]{name} Hybrid spawned objects do not support {nameof(NetworkTransform)} and " + + $"are removed at runtime. If hybrid spawning is intended, then remove it from the network prefab to avoid allocating and de-allocating at runtime.")); + for (int i = NetworkTransforms.Count - 1; i >= 0; i--) + { + ChildNetworkBehaviours.Remove(NetworkTransforms[i].NetworkBehaviourId); + Destroy(NetworkTransforms[i]); + } + NetworkTransforms.Clear(); + } +#endif + } +#endif return true; } @@ -2914,18 +3233,21 @@ internal struct SerializedObject public ulong OwnerClientId; public ushort OwnershipFlags; - private const ushort k_IsPlayerObject = 0x001; - private const ushort k_HasParent = 0x002; - private const ushort k_IsSceneObject = 0x004; - private const ushort k_HasTransform = 0x008; - private const ushort k_IsLatestParentSet = 0x010; - private const ushort k_WorldPositionStays = 0x020; - private const ushort k_DestroyWithScene = 0x040; - private const ushort k_DontDestroyWithOwner = 0x080; - private const ushort k_HasOwnershipFlags = 0x100; - private const ushort k_SyncObservers = 0x200; - private const ushort k_SpawnWithObservers = 0x400; - private const ushort k_HasInstantiationData = 0x800; + private const ushort k_IsPlayerObject = 0x0001; + private const ushort k_HasParent = 0x0002; + private const ushort k_IsSceneObject = 0x0004; + private const ushort k_HasTransform = 0x0008; + private const ushort k_IsLatestParentSet = 0x0010; + private const ushort k_WorldPositionStays = 0x0020; + private const ushort k_DestroyWithScene = 0x0040; + private const ushort k_DontDestroyWithOwner = 0x0080; + private const ushort k_HasOwnershipFlags = 0x0100; + private const ushort k_SyncObservers = 0x0200; + private const ushort k_SpawnWithObservers = 0x0400; + private const ushort k_HasInstantiationData = 0x0800; +#if UNIFIED_NETCODE + private const ushort k_HasGhost = 0x1000; +#endif public bool IsPlayerObject; public bool HasParent; @@ -2946,6 +3268,9 @@ internal struct SerializedObject public bool SyncObservers; public bool SpawnWithObservers; public bool HasInstantiationData; +#if UNIFIED_NETCODE + public bool HasGhost; +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ushort GetBitsetRepresentation() @@ -2995,10 +3320,19 @@ internal ushort GetBitsetRepresentation() { bitset |= k_SpawnWithObservers; } + if (HasInstantiationData) { bitset |= k_HasInstantiationData; } + +#if UNIFIED_NETCODE + if (HasGhost) + { + bitset |= k_HasGhost; + } + +#endif return bitset; } @@ -3017,6 +3351,9 @@ internal void SetStateFromBitset(ushort bitset) SyncObservers = (bitset & k_SyncObservers) != 0; SpawnWithObservers = (bitset & k_SpawnWithObservers) != 0; HasInstantiationData = (bitset & k_HasInstantiationData) != 0; +#if UNIFIED_NETCODE + HasGhost = (bitset & k_HasGhost) != 0; +#endif } // When handling the initial synchronization of NetworkObjects, @@ -3262,7 +3599,12 @@ internal void SynchronizeNetworkBehaviours(ref BufferSerializer serializer } } - internal SerializedObject Serialize(ulong targetClientId = NetworkManager.ServerClientId, bool syncObservers = false) + /// + /// Creates a on an authority client. + /// Used to synchronize state to a non-authority client. + /// + /// This function is the authority mirror of + internal SerializedObject SerializeSpawnedObject(ulong targetClientId = NetworkManager.ServerClientId, bool syncObservers = false) { var obj = new SerializedObject { @@ -3271,7 +3613,7 @@ internal SerializedObject Serialize(ulong targetClientId = NetworkManager.Server NetworkObjectId = NetworkObjectId, OwnerClientId = OwnerClientId, IsPlayerObject = IsPlayerObject, - IsSceneObject = IsSceneObject ?? true, + IsSceneObject = InScenePlaced, DestroyWithScene = DestroyWithScene, DontDestroyWithOwner = DontDestroyWithOwner, HasOwnershipFlags = NetworkManagerOwner.DistributedAuthorityMode, @@ -3282,7 +3624,10 @@ internal SerializedObject Serialize(ulong targetClientId = NetworkManager.Server Hash = CheckForGlobalObjectIdHashOverride(), OwnerObject = this, TargetClientId = targetClientId, - HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0 + HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0, +#if UNIFIED_NETCODE + HasGhost = HasGhost, +#endif }; // Handle Parenting @@ -3337,25 +3682,23 @@ internal SerializedObject Serialize(ulong targetClientId = NetworkManager.Server } /// - /// Used to deserialize a serialized which occurs - /// when the client is approved or during a scene transition + /// Does a non-authority local spawn of a given . + /// This occurs when the client is approved, a new object is spawned by an authority, or during a scene transition. /// - /// Deserialized scene object data - /// FastBufferReader for the NetworkVariable data - /// NetworkManager instance + /// This function is the non-authority mirror of + /// Deserialized data received from the authority for this + /// FastBufferReader for any additional data sent with this object on spawn. + /// NetworkManager instance. /// will be true if invoked by CreateObjectMessage /// The deserialized NetworkObject or null if deserialization failed - internal static NetworkObject Deserialize(in SerializedObject serializedObject, FastBufferReader reader, NetworkManager networkManager, bool invokedByMessage = false) + [return: MaybeNull] + internal static NetworkObject DeserializeAndSpawnObject(in SerializedObject serializedObject, FastBufferReader reader, NetworkManager networkManager, bool invokedByMessage = false) { var endOfSynchronizationData = reader.Position + serializedObject.SynchronizationDataSize; if (serializedObject.NetworkObjectId == default) { - if (networkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogErrorServer($"[{nameof(GlobalObjectIdHash)}={serializedObject.Hash}] Received spawn request with invalid {nameof(NetworkObjectId)} {serializedObject.NetworkObjectId}. This should not happen!"); - } - + NetworkLog.LogErrorServer(new Context(LogLevel.Error, $"Received spawn request with invalid {nameof(NetworkObjectId)}. This should not happen!").AddInfo(nameof(NetworkObjectId), serializedObject.NetworkObjectId).AddInfo(nameof(GlobalObjectIdHash), serializedObject.Hash)); reader.Seek(endOfSynchronizationData); return null; } @@ -3372,7 +3715,8 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject, { if (networkManager.LogLevel <= LogLevel.Normal) { - NetworkLog.LogWarning($"[{networkObject.name}][Deserialize][{nameof(NetworkBehaviour)}Synchronization][Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!"); + var networkObjectName = networkObject != null ? networkObject.name : "null"; + NetworkLog.LogWarning($"[{networkObjectName}][Deserialize][{nameof(NetworkBehaviour)}Synchronization][Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!"); } reader.Seek(endOfSynchronizationData); } @@ -3446,34 +3790,18 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject, return networkObject; } - /// - /// Subscribes to changes in the currently active scene - /// - /// - /// Only for dynamically spawned NetworkObjects - /// - internal void SubscribeToActiveSceneForSynch() - { - if (ActiveSceneSynchronization) - { - if (IsSceneObject.HasValue && !IsSceneObject.Value) - { - // Just in case it is a recycled NetworkObject, unsubscribe first - SceneManager.activeSceneChanged -= CurrentlyActiveSceneChanged; - SceneManager.activeSceneChanged += CurrentlyActiveSceneChanged; - } - } - } - /// /// If AutoSynchActiveScene is enabled, then this is the callback that handles updating /// a NetworkObject's scene information. /// + /// + /// Should only be used for dynamically spawned NetworkObjects + /// private void CurrentlyActiveSceneChanged(Scene current, Scene next) { // Early exit if the NetworkObject is not spawned, is an in-scene placed NetworkObject, // or the NetworkManager is shutting down. - if (!IsSpawned || IsSceneObject != false || NetworkManagerOwner.ShutdownInProgress) + if (!IsSpawned || NetworkManagerOwner.ShutdownInProgress || InScenePlaced) { return; } @@ -3483,7 +3811,7 @@ private void CurrentlyActiveSceneChanged(Scene current, Scene next) { // Only dynamically spawned NetworkObjects that are not already in the newly assigned active scene will migrate // and update their scene handles - if (IsSceneObject.HasValue && !IsSceneObject.Value && gameObject.scene != next && gameObject.transform.parent == null) + if (gameObject.scene != next && gameObject.transform.parent == null) { SceneManager.MoveGameObjectToScene(gameObject, next); SceneChangedUpdate(next); @@ -3502,20 +3830,20 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false) return; } + // Don't create notification if there is a scene event in progress. if (NetworkManagerOwner.SceneManager.IsSceneEventInProgress()) { return; } - var isAuthority = HasAuthority; SceneOriginHandle = scene.handle; // non-authority needs to update the NetworkSceneHandle - if (!isAuthority && NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle)) + if (!m_HasAuthority && NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle)) { NetworkSceneHandle = NetworkManagerOwner.SceneManager.ClientSceneHandleToServerSceneHandle[SceneOriginHandle]; } - else if (isAuthority) + else if (m_HasAuthority) { // Since the authority is the source of truth for the NetworkSceneHandle, // the NetworkSceneHandle is the same as the SceneOriginHandle. @@ -3528,8 +3856,8 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false) NetworkSceneHandle = SceneOriginHandle; } } - else // Otherwise, the client did not find the client to server scene handle - if (NetworkManagerOwner.LogLevel <= LogLevel.Developer) + // Otherwise, the client did not find the client to server scene handle + else if (NetworkManagerOwner.LogLevel <= LogLevel.Developer) { // There could be a scenario where a user has some client-local scene loaded that they migrate the NetworkObject // into, but that scenario seemed very edge case and under most instances a user should be notified that this @@ -3542,7 +3870,7 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false) OnMigratedToNewScene?.Invoke(); // Only the authority side will notify clients of non-parented NetworkObject scene changes - if (isAuthority && notify && !transform.parent) + if (m_HasAuthority && notify && !transform.parent) { NetworkManagerOwner.SceneManager.NotifyNetworkObjectSceneChanged(this); } @@ -3552,7 +3880,76 @@ private void Awake() { SetCachedParent(transform.parent); SceneOrigin = gameObject.scene; + + } + +#if UNIFIED_NETCODE + +#if DEBUG_ENABLE_DISABLE + private void OnEnable() + { + Debug.Log("Enabled!"); + } + + private void OnDisable() + { + Debug.Log("Disabled!"); + if (IsSpawned || HasGhost) + { + if (HasGhost && GhostObject.IsPrefab()) + { + return; + } + gameObject.SetActive(true); + } + + try + { + throw new Exception("Disabled trap!"); + } + catch (Exception ex) + { + Debug.LogWarning($"[{name}][{ex.Message}] Callstack:\n{ex.StackTrace}"); + } } +#endif + + private void Start() + { + InitGhost(); + } + [SerializeField] + [HideInInspector] + internal NetworkObjectBridge NetworkObjectBridge; + + private void InitGhost() + { + if (!NetworkManager.IsListening) + { + if (NetworkManager.LogLevel == LogLevel.Developer) + { + Debug.LogWarning($"[{nameof(NetworkObject)}] Did not register because there is no session in progress!"); + } + return; + } + + if (!HasGhost || !NetworkObjectBridge || GhostObject.IsPrefab()) + { + // Nothing to register + return; + } + + // All instances with Ghosts are automatically registered + if (NetworkManager.LogLevel == LogLevel.Developer) + { + Debug.Log($"[{nameof(NetworkObject)}] GhostBridge {name} detected and instantiated."); + } + if (GhostObject.WasInitialized && NetworkObjectBridge.NetworkObjectId.Value != 0) + { + NetworkManager.SpawnManager.GhostSpawnManager.RegisterGhostBridge(NetworkObjectBridge.NetworkObjectId.Value, this); + } + } +#endif /// /// Update @@ -3571,7 +3968,7 @@ internal bool UpdateForSceneChanges() // the NetworkManager is shutting down, the NetworkObject is not spawned, it is an in-scene placed // NetworkObject, or the GameObject's current scene handle is the same as the SceneOriginHandle if (!SceneMigrationSynchronization || !IsSpawned || NetworkManagerOwner.ShutdownInProgress || - !NetworkManagerOwner.NetworkConfig.EnableSceneManagement || IsSceneObject != false || !gameObject) + !NetworkManagerOwner.NetworkConfig.EnableSceneManagement || InScenePlaced || !gameObject) { // Stop checking for a scene migration return false; @@ -3606,7 +4003,7 @@ internal uint CheckForGlobalObjectIdHashOverride() // If scene management is disabled and this is an in-scene placed NetworkObject then go ahead // and send the InScenePlacedSourcePrefab's GlobalObjectIdHash value (i.e. what to dynamically spawn) - if (!networkManager.NetworkConfig.EnableSceneManagement && IsSceneObject.Value && InScenePlacedSourceGlobalObjectIdHash != 0) + if (!networkManager.NetworkConfig.EnableSceneManagement && InScenePlaced && InScenePlacedSourceGlobalObjectIdHash != 0) { return InScenePlacedSourceGlobalObjectIdHash; } @@ -3614,7 +4011,7 @@ internal uint CheckForGlobalObjectIdHashOverride() // If the PrefabGlobalObjectIdHash is a non-zero value and the GlobalObjectIdHash value is // different from the PrefabGlobalObjectIdHash value, then the NetworkObject instance is // an override for the original network prefab (i.e. PrefabGlobalObjectIdHash) - if (!IsSceneObject.Value && GlobalObjectIdHash != PrefabGlobalObjectIdHash) + if (!InScenePlaced && GlobalObjectIdHash != PrefabGlobalObjectIdHash) { // If the PrefabGlobalObjectIdHash is already populated (i.e. InstantiateAndSpawn used), then return this if (PrefabGlobalObjectIdHash != 0) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs index c48dea0333..5ac60e2cd4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkUpdateLoop.cs @@ -70,19 +70,19 @@ public enum NetworkUpdateStage : byte /// public static class NetworkUpdateLoop { - private static Dictionary> s_UpdateSystem_Sets; - private static Dictionary s_UpdateSystem_Arrays; - private const int k_UpdateSystem_InitialArrayCapacity = 1024; + private static Dictionary> s_UpdateSystemSets; + private static Dictionary s_UpdateSystemArrays; + private const int k_UpdateSystemInitialArrayCapacity = 1024; static NetworkUpdateLoop() { - s_UpdateSystem_Sets = new Dictionary>(); - s_UpdateSystem_Arrays = new Dictionary(); + s_UpdateSystemSets = new Dictionary>(); + s_UpdateSystemArrays = new Dictionary(); foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage))) { - s_UpdateSystem_Sets.Add(updateStage, new HashSet()); - s_UpdateSystem_Arrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystem_InitialArrayCapacity]); + s_UpdateSystemSets.Add(updateStage, new HashSet()); + s_UpdateSystemArrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystemInitialArrayCapacity]); } } @@ -105,19 +105,19 @@ public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSys /// The being registered for the implementation public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update) { - var sysSet = s_UpdateSystem_Sets[updateStage]; + var sysSet = s_UpdateSystemSets[updateStage]; if (!sysSet.Contains(updateSystem)) { sysSet.Add(updateSystem); int setLen = sysSet.Count; - var sysArr = s_UpdateSystem_Arrays[updateStage]; + var sysArr = s_UpdateSystemArrays[updateStage]; int arrLen = sysArr.Length; if (setLen > arrLen) { // double capacity - sysArr = s_UpdateSystem_Arrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2]; + sysArr = s_UpdateSystemArrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2]; } sysSet.CopyTo(sysArr); @@ -149,13 +149,13 @@ public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateS /// The to be deregistered from the implementation public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update) { - var sysSet = s_UpdateSystem_Sets[updateStage]; + var sysSet = s_UpdateSystemSets[updateStage]; if (sysSet.Contains(updateSystem)) { sysSet.Remove(updateSystem); int setLen = sysSet.Count; - var sysArr = s_UpdateSystem_Arrays[updateStage]; + var sysArr = s_UpdateSystemArrays[updateStage]; int arrLen = sysArr.Length; sysSet.CopyTo(sysArr); @@ -177,7 +177,7 @@ internal static void RunNetworkUpdateStage(NetworkUpdateStage updateStage) { UpdateStage = updateStage; - var sysArr = s_UpdateSystem_Arrays[updateStage]; + var sysArr = s_UpdateSystemArrays[updateStage]; int arrLen = sysArr.Length; for (int curIdx = 0; curIdx < arrLen; curIdx++) { @@ -291,6 +291,17 @@ public static PlayerLoopSystem CreateLoopSystem() [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Initialize() { +#if UNITY_EDITOR + // Reset statics + s_UpdateSystemSets = new Dictionary>(); + s_UpdateSystemArrays = new Dictionary(); + foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage))) + { + s_UpdateSystemSets.Add(updateStage, new HashSet()); + s_UpdateSystemArrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystemInitialArrayCapacity]); + } + UpdateStage = default; +#endif UnregisterLoopSystems(); RegisterLoopSystems(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/ContextualLogger.cs b/com.unity.netcode.gameobjects/Runtime/Logging/ContextualLogger.cs index 006f919ce1..3141a2a3bf 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/ContextualLogger.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/ContextualLogger.cs @@ -60,7 +60,7 @@ public ContextualLogger(Object inspectorObject, [NotNull] NetworkManager network } /// Used for the NetworkLog - internal ContextualLogger(NetworkManager networkManager, bool useCompatibilityMode) + internal ContextualLogger(NetworkManager networkManager, bool useCompatibilityMode = false) { m_UseCompatibilityMode = useCompatibilityMode; m_ManagerContext = new LogContextNetworkManager(networkManager); @@ -89,7 +89,7 @@ internal DisposableContext AddDisposableInfo(string key, object value) [Conditional(k_CompilationCondition)] internal void RemoveInfo(string key) { - m_LoggerContext.ClearInfo(key); + m_LoggerContext.RemoveInfo(key); } [HideInCallstack] @@ -125,6 +125,19 @@ public void Exception(Exception exception) { Debug.unityLogger.LogException(exception, m_Object); } + [HideInCallstack] + public void Exception(Exception exception, Context context) + { + // Don't act if the LogLevel is higher than the level of this log + if (m_ManagerContext.LogLevel > context.Level) + { + return; + } + + var message = BuildLog(context); + Debug.unityLogger.LogException(new Exception(message, exception), context.RelevantObjectOverride ?? m_Object); + } + [HideInCallstack] private void Log(LogType logType, Context context) diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogBuilder.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogBuilder.cs index 0c59e10bd4..c8f8c1ba25 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/LogBuilder.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogBuilder.cs @@ -31,6 +31,7 @@ public void AppendInfo(object key, object value) } public void Append(string value) => m_Builder.Append(value); + public void AppendLine(string value) => m_Builder.AppendLine(value); public string Build() => m_Builder.ToString(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.cs deleted file mode 100644 index 8c6cbd0bf1..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Runtime.CompilerServices; -using Object = UnityEngine.Object; - -namespace Unity.Netcode.Logging -{ - internal interface ILogContext - { - public void AppendTo(LogBuilder builder) - { - } - } - - internal struct Context : ILogContext - { - public readonly LogLevel Level; - private readonly string m_CallingFunction; - internal readonly string Message; - internal Object RelevantObjectOverride; - - private readonly GenericContext m_Other; - - public Context(LogLevel level, string msg, [CallerMemberName] string memberName = "") - { - Level = level; - Message = msg; - m_CallingFunction = memberName; - - m_Other = GenericContext.Create(); - RelevantObjectOverride = null; - } - - internal Context(LogLevel level, string msg, bool noCaller) - { - Level = level; - Message = msg; - m_CallingFunction = null; - - m_Other = GenericContext.Create(); - RelevantObjectOverride = null; - } - - public void AppendTo(LogBuilder builder) - { - // [CallingFunction] - if (!string.IsNullOrEmpty(m_CallingFunction)) - { - builder.AppendTag(m_CallingFunction); - } - - // [SomeContext][SomeName:SomeValue] - m_Other.AppendTo(builder); - - // Human-readable log message - builder.Append(" "); - builder.Append(Message); - } - - public Context AddInfo(object key, object value) - { - m_Other.StoreInfo(key, value); - return this; - } - - public Context AddTag(string msg) - { - m_Other.StoreTag(msg); - return this; - } - - public Context AddObject(Object obj) - { - RelevantObjectOverride = obj; - return this; - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.meta new file mode 100644 index 0000000000..7c7f21c305 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a57adef9bcf4768866c1e922dff4ad1 +timeCreated: 1779217887 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/CollectionContext.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/CollectionContext.cs new file mode 100644 index 0000000000..70c493950d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/CollectionContext.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Unity.Netcode.Logging +{ + internal delegate string LogCollectionBuilder(TItem item); + internal readonly struct CollectionContext : ILogContext + { + private readonly LogCollectionBuilder m_Delegate; + private readonly IEnumerable m_Collection; + + public CollectionContext(IEnumerable collection, LogCollectionBuilder builder) + { + m_Delegate = builder; + m_Collection = collection; + } + + public void AppendTo(LogBuilder builder) + { + foreach (var item in m_Collection) + { + builder.AppendLine(m_Delegate(item)); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/CollectionContext.cs.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/CollectionContext.cs.meta new file mode 100644 index 0000000000..5323a6eda2 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/CollectionContext.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f64de1e86c70433e96280f0c8af6e8f6 +timeCreated: 1779214517 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/GenericContext.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/GenericContext.cs similarity index 78% rename from com.unity.netcode.gameobjects/Runtime/Logging/GenericContext.cs rename to com.unity.netcode.gameobjects/Runtime/Logging/LogContext/GenericContext.cs index 47d198f447..643ec30324 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/GenericContext.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/GenericContext.cs @@ -5,20 +5,20 @@ namespace Unity.Netcode.Logging { internal readonly struct GenericContext : ILogContext, IDisposable { - private readonly List m_Contexts; + private readonly List m_Tags; private readonly Dictionary m_Info; - private GenericContext(List contexts, Dictionary info) + private GenericContext(List tags, Dictionary info) { - m_Contexts = contexts; + m_Tags = tags; m_Info = info; } public void AppendTo(LogBuilder builder) { - if (m_Contexts != null) + if (m_Tags != null) { - foreach (var ctx in m_Contexts) + foreach (var ctx in m_Tags) { builder.AppendTag(ctx); } @@ -33,9 +33,9 @@ public void AppendTo(LogBuilder builder) } } - public void StoreTag(string msg) + public void StoreTag(string tag) { - m_Contexts.Add(msg); + m_Tags.Add(tag); } public void StoreInfo(object key, object value) @@ -43,11 +43,16 @@ public void StoreInfo(object key, object value) m_Info.Add(key, value); } - public void ClearInfo(object key) + public void RemoveInfo(object key) { m_Info?.Remove(key); } + public void RemoveTag(string tag) + { + m_Tags?.Remove(tag); + } + public void Dispose() { PreallocatedStore.Free(this); @@ -76,7 +81,7 @@ internal static GenericContext GetPreallocated() internal static void Free(GenericContext ctx) { - ctx.m_Contexts.Clear(); + ctx.m_Tags.Clear(); ctx.m_Info.Clear(); k_Preallocated.Enqueue(ctx); } diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/GenericContext.cs.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/GenericContext.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Logging/GenericContext.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Logging/LogContext/GenericContext.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContext.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContext.cs new file mode 100644 index 0000000000..a36b6b91fb --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContext.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Object = UnityEngine.Object; + +namespace Unity.Netcode.Logging +{ + internal interface ILogContext + { + public void AppendTo(LogBuilder builder); + } + + internal struct Context : ILogContext, IDisposable + { + public readonly LogLevel Level; + private readonly string m_CallingFunction; + internal readonly string Message; + internal Object RelevantObjectOverride; + + private readonly GenericContext m_Other; + private List m_Prepend; + private List m_Postpend; + + public Context(LogLevel level, string msg, [CallerMemberName] string memberName = "") + { + Level = level; + Message = msg; + m_CallingFunction = memberName; + + m_Other = GenericContext.Create(); + RelevantObjectOverride = null; + m_Prepend = null; + m_Postpend = null; + } + + internal Context(LogLevel level, string msg, bool noCaller) + { + Level = level; + Message = msg; + m_CallingFunction = null; + + m_Other = GenericContext.Create(); + RelevantObjectOverride = null; + m_Prepend = null; + m_Postpend = null; + } + + public void AppendTo(LogBuilder builder) + { + // [CallingFunction] + if (!string.IsNullOrEmpty(m_CallingFunction)) + { + builder.AppendTag(m_CallingFunction); + } + + + // [SomeContext][SomeName:SomeValue] + m_Other.AppendTo(builder); + + if (m_Prepend != null) + { + foreach (var context in m_Prepend) + { + context.AppendTo(builder); + } + } + + // Human-readable log message + builder.Append(" "); + builder.Append(Message); + + if (m_Postpend != null) + { + foreach (var context in m_Postpend) + { + context.AppendTo(builder); + } + } + } + + public Context AddInfo(object key, object value) + { + m_Other.StoreInfo(key, value); + return this; + } + + public Context AddTag(string msg) + { + m_Other.StoreTag(msg); + return this; + } + + public Context AddObject(Object obj) + { + RelevantObjectOverride = obj; + return this; + } + + public Context AddNetworkObject(NetworkObject networkObject) + { + AddPrepend(new LogContextNetworkObject(networkObject)); + RelevantObjectOverride = networkObject; + return this; + } + + public Context AddNetworkBehaviour(NetworkBehaviour networkBehaviour) + { + AddPrepend(new LogContextNetworkBehaviour(networkBehaviour)); + RelevantObjectOverride = networkBehaviour; + return this; + } + + public Context AddCollection(IEnumerable collection, LogCollectionBuilder builder) + { + AddPostpend(new CollectionContext(collection, builder)); + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddPrepend(ILogContext prepend) + { + if (m_Prepend == null) + { + m_Prepend = PreallocatedStore.GetPreallocated(); + } + m_Prepend.Add(prepend); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddPostpend(ILogContext postpend) + { + if (m_Postpend == null) + { + m_Postpend = PreallocatedStore.GetPreallocated(); + } + m_Postpend.Add(postpend); + } + + public void Dispose() + { + m_Other.Dispose(); + PreallocatedStore.Free(m_Prepend); + PreallocatedStore.Free(m_Postpend); + m_Prepend = null; + m_Postpend = null; + } + + private static class PreallocatedStore + { + private static readonly Queue> k_Preallocated = new(); + + internal static List GetPreallocated() + { + if (k_Preallocated.Count > 0) + { + k_Preallocated.Dequeue(); + } + + return new List(); + } + + internal static void Free(List collection) + { + collection.Clear(); + k_Preallocated.Enqueue(collection); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext.cs.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContext.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Logging/LogContext.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContext.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkBehaviour.cs new file mode 100644 index 0000000000..149fd001ff --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkBehaviour.cs @@ -0,0 +1,21 @@ +namespace Unity.Netcode.Logging +{ + internal readonly struct LogContextNetworkBehaviour : ILogContext + { + private readonly NetworkBehaviour m_NetworkBehaviour; + + public LogContextNetworkBehaviour(NetworkBehaviour networkBehaviour) + { + m_NetworkBehaviour = networkBehaviour; + } + + public void AppendTo(LogBuilder builder) + { + builder.AppendTag(m_NetworkBehaviour.gameObject.name); + if (m_NetworkBehaviour.IsSpawned) + { + builder.AppendInfo(nameof(NetworkBehaviour.NetworkBehaviourId), m_NetworkBehaviour.NetworkBehaviourId); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkBehaviour.cs.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkBehaviour.cs.meta new file mode 100644 index 0000000000..a4f9cae7ed --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkBehaviour.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ce875bd203c24cf1b916741eac3a55f8 +timeCreated: 1779217913 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContextNetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkManager.cs similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Logging/LogContextNetworkManager.cs rename to com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkManager.cs diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContextNetworkManager.cs.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkManager.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Logging/LogContextNetworkManager.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkManager.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkObject.cs new file mode 100644 index 0000000000..d7feb201b1 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkObject.cs @@ -0,0 +1,21 @@ +namespace Unity.Netcode.Logging +{ + internal readonly struct LogContextNetworkObject : ILogContext + { + private readonly NetworkObject m_NetworkObject; + + public LogContextNetworkObject(NetworkObject networkObject) + { + m_NetworkObject = networkObject; + } + + public void AppendTo(LogBuilder builder) + { + builder.AppendTag(m_NetworkObject.name); + if (m_NetworkObject.IsSpawned) + { + builder.AppendInfo(nameof(NetworkObject.NetworkObjectId), m_NetworkObject.NetworkObjectId); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkObject.cs.meta b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkObject.cs.meta new file mode 100644 index 0000000000..9c98d47b80 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Logging/LogContext/LogContextNetworkObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d6f400599d664e92a747913ca672e02e +timeCreated: 1779217114 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index 29a092e77d..66a7df1f96 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -1,18 +1,26 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Unity.Netcode.Logging; using UnityEngine; +using UnityEngine.Assertions; namespace Unity.Netcode { + /// + /// Log configuration containing : + /// - used in LogContextNetworkManager.cs + /// - used in SceneEventData.cs + /// internal struct LogConfiguration { internal bool LogNetworkManagerRole; + internal bool LogSerializationOrder; } /// - /// Helper class for logging + /// Helper class for logging. /// public static class NetworkLog { @@ -58,7 +66,7 @@ internal static void ConfigureIntegrationTestLogging(NetworkManager networkManag internal static void LogWarning(Context context) => s_Log.Warning(context); /// - /// Locally logs a error log with Netcode prefixing. + /// Locally logs an error log with Netcode prefixing. /// /// The message to log [HideInCallstack] @@ -95,6 +103,8 @@ internal static void ConfigureIntegrationTestLogging(NetworkManager networkManag /// The message to log [HideInCallstack] public static void LogErrorServer(string message) => s_Log.ErrorServer(new Context(LogLevel.Error, message, true)); + [HideInCallstack] + internal static void LogErrorServer(Context context) => s_Log.ErrorServer(context); internal static LogType GetMessageLogType(UnityEngine.LogType engineLogType) { @@ -107,14 +117,14 @@ internal static LogType GetMessageLogType(UnityEngine.LogType engineLogType) }; } - private const string k_SenderId = "SenderId"; internal static Context BuildContextForServerMessage([NotNull] NetworkManager networkManager, LogLevel level, ulong senderId, string message) { - var ctx = new Context(level, message, true).AddInfo(k_SenderId, senderId); - if (TryGetNetworkObjectName(networkManager, message, out var name)) + var ctx = new Context(level, message, true).AddTag("Received log from client").AddInfo(k_SenderId, senderId); + var networkObject = TryGetNetworkObject(networkManager, message); + if (networkObject != null) { - ctx.AddTag(name); + ctx.AddNetworkObject(networkObject); } return ctx; } @@ -127,30 +137,48 @@ internal enum LogType : byte None } - private static readonly Regex k_GlobalObjectIdHash = new($@"\[{nameof(NetworkObject.GlobalObjectIdHash)}=(\d+)\]", RegexOptions.Compiled); + private static readonly Regex k_NetworkObjectId = new($@"\[{nameof(NetworkObject.NetworkObjectId)}:(\d+)\]", RegexOptions.Compiled); + private static readonly Regex k_GlobalObjectIdHash = new($@"\[{nameof(NetworkObject.GlobalObjectIdHash)}:(\d+)\]", RegexOptions.Compiled); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryGetNetworkObjectName([NotNull] NetworkManager networkManager, string message, out string name) + private static NetworkObject TryGetNetworkObject([NotNull] NetworkManager networkManager, string message) { - name = null; + if (k_NetworkObjectId.IsMatch(message)) + { + var stringId = k_NetworkObjectId.Match(message).Groups[1].Value; + if (ulong.TryParse(stringId, out var networkObjectId) && networkObjectId > 0 && networkManager.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var networkObject)) + { + return networkObject; + } + } + if (!k_GlobalObjectIdHash.IsMatch(message)) { - return false; + return null; } var stringHash = k_GlobalObjectIdHash.Match(message).Groups[1].Value; if (!ulong.TryParse(stringHash, out var globalObjectIdHash)) { - return false; + return null; } - if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(globalObjectIdHash, out var networkObject)) + NetworkObject matchingObject = null; + foreach (var networkObject in networkManager.SpawnManager.SpawnedObjectsList) { - return false; + if (networkObject.GlobalObjectIdHash == globalObjectIdHash) + { + matchingObject = networkObject; + } } - name = networkObject.name; - return true; + return matchingObject; } + [HideInCallstack] + [Conditional("NETCODE_INTERNAL")] + internal static void InternalAssert(bool condition, string message) + { + Assert.IsTrue(condition, message); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index 15e30205f8..58a1f93470 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -434,7 +434,7 @@ public void SendNamedMessage(string messageName, IReadOnlyList clientIds, /// Exception thrown in case validation fails private unsafe void ValidateMessageSize(FastBufferWriter messageStream, NetworkDelivery networkDelivery, bool isNamed) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG var maxNonFragmentedSize = m_NetworkManager.MessageManager.NonFragmentedMessageMaxSize - FastBufferWriter.GetWriteSize() - sizeof(NetworkBatchHeader); if (isNamed) { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs index f05d000fec..282d3e4758 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DeferredMessageManager.cs @@ -96,6 +96,10 @@ public virtual unsafe void CleanupStaleTriggers() /// Used for testing purposes ///
internal static bool IncludeMessageType = true; +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => IncludeMessageType = true; +#endif private string GetWarningMessage(IDeferredNetworkMessageManager.TriggerType triggerType, ulong key, TriggerInfo triggerInfo, float spawnTimeout) { @@ -125,6 +129,16 @@ protected virtual void PurgeTrigger(IDeferredNetworkMessageManager.TriggerType t triggerInfo.TriggerData.Dispose(); } + public bool HasAnyOfTrigger(IDeferredNetworkMessageManager.TriggerType trigger) + { + if (m_Triggers.TryGetValue(trigger, out var triggers)) + { + return triggers.Count != 0; + } + + return false; + } + public virtual void ProcessTriggers(IDeferredNetworkMessageManager.TriggerType trigger, ulong key) { if (m_Triggers.TryGetValue(trigger, out var triggers)) @@ -143,6 +157,11 @@ public virtual void ProcessTriggers(IDeferredNetworkMessageManager.TriggerType t triggerInfo.TriggerData.Dispose(); } } + + if (trigger != IDeferredNetworkMessageManager.TriggerType.OnOtherTriggerFinishedProcessing) + { + ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnOtherTriggerFinishedProcessing, (ulong)trigger); + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs index 910021f606..569970b22f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs @@ -8,6 +8,10 @@ internal enum TriggerType OnSpawn, OnAddPrefab, OnNextFrame, +#if UNIFIED_NETCODE + OnGhostSpawned, +#endif + OnOtherTriggerFinishedProcessing, } /// @@ -28,6 +32,8 @@ internal enum TriggerType public void ProcessTriggers(TriggerType trigger, ulong key); + public bool HasAnyOfTrigger(TriggerType trigger); + /// /// Cleans up any trigger that's existed for more than a second. /// These triggers were probably for situations where a request was received after a despawn rather than before a spawn. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs index ec460cc821..8d8379dc05 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs @@ -53,6 +53,10 @@ internal struct ILPPMessageProvider : INetworkMessageProvider // Enable this for integration tests that need no message types defined internal static bool IntegrationTestNoMessages; +#if UNITY_EDITOR + [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => IntegrationTestNoMessages = false; +#endif /// /// Returns a table of message type to NetworkMessageTypes enum value @@ -144,6 +148,7 @@ internal static Dictionary GetMessageTypesMap() [InitializeOnLoadMethod] public static void NotifyOnPlayStateChange() { + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs index fe481d25f5..7417bd6b85 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -1,4 +1,3 @@ - namespace Unity.Netcode { /// @@ -45,8 +44,10 @@ internal interface INetworkMessage public int Version { get; } } - - internal static class MessageDeliveryType where T : INetworkMessage +#if UNITY_6000_6_OR_NEWER + [Scripting.LifecycleManagement.AutoStaticsCleanup] +#endif + internal static partial class MessageDeliveryType where T : INetworkMessage { internal static NetworkDelivery DefaultDelivery { get; private set; } internal static void Initialize() diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs index e8cf2ca6db..cc54d9d102 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageDelivery.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Unity.Netcode; -using UnityEditor; using UnityEngine; internal static class MessageDelivery @@ -15,17 +14,21 @@ internal static class MessageDelivery /// when sending the message via public API. /// - Skip the time sync messages since it has always used unreliable network delivery. /// - private static HashSet s_SkipMessageTypes = new HashSet(){ + private static readonly HashSet k_SkipMessageTypes = new HashSet(){ NetworkMessageTypes.NamedMessage, NetworkMessageTypes.Unnamed}; - [RuntimeInitializeOnLoadMethod] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void OnApplicationStart() { +#if UNITY_EDITOR + s_MessageToDelivery = new Dictionary(); + s_MessageToMessageType = new Dictionary(); +#endif UpdateMessageTypes(); } /// - /// FIrst pass at providing an easier path to configuring the network + /// First pass at providing an easier path to configuring the network /// delivery type for the message type. /// TODO: Once coalesces all reliable messages /// and/or organizes by a more unified order of operation tracking built into the @@ -40,7 +43,7 @@ private static void UpdateMessageTypes() foreach (var messageTypeObject in networkMessageTypes) { var messageType = (NetworkMessageTypes)messageTypeObject; - if (s_SkipMessageTypes.Contains(messageType)) + if (k_SkipMessageTypes.Contains(messageType)) { continue; } @@ -56,9 +59,13 @@ private static void UpdateMessageTypes() MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); MessageDeliveryType.Initialize(); + MessageDeliveryType.Initialize(); // RpcMessage.cs { MessageDeliveryType.Initialize(); @@ -71,18 +78,10 @@ private static void UpdateMessageTypes() MessageDeliveryType.Initialize(); } -#if UNITY_EDITOR - [InitializeOnLoadMethod] - [InitializeOnEnterPlayMode] - private static void OnEnterPlayMode() - { - UpdateMessageTypes(); - } -#endif internal static NetworkDelivery GetDelivery(Type type) { // Return the default if not registered or null - if (type == null || s_SkipMessageTypes.Contains(s_MessageToMessageType[type])) + if (type == null || k_SkipMessageTypes.Contains(s_MessageToMessageType[type])) { return NetworkDelivery.ReliableFragmentedSequenced; } @@ -91,7 +90,7 @@ internal static NetworkDelivery GetDelivery(Type type) internal static NetworkDelivery GetDelivery(NetworkMessageTypes messageType) { - if (s_SkipMessageTypes.Contains(messageType)) + if (k_SkipMessageTypes.Contains(messageType)) { throw new Exception($"{messageType} is not registered in the message type to network delivery map!"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs index 17669a335f..2a7eee8be7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -160,7 +160,7 @@ public void Serialize(FastBufferWriter writer, int targetVersion) { sobj.AddObserver(OwnerClientId); // In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized. - var serializedObject = sobj.Serialize(OwnerClientId, IsDistributedAuthority); + var serializedObject = sobj.SerializeSpawnedObject(OwnerClientId, IsDistributedAuthority); serializedObject.Serialize(writer); ++sceneObjectCount; } @@ -344,7 +344,7 @@ public void Handle(ref NetworkContext context) { var serializedObject = new NetworkObject.SerializedObject(); serializedObject.Deserialize(m_ReceivedSceneObjectData); - NetworkObject.Deserialize(serializedObject, m_ReceivedSceneObjectData, networkManager); + NetworkObject.DeserializeAndSpawnObject(serializedObject, m_ReceivedSceneObjectData, networkManager); } if (networkManager.DistributedAuthorityMode && networkManager.AutoSpawnPlayerPrefabClientSide) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs index cf518a6216..0238e0ddb7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Runtime.CompilerServices; +using Unity.Netcode.Logging; namespace Unity.Netcode { @@ -120,6 +121,25 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int ByteUnpacker.ReadValuePacked(reader, out NetworkObjectId); } +#if UNIFIED_NETCODE + // Leaving for debugging purposes + //if (networkManager.LogLevel == LogLevel.Developer) + //{ + // UnityEngine.Debug.Log($"Received {nameof(CreateObjectMessage)} for NetworkObjectId-{ObjectInfo.NetworkObjectId}."); + //} + + // For now, we will defer the create object message until the associated Ghost is spawned + if (ObjectInfo.HasGhost && !networkManager.SpawnManager.GhostSpawnManager.IsGhostPendingSpawn(ObjectInfo.NetworkObjectId)) + { + if (networkManager.LogLevel == LogLevel.Developer) + { + UnityEngine.Debug.Log($"[{nameof(NetworkObject)}-{ObjectInfo.NetworkObjectId}] Deferring {nameof(CreateObjectMessage)} to wait for Ghost."); + } + networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, ObjectInfo.NetworkObjectId, reader, ref context, k_Name); + return false; + } +#endif + if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo)) { networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Hash, reader, ref context, k_Name); @@ -171,7 +191,12 @@ internal static void CreateObject(ref NetworkManager networkManager, ulong sende { if (!networkManager.DistributedAuthorityMode) { - networkObject = NetworkObject.Deserialize(serializedObject, networkVariableData, networkManager); + networkObject = NetworkObject.DeserializeAndSpawnObject(serializedObject, networkVariableData, networkManager); + if (networkObject == null) + { + networkManager.Log.ErrorServer(new Context(LogLevel.Developer, $"Failed to deserialize {nameof(NetworkObject)}.").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), serializedObject.Hash).AddInfo(nameof(NetworkObject.NetworkObjectId), serializedObject.NetworkObjectId)); + return; + } } else { @@ -179,25 +204,27 @@ internal static void CreateObject(ref NetworkManager networkManager, ulong sende var hasNewObserverIdList = newObserverIds != null && newObserverIds.Length > 0; // Depending upon visibility of the NetworkObject and the client in question, it could be that // this client already has visibility of this NetworkObject - if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(serializedObject.NetworkObjectId)) + if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(serializedObject.NetworkObjectId, out networkObject)) { - // If so, then just get the local instance - networkObject = networkManager.SpawnManager.SpawnedObjects[serializedObject.NetworkObjectId]; - // This should not happen, logging error just in case if (hasNewObserverIdList && newObserverIds.Contains(networkManager.LocalClientId)) { NetworkLog.LogErrorServer($"[{nameof(CreateObjectMessage)}][Duplicate-Broadcast] Detected duplicated object creation for {serializedObject.NetworkObjectId}!"); } - else // Trap to make sure the owner is not receiving any messages it sent - if (networkManager.CMBServiceConnection && networkManager.LocalClientId == networkObject.OwnerClientId) + // Trap to make sure the owner is not receiving any messages it sent + else if (networkManager.CMBServiceConnection && networkManager.LocalClientId == networkObject.OwnerClientId) { NetworkLog.LogWarning($"[{nameof(CreateObjectMessage)}][Client-{networkManager.LocalClientId}][Duplicate-CreateObjectMessage][Client Is Owner] Detected duplicated object creation for {networkObject.name}-{serializedObject.NetworkObjectId}!"); } } else { - networkObject = NetworkObject.Deserialize(serializedObject, networkVariableData, networkManager, true); + networkObject = NetworkObject.DeserializeAndSpawnObject(serializedObject, networkVariableData, networkManager, true); + if (networkObject == null) + { + networkManager.Log.ErrorServer(new Context(LogLevel.Developer, $"Failed to deserialize {nameof(NetworkObject)}.").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), serializedObject.Hash).AddInfo(nameof(NetworkObject.NetworkObjectId), serializedObject.NetworkObjectId)); + return; + } } // DA - NGO CMB SERVICE NOTES: @@ -214,27 +241,6 @@ internal static void CreateObject(ref NetworkManager networkManager, ulong sende // Mock CMB Service and forward to all clients if (networkManager.DAHost) { - // DA - NGO CMB SERVICE NOTES: - // (*** See above notes fist ***) - // If it is a player object freshly spawning and one or more clients all connect at the exact same time (i.e. received on effectively - // the same frame), then we need to check the observers list to make sure all players are visible upon first spawning. At a later date, - // for area of interest we will need to have some form of follow up "observer update" message to cull out players not within each - // player's AOI. - if (networkObject.IsPlayerObject && hasNewObserverIdList && clientList.Count != observerIds.Length) - { - // For same-frame newly spawned players that might not be aware of all other players, update the player's observer - // list. - observerIds = clientList.ToArray(); - } - - var createObjectMessage = new CreateObjectMessage() - { - ObjectInfo = serializedObject, - m_ReceivedNetworkVariableData = networkVariableData, - ObserverIds = hasObserverIdList ? observerIds : null, - NetworkObjectId = networkObject.NetworkObjectId, - IncludesSerializedObject = true, - }; foreach (var clientId in clientList) { // DA - NGO CMB SERVICE NOTES: @@ -250,16 +256,12 @@ internal static void CreateObject(ref NetworkManager networkManager, ulong sende // If this included a list of new observers and the targeted clientId is one of the observers, then send the serialized data. // Otherwise, the targeted clientId has already has visibility (i.e. it is already spawned) and so just send the updated // observers list to that client's instance. - createObjectMessage.IncludesSerializedObject = hasNewObserverIdList && newObserverIds.Contains(clientId); - networkManager.SpawnManager.SendSpawnCallForObject(clientId, networkObject); } } } - if (networkObject != null) - { - networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject, messageSize); - } + + networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject, messageSize); } catch (System.Exception ex) { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs index 39daa80c39..e0d4d4a2bc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs @@ -118,29 +118,42 @@ public void Handle(ref NetworkContext context) // DANGO-TODO: Still determining if we should not apply this change (I am leaning towards not allowing it). } + networkObject.SetNetworkParenting(LatestParent, WorldPositionStays); networkObject.ApplyNetworkParenting(RemoveParent); - // This check is primarily for client-server network topologies when the motion model is owner authoritative: - // When SyncOwnerTransformWhenParented is enabled, then always apply the transform values. - // When SyncOwnerTransformWhenParented is disabled, then only synchronize the transform on non-owner instances. - if (networkObject.SyncOwnerTransformWhenParented || (!networkObject.SyncOwnerTransformWhenParented && !networkObject.IsOwner)) +#if UNIFIED_NETCODE + if (networkObject.HasGhost) { - // We set all of the transform values after parenting as they are - // the values of the server-side post-parenting transform values - if (!WorldPositionStays) - { - networkObject.transform.SetLocalPositionAndRotation(Position, Rotation); - } - else + // Handles the GhostObject side of things for parenting + networkObject.NetworkObjectBridge.HybridParentUpdate(Scale); + } + else +#endif + { + // This check is primarily for client-server network topologies when the motion model is owner authoritative: + // When SyncOwnerTransformWhenParented is enabled, then always apply the transform values. + // When SyncOwnerTransformWhenParented is disabled, then only synchronize the transform on non-owner instances. + if (networkObject.SyncOwnerTransformWhenParented || (!networkObject.SyncOwnerTransformWhenParented && !networkObject.IsOwner)) { - networkObject.transform.SetPositionAndRotation(Position, Rotation); + + // We set all of the transform values after parenting as they are + // the values of the server-side post-parenting transform values + if (!WorldPositionStays) + { + networkObject.transform.SetLocalPositionAndRotation(Position, Rotation); + } + else + { + networkObject.transform.SetPositionAndRotation(Position, Rotation); + } + networkObject.transform.localScale = Scale; } - networkObject.transform.localScale = Scale; } // If in distributed authority mode and we are running a DAHost and this is the DAHost, then forward the parent changed message to any remaining clients - if ((networkManager.DistributedAuthorityMode && !networkManager.CMBServiceConnection && networkManager.DAHost) || (networkObject.AllowOwnerToParent && context.SenderId == networkObject.OwnerClientId && networkManager.IsServer)) + if ((networkManager.DistributedAuthorityMode && !networkManager.CMBServiceConnection && networkManager.DAHost) + || (networkObject.AllowOwnerToParent && context.SenderId == networkObject.OwnerClientId && networkManager.IsServer)) { var size = 0; var message = this; diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs index c8be0c251d..b7f1320788 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs @@ -1,6 +1,6 @@ using System; using Unity.Collections; -using UnityEngine; +using Unity.Netcode.Logging; namespace Unity.Netcode { @@ -16,80 +16,79 @@ public static unsafe void Serialize(ref FastBufferWriter writer, ref RpcMetadata public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload, string messageType) { + var networkManager = (NetworkManager)context.SystemOwner; ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkObjectId); - ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkBehaviourId); - ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkRpcMethodId); - var networkManager = (NetworkManager)context.SystemOwner; - if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId)) + if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(metadata.NetworkObjectId, out var networkObject)) { + networkManager.Log.Info(new Context(LogLevel.Developer, $"Received RPC message for {nameof(NetworkObject)} that doesn't exist yet. Deferring the message.").AddInfo("SenderClientId", context.SenderId).AddInfo(nameof(NetworkObject.NetworkObjectId), metadata.NetworkObjectId).AddInfo(nameof(RpcMetadata.NetworkRpcMethodId), metadata.NetworkRpcMethodId)); networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, metadata.NetworkObjectId, reader, ref context, messageType); return false; } - var networkObject = networkManager.SpawnManager.SpawnedObjects[metadata.NetworkObjectId]; - var networkBehaviour = networkManager.SpawnManager.SpawnedObjects[metadata.NetworkObjectId].GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId); - if (networkBehaviour == null) - { - return false; - } - - if (!NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()].ContainsKey(metadata.NetworkRpcMethodId)) - { - return false; - } + ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkBehaviourId); + ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkRpcMethodId); payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) - networkBehaviour.TrackRpcMetricsReceive(ref metadata, ref context, reader.Length); -#endif return true; } public static void Handle(ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload, ref __RpcParams rpcParams) { var networkManager = (NetworkManager)context.SystemOwner; + if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(metadata.NetworkObjectId, out var networkObject)) { // If the NetworkObject no longer exists then just log a warning when developer mode logging is enabled and exit. // This can happen if NetworkObject is despawned and a client sends an RPC before receiving the despawn message. - if (networkManager.LogLevel == LogLevel.Developer) - { - NetworkLog.LogWarning($"[{metadata.NetworkObjectId}, {metadata.NetworkBehaviourId}, {metadata.NetworkRpcMethodId}] An RPC called on a {nameof(NetworkObject)} that is not in the spawned objects list. Please make sure the {nameof(NetworkObject)} is spawned before calling RPCs."); - } - + networkManager.Log.Warning(new Context(LogLevel.Developer, $"Received RPC message for {nameof(NetworkObject)} that doesn't exist yet. Deferring the message.").AddInfo(nameof(NetworkObject.NetworkObjectId), metadata.NetworkObjectId).AddInfo(nameof(RpcMetadata.NetworkRpcMethodId), metadata.NetworkRpcMethodId)); return; } var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId); - try + if (networkBehaviour == null) + { + networkManager.Log.Error(new Context(LogLevel.Normal, $"Received RPC message for {nameof(NetworkBehaviour)} that doesn't exist. Dropping RPC message").AddNetworkObject(networkObject).AddInfo(nameof(NetworkBehaviour.NetworkBehaviourId), networkBehaviour.NetworkBehaviourId)); + return; + } + + var type = networkBehaviour.GetType(); + if (!NetworkBehaviour.__rpc_func_table.TryGetValue(type, out var rpcsForBehaviour) || !NetworkBehaviour.__rpc_permission_table.TryGetValue(type, out var permissionsTable)) + { + networkManager.Log.Error(new Context(LogLevel.Normal, $"Rpc table doesn't have RPCs registered for this {nameof(NetworkBehaviour)}. Dropping RPC message").AddNetworkObject(networkObject).AddInfo(nameof(NetworkBehaviour.NetworkBehaviourId), networkBehaviour.NetworkBehaviourId).AddInfo(nameof(NetworkBehaviour), type)); + return; + } + if (!rpcsForBehaviour.TryGetValue(metadata.NetworkRpcMethodId, out var receiveHandler) || !permissionsTable.TryGetValue(metadata.NetworkRpcMethodId, out var permission)) { - var permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId]; + networkManager.Log.Error(new Context(LogLevel.Normal, "Received RPC message for RPC receiver that doesn't exist. Dropping RPC message").AddNetworkBehaviour(networkBehaviour).AddInfo(nameof(RpcMetadata.NetworkRpcMethodId), metadata.NetworkRpcMethodId)); + return; + } - if ((permission == RpcInvokePermission.Server && rpcParams.SenderId != NetworkManager.ServerClientId) || - (permission == RpcInvokePermission.Owner && rpcParams.SenderId != networkObject.OwnerClientId)) - { - if (networkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogErrorServer($"Rpc message received from client-{rpcParams.SenderId} who does not have permission to perform this operation!"); - } - return; - } + if ((permission == RpcInvokePermission.Server && rpcParams.SenderId != NetworkManager.ServerClientId) || + (permission == RpcInvokePermission.Owner && rpcParams.SenderId != networkObject.OwnerClientId)) + { + networkManager.Log.ErrorServer(new Context(LogLevel.Normal, "Rpc message received from a client without permission to perform this operation!. Dropping RPC message").AddNetworkBehaviour(networkBehaviour)); + return; + } - NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams); +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) + networkBehaviour.TrackRpcMetricsReceive(ref metadata, ref context, payload.Length); +#endif + + try + { + receiveHandler(networkBehaviour, payload, rpcParams); } catch (Exception ex) { - Debug.LogException(new Exception($"Unhandled RPC exception!", ex)); - if (networkManager.LogLevel <= LogLevel.Developer) + networkManager.Log.Exception(ex, new Context(LogLevel.Error, "Unhandled RPC exception!").AddNetworkBehaviour(networkBehaviour)); + + var methodId = metadata.NetworkRpcMethodId; + networkManager.Log.Info(new Context(LogLevel.Developer, "RPC Table Contents").AddCollection(rpcsForBehaviour, entry => { - Debug.Log($"RPC Table Contents"); - foreach (var entry in NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()]) - { - var permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId]; - Debug.Log($"{entry.Key} | {entry.Value.Method.Name} | {permission}"); - } - } + var invokePermission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][methodId]; + return $"{entry.Key} | {entry.Value.Method.Name} | {invokePermission}"; + })); } } } @@ -270,12 +269,12 @@ public void Handle(ref NetworkContext context) } catch (Exception ex) { - Debug.LogException(ex); + networkManager.Log.Exception(ex); } } else { - NetworkLog.LogErrorServer($"Received {nameof(ForwardServerRpcMessage)} on client-{networkManager.LocalClientId}! Only DAHost may forward RPC messages!"); + networkManager.Log.ErrorServer(new Context(LogLevel.Error, $"Received {nameof(ForwardServerRpcMessage)} when not the DAHost! Only DAHost may forward RPC messages!").AddInfo("SenderClientId", context.SenderId)); } ServerRpcMessage.ReadBuffer.Dispose(); ServerRpcMessage.WriteBuffer.Dispose(); @@ -351,7 +350,7 @@ public void Handle(ref NetworkContext context) } else { - NetworkLog.LogErrorServer($"Received {nameof(ForwardClientRpcMessage)} on client-{networkManager.LocalClientId}! Only DAHost may forward RPC messages!"); + networkManager.Log.ErrorServer(new Context(LogLevel.Error, $"Received {nameof(ForwardClientRpcMessage)} when not the DAHost! Only DAHost may forward RPC messages!").AddInfo("SenderClientId", context.SenderId)); } ClientRpcMessage.WriteBuffer.Dispose(); ClientRpcMessage.ReadBuffer.Dispose(); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs index 25c53a2786..901b421cdb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs @@ -1,4 +1,3 @@ - namespace Unity.Netcode { // Todo: Would be lovely to get this one nicely formatted with all the data it sends in the struct @@ -9,6 +8,7 @@ internal struct SceneEventMessage : INetworkMessage public SceneEventData EventData; + private const string k_Name = "SceneEventMessage"; private FastBufferReader m_ReceivedData; @@ -19,6 +19,19 @@ public void Serialize(FastBufferWriter writer, int targetVersion) public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion) { + var networkManager = (NetworkManager)context.SystemOwner; +#if UNIFIED_NETCODE + // Defer this message if the OnGhostSpawned trigger is still being processed. This is because the scene event message can be sent + // as part of the ghost spawning process and we want to make sure that all ghost spawning related messages are processed before we + // process this one. This is to avoid any potential issues with the order of message processing and to ensure that all ghost + // related messages are processed before we process this one. + if (networkManager.DeferredMessageManager.HasAnyOfTrigger(IDeferredNetworkMessageManager.TriggerType + .OnGhostSpawned)) + { + networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnOtherTriggerFinishedProcessing, (ulong)IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, reader, ref context, k_Name); + return false; + } +#endif m_ReceivedData = reader; return true; } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs index c66aa62273..343cec9961 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs @@ -34,9 +34,9 @@ public InvalidMessageStructureException(string issue) : base(issue) internal class NetworkMessageManager : IDisposable { public bool StopProcessing = false; - private static Type s_ConnectionApprovedType = typeof(ConnectionApprovedMessage); - private static Type s_ConnectionRequestType = typeof(ConnectionRequestMessage); - private static Type s_DisconnectReasonType = typeof(DisconnectReasonMessage); + private static readonly Type k_ConnectionApprovedType = typeof(ConnectionApprovedMessage); + private static readonly Type k_ConnectionRequestType = typeof(ConnectionRequestMessage); + private static readonly Type k_DisconnectReasonType = typeof(DisconnectReasonMessage); private struct ReceiveQueueItem { @@ -149,8 +149,6 @@ public NetworkMessageManager(INetworkMessageSender sender, object owner, INetwor } } - internal static bool EnableMessageOrderConsoleLog = false; - public void Dispose() { if (m_Disposed) @@ -549,7 +547,7 @@ internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = fals // Special cases because these are the messages that carry the version info - thus the version info isn't // populated yet when we get these. The first part of these messages always has to be the version data // and can't change. - if (messageType != s_ConnectionRequestType && messageType != s_ConnectionApprovedType && messageType != s_DisconnectReasonType && context.SenderId != manager.m_LocalClientId) + if (messageType != k_ConnectionRequestType && messageType != k_ConnectionApprovedType && messageType != k_DisconnectReasonType && context.SenderId != manager.m_LocalClientId) { messageVersion = manager.GetMessageVersion(messageType, context.SenderId, true); if (messageVersion < 0) @@ -603,7 +601,7 @@ internal int SendMessage(ref TMessageType messa var messageVersion = 0; // Special case because this is the message that carries the version info - thus the version info isn't populated yet when we get this. // The first part of this message always has to be the version data and can't change. - if (typeof(TMessageType) != s_ConnectionRequestType) + if (typeof(TMessageType) != k_ConnectionRequestType) { messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]); if (messageVersion < 0) @@ -657,7 +655,7 @@ internal unsafe int SendPreSerializedMessage(in FastBufferWriter t // Special case because this is the message that carries the version info - thus the version info isn't populated yet when we get this. // The first part of this message always has to be the version data and can't change. - if (typeof(TMessageType) != s_ConnectionRequestType) + if (typeof(TMessageType) != k_ConnectionRequestType) { var messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]); if (messageVersion < 0) @@ -741,7 +739,7 @@ internal unsafe int SendPreSerializedMessage(in FastBufferWriter t // Special case because this is the message that carries the version info - thus the version info isn't // populated yet when we get this. The first part of this message always has to be the version data // and can't change. - if (typeof(TMessageType) != s_ConnectionRequestType) + if (typeof(TMessageType) != k_ConnectionRequestType) { messageVersion = GetMessageVersion(typeof(TMessageType), clientId); if (messageVersion < 0) @@ -851,7 +849,7 @@ internal unsafe void ProcessSendQueues() } queueItem.Writer.Seek(0); -#if UNITY_EDITOR || DEVELOPMENT_BUILD +#if DEBUG // Skipping the Verify and sneaking the write mark in because we know it's fine. queueItem.Writer.Handle->AllowedWriteMark = sizeof(NetworkBatchHeader); #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/BaseRpcTarget.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/BaseRpcTarget.cs index a1e12a5310..a1b5b5d8aa 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/BaseRpcTarget.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/BaseRpcTarget.cs @@ -53,7 +53,7 @@ protected void CheckLockBeforeDispose() private protected void SendMessageToClient(NetworkBehaviour behaviour, ulong clientId, ref RpcMessage message, NetworkDelivery delivery) { var size = behaviour.NetworkManager.MessageManager.SendMessage(ref message, delivery, clientId); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) // Send to a specific client behaviour.TrackRpcMetricsSend(clientId, ref message, size); #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/LocalSendRpcTarget.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/LocalSendRpcTarget.cs index 386af8816f..4afa621d11 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/LocalSendRpcTarget.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/LocalSendRpcTarget.cs @@ -46,7 +46,7 @@ internal override void Send(NetworkBehaviour behaviour, ref RpcMessage message, message.Handle(ref context); length = tempBuffer.Length; } -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) // Local invocation sends to self behaviour.TrackRpcMetricsSend(m_NetworkManager.LocalClientId, ref message, length); #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ProxyRpcTargetGroup.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ProxyRpcTargetGroup.cs index 8dd2e744eb..40321b3742 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ProxyRpcTargetGroup.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ProxyRpcTargetGroup.cs @@ -24,7 +24,7 @@ internal override void Send(NetworkBehaviour behaviour, ref RpcMessage message, } var proxyMessage = new ProxyMessage { Delivery = delivery, TargetClientIds = TargetClientIds.AsArray(), WrappedMessage = message }; var size = behaviour.NetworkManager.MessageManager.SendMessage(ref proxyMessage, delivery, NetworkManager.ServerClientId); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) foreach (var clientId in TargetClientIds) { behaviour.TrackRpcMetricsSend(clientId, ref message, size); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs index 63380ce40c..7382b41153 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs @@ -36,7 +36,7 @@ internal override void Send(NetworkBehaviour behaviour, ref RpcMessage message, using var tempBuffer = new FastBufferReader(message.WriteBuffer, Allocator.None); message.ReadBuffer = tempBuffer; message.Handle(ref context); -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) // Local invocation sends to self behaviour.TrackRpcMetricsSend(m_NetworkManager.LocalClientId, ref message, tempBuffer.Length); #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs index b171932103..4bb7bb20a0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs @@ -11,26 +11,22 @@ namespace Unity.Netcode internal class NetworkMetrics : INetworkMetrics { private const ulong k_MaxMetricsPerFrame = 1000L; - private static Dictionary s_SceneEventTypeNames; - private static ProfilerMarker s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); + private static readonly Dictionary k_SceneEventTypeNames; + private static readonly ProfilerMarker k_FrameDispatch; static NetworkMetrics() { - s_SceneEventTypeNames = new Dictionary(); + k_SceneEventTypeNames = new Dictionary(); foreach (SceneEventType type in Enum.GetValues(typeof(SceneEventType))) { - s_SceneEventTypeNames[(uint)type] = type.ToString(); + k_SceneEventTypeNames[(uint)type] = type.ToString(); } + k_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); } private static string GetSceneEventTypeName(uint typeCode) { - if (!s_SceneEventTypeNames.TryGetValue(typeCode, out string name)) - { - name = "Unknown"; - } - - return name; + return k_SceneEventTypeNames.GetValueOrDefault(typeCode, "Unknown"); } private readonly Counter m_TransportBytesSent = new Counter(NetworkMetricTypes.TotalBytesSent.Id) @@ -511,9 +507,9 @@ public void UpdatePacketLoss(float packetLoss) public void DispatchFrame() { - s_FrameDispatch.Begin(); + k_FrameDispatch.Begin(); Dispatcher.Dispatch(); - s_FrameDispatch.End(); + k_FrameDispatch.End(); m_NumberOfMetricsThisFrame = 0; } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index aafeeb2ab4..9c0cb4bd83 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -683,14 +683,8 @@ private void HandleAddListEvent(NetworkListEvent listEvent) /// /// This method should not be used. It is left over from a previous interface /// - public int LastModifiedTick - { - get - { - // todo: implement proper network tick for NetworkList - return NetworkTickSystem.NoTick; - } - } + [Obsolete("This property is no longer used and will be removed in a future version.")] + public int LastModifiedTick => NetworkTickSystem.NoTick; /// /// Overridden implementation. diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs index d9327f2441..7c1769d9d8 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/CollectionSerializationUtility.cs @@ -6,7 +6,7 @@ namespace Unity.Netcode { - internal static class CollectionSerializationUtility + internal static partial class CollectionSerializationUtility { public static void WriteNativeArrayDelta(FastBufferWriter writer, ref NativeArray value, ref NativeArray previousValue) where T : unmanaged { @@ -260,24 +260,24 @@ public static void ReadListDelta(FastBufferReader reader, ref List value) // so we're going to keep static lists that we can reuse in these methods. private static class ListCache { - private static List s_AddedList = new List(); - private static List s_RemovedList = new List(); - private static List s_ChangedList = new List(); + private static readonly List k_AddedList = new List(); + private static readonly List k_RemovedList = new List(); + private static readonly List k_ChangedList = new List(); public static List GetAddedList() { - s_AddedList.Clear(); - return s_AddedList; + k_AddedList.Clear(); + return k_AddedList; } public static List GetRemovedList() { - s_RemovedList.Clear(); - return s_RemovedList; + k_RemovedList.Clear(); + return k_RemovedList; } public static List GetChangedList() { - s_ChangedList.Clear(); - return s_ChangedList; + k_ChangedList.Clear(); + return k_ChangedList; } } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs index 1a00d2c86a..0c5528ca66 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/NetworkVariableSerialization.cs @@ -12,6 +12,8 @@ namespace Unity.Netcode [Serializable] public static class NetworkVariableSerialization { + // This is all setup in ILPP (in the file NetworkBehaviorILPP), using the functions in TypedILPPInitializers. + // There is no need to reset statics here. internal static INetworkVariableSerializer Serializer = new FallbackSerializer(); /// diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedSerializerImplementations.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedSerializerImplementations.cs index e1242378dd..00dd2978b6 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedSerializerImplementations.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/TypedSerializerImplementations.cs @@ -243,11 +243,23 @@ public void Read(FastBufferReader reader, ref T value) public void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue) { + if (UserNetworkVariableSerialization.WriteDelta != null && UserNetworkVariableSerialization.ReadDelta != null) + { + UserNetworkVariableSerialization.WriteDelta(writer, value, previousValue); + return; + } + Write(writer, ref value); } public void ReadDelta(FastBufferReader reader, ref T value) { + if (UserNetworkVariableSerialization.WriteDelta != null && UserNetworkVariableSerialization.ReadDelta != null) + { + UserNetworkVariableSerialization.ReadDelta(reader, ref value); + return; + } + Read(reader, ref value); } @@ -258,6 +270,12 @@ void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, ou public void Duplicate(in T value, ref T duplicatedValue) { + if (UserNetworkVariableSerialization.DuplicateValue != null) + { + UserNetworkVariableSerialization.DuplicateValue(value, ref duplicatedValue); + return; + } + duplicatedValue = value; } } @@ -959,6 +977,12 @@ void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, ou public void Duplicate(in T value, ref T duplicatedValue) { + if (UserNetworkVariableSerialization.DuplicateValue != null) + { + UserNetworkVariableSerialization.DuplicateValue(value, ref duplicatedValue); + return; + } + duplicatedValue = value; } } @@ -1128,6 +1152,12 @@ void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, ou public void Duplicate(in T value, ref T duplicatedValue) { + if (UserNetworkVariableSerialization.DuplicateValue != null) + { + UserNetworkVariableSerialization.DuplicateValue(value, ref duplicatedValue); + return; + } + using var writer = new FastBufferWriter(256, Allocator.Temp, int.MaxValue); var refValue = value; Write(writer, ref refValue); diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs index 3f2a64585c..fe6a7acbba 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Serialization/UserNetworkVariableSerialization.cs @@ -7,7 +7,10 @@ namespace Unity.Netcode /// users to tell NetworkVariable about those extension methods (or simply pass in a lambda) /// /// The type of value being serialized - public class UserNetworkVariableSerialization +#if UNITY_6000_6_OR_NEWER + [Scripting.LifecycleManagement.AutoStaticsCleanup] +#endif + public partial class UserNetworkVariableSerialization { /// /// The write value delegate handler definition diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs index 5a11a01b4a..f7ce87d521 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs @@ -206,7 +206,7 @@ public void PopulateLoadedScenes(ref Dictionary scene /// Unloads any scenes that have not been assigned. /// /// - public void UnloadUnassignedScenes(NetworkManager networkManager = null) + public void UnloadUnassignedScenes(NetworkManager networkManager) { var sceneManager = networkManager.SceneManager; SceneManager.sceneUnloaded += SceneManager_SceneUnloaded; @@ -311,7 +311,7 @@ public void MoveObjectsFromSceneToDontDestroyOnLoad(ref NetworkManager networkMa if (!networkObject.DestroyWithScene && networkObject.gameObject.scene != networkManager.SceneManager.DontDestroyOnLoadScene) { // Only move dynamically spawned NetworkObjects with no parent as the children will follow - if (networkObject.gameObject.transform.parent == null && networkObject.IsSceneObject != null && !networkObject.IsSceneObject.Value) + if (networkObject.gameObject.transform.parent == null && !networkObject.InScenePlaced) { UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject); } @@ -359,8 +359,8 @@ public void SetClientSynchronizationMode(ref NetworkManager networkManager, Load } return; } - else // Warn users if they are changing this after there are clients already connected and synchronized - if (!networkManager.DistributedAuthorityMode && networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode) + // Warn users if they are changing this after there are clients already connected and synchronized + else if (!networkManager.DistributedAuthorityMode && networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs index e544eb1f24..f57414bcc5 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs @@ -34,7 +34,6 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade else { var reader = serializer.GetFastBufferReader(); - // DANGO-TODO Rust needs to be updated to either handle this ulong or to remove the scene store. #if SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG reader.ReadValueSafe(out ulong rawData); m_Handle = SceneHandle.FromRawData(rawData); @@ -87,6 +86,11 @@ internal NetworkSceneHandle(int handle, bool asMock) public int GetRawData() => m_Handle; #endif + public override string ToString() + { + return m_Handle.ToString(); + } + #region Implicit conversions #if SCENE_MANAGEMENT_SCENE_HANDLE_AVAILABLE /// diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 2fbdf1fbe9..b0db598848 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -5,6 +5,7 @@ using Unity.Collections; using UnityEngine; using UnityEngine.SceneManagement; +using Debug = UnityEngine.Debug; namespace Unity.Netcode @@ -142,7 +143,7 @@ public class SceneEvent /// /// Main class for managing network scenes when is enabled. - /// Uses the message to communicate between the server and client(s) + /// Uses the SceneEventMessage message to communicate SceneEventData between the server and client(s) /// [Serializable] public class NetworkSceneManager : IDisposable @@ -161,8 +162,7 @@ public class NetworkSceneManager : IDisposable /// /// The delegate callback definition for scene event notifications.
/// See also:
- ///
- /// + /// ///
/// SceneEvent which contains information about the scene event, including type, progress, and scene details public delegate void SceneEventDelegate(SceneEvent sceneEvent); @@ -548,6 +548,15 @@ internal bool RemoveServerClientSceneHandle(NetworkSceneHandle serverHandle, Net /// not destroy temporary scene are moved into the active scene ///
internal static bool IsSpawnedObjectsPendingInDontDestroyOnLoad; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + DisableReSynchronization = false; + IsSpawnedObjectsPendingInDontDestroyOnLoad = false; + SceneUnloadEventHandler.ResetInstances(); + } +#endif /// /// Client and Server: @@ -590,7 +599,7 @@ internal bool HasSceneAuthority() } /// - /// Handle NetworkSeneManager clean up + /// Handle NetworkSceneManager clean up /// public void Dispose() { @@ -1025,7 +1034,7 @@ internal void SetTheSceneBeingSynchronized(NetworkSceneHandle serverSceneHandle) // Most common scenario for DontDestroyOnLoad is when NetworkManager is set to not be destroyed if (serverSceneHandle == DontDestroyOnLoadScene.handle) { - SceneBeingSynchronized = NetworkManager.gameObject.scene; + SceneBeingSynchronized = DontDestroyOnLoadScene; return; } else @@ -1570,6 +1579,9 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc internal class SceneUnloadEventHandler { private static Dictionary> s_Instances = new Dictionary>(); +#if UNITY_EDITOR + internal static void ResetInstances() => s_Instances = new Dictionary>(); +#endif internal static void RegisterScene(NetworkSceneManager networkSceneManager, Scene scene, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation = null) { @@ -1939,6 +1951,21 @@ private void OnClientLoadedScene(uint sceneEventId, Scene scene) /// internal List ClientConnectionQueue = new List(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddSceneToClientSynchronization(ref SceneEventData sceneEventData, ref Scene scene) + { + // If we are just a normal client and in distributed authority mode, then always use the known server scene handle + if (NetworkManager.DistributedAuthorityMode && NetworkManager.CMBServiceConnection) + { + sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), ClientSceneHandleToServerSceneHandle[scene.handle]); + } + else + { + sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), scene.handle); + } + } + /// /// Server Side: /// This is used for players that have just had their connection approved and will assure they are synchronized @@ -1989,61 +2016,53 @@ internal void SynchronizeNetworkObjects(ulong clientId, bool synchronizingServic // Organize how (and when) we serialize our NetworkObjects var hasSynchronizedActive = false; - for (int i = 0; i < SceneManager.sceneCount; i++) - { - var scene = SceneManager.GetSceneAt(i); - // NetworkSceneManager does not synchronize scenes that are not loaded by NetworkSceneManager - // unless the scene in question is the currently active scene. - if (ExcludeSceneFromSychronization != null && !ExcludeSceneFromSychronization(scene)) + // It is possible a user might not want to synchronize the active scene, so we will check to see if it is valid before adding it to the synchronization list. + // !! Important !! + // The active scene MUST always be the first scene in the synchronization list. + if (ValidateSceneBeforeLoading(activeScene.buildIndex, activeScene.name, sceneEventData.LoadSceneMode)) + { + sceneEventData.SceneHash = SceneHashFromNameOrPath(activeScene.path); + if (sceneEventData.SceneHash == sceneEventData.ActiveSceneHash) { - continue; + hasSynchronizedActive = true; } - if (scene == DontDestroyOnLoadScene) + // If we are just a normal client, then always use the server scene handle + if (NetworkManager.DistributedAuthorityMode) { - continue; + sceneEventData.SenderClientId = NetworkManager.LocalClientId; + sceneEventData.SceneHandle = ClientSceneHandleToServerSceneHandle[activeScene.handle]; } - - // This would depend upon whether we are additive or not - // If we are the base scene, then we set the root scene index; - if (activeScene == scene) + else { - if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, sceneEventData.LoadSceneMode)) - { - continue; - } - sceneEventData.SceneHash = SceneHashFromNameOrPath(scene.path); - if (sceneEventData.SceneHash == sceneEventData.ActiveSceneHash) - { - hasSynchronizedActive = true; - } - - // If we are just a normal client, then always use the server scene handle - if (NetworkManager.DistributedAuthorityMode) - { - sceneEventData.SenderClientId = NetworkManager.LocalClientId; - sceneEventData.SceneHandle = ClientSceneHandleToServerSceneHandle[scene.handle]; - } - else - { - sceneEventData.SceneHandle = scene.handle; - } + sceneEventData.SceneHandle = activeScene.handle; } - else if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, LoadSceneMode.Additive)) + AddSceneToClientSynchronization(ref sceneEventData, ref activeScene); + } + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + // Skip adding the active scene at this point as we are just adding all other additively loaded scenes to the synchronization list. + // Skip adding the dont destroy on load scene as that is never synchronized. + if ((scene.handle == activeScene.handle) || (scene == DontDestroyOnLoadScene)) { continue; } - // If we are just a normal client and in distributed authority mode, then always use the known server scene handle - if (NetworkManager.DistributedAuthorityMode && NetworkManager.CMBServiceConnection) + // NetworkSceneManager does not synchronize scenes that are not loaded by NetworkSceneManager + // unless the scene in question is the currently active scene. + if (ExcludeSceneFromSychronization != null && !ExcludeSceneFromSychronization(scene)) { - sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), ClientSceneHandleToServerSceneHandle[scene.handle]); + continue; } - else + + if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, LoadSceneMode.Additive)) { - sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), scene.handle); + continue; } + AddSceneToClientSynchronization(ref sceneEventData, ref scene); } if (!hasSynchronizedActive && NetworkManager.CMBServiceConnection && synchronizingService) @@ -2096,9 +2115,12 @@ private void OnClientBeginSync(uint sceneEventId) var sceneHash = sceneEventData.GetNextSceneSynchronizationHash(); var sceneHandle = sceneEventData.GetNextSceneSynchronizationHandle(); var sceneName = SceneNameFromHash(sceneHash); + var activeSceneName = SceneNameFromHash(sceneEventData.ActiveSceneHash); var activeScene = SceneManager.GetActiveScene(); - var loadSceneMode = sceneHash == sceneEventData.SceneHash ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive; + var activeSceneLoaded = activeSceneName == activeScene.name; + + var loadSceneMode = sceneHash == sceneEventData.SceneHash && !activeSceneLoaded ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive; // Store the sceneHandle and hash sceneEventData.NetworkSceneHandle = sceneHandle; @@ -2234,12 +2256,10 @@ private void SynchronizeNetworkObjectScene() // This is only done for dynamically spawned NetworkObjects // Theoretically, a server could have NetworkObjects in a server-side only scene, if the client doesn't have that scene loaded // then skip it (it will reside in the currently active scene in this scenario on the client-side) - if (networkObject.IsSceneObject.Value == false && ServerSceneHandleToClientSceneHandle.ContainsKey(networkObject.NetworkSceneHandle)) + if (!networkObject.InScenePlaced && ServerSceneHandleToClientSceneHandle.ContainsKey(networkObject.NetworkSceneHandle)) { networkObject.SceneOriginHandle = ServerSceneHandleToClientSceneHandle[networkObject.NetworkSceneHandle]; - - // If the NetworkObject does not have a parent and is not in the same scene as it is on the server side, then find the right scene // and move it to that scene. if (networkObject.gameObject.scene.handle != networkObject.SceneOriginHandle && networkObject.transform.parent == null) @@ -2247,11 +2267,6 @@ private void SynchronizeNetworkObjectScene() if (ScenesLoaded.ContainsKey(networkObject.SceneOriginHandle)) { var scene = ScenesLoaded[networkObject.SceneOriginHandle]; - if (scene == DontDestroyOnLoadScene) - { - Debug.Log($"{networkObject.gameObject.name} migrating into DDOL!"); - } - SceneManager.MoveGameObjectToScene(networkObject.gameObject, scene); } else if (NetworkManager.LogLevel <= LogLevel.Normal) @@ -2709,7 +2724,7 @@ internal void MoveObjectsToDontDestroyOnLoad() if (!networkObject.DestroyWithScene) { // Only move dynamically spawned NetworkObjects with no parent as the children will follow - if (networkObject.gameObject.transform.parent == null && networkObject.IsSceneObject != null && !networkObject.IsSceneObject.Value) + if (networkObject.gameObject.transform.parent == null && !networkObject.InScenePlaced) { UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject); // When temporarily migrating to the DDOL, adjust the network and origin scene handles so no messages are generated @@ -2721,10 +2736,9 @@ internal void MoveObjectsToDontDestroyOnLoad() else if (networkObject.HasAuthority) { networkObject.SetIsDestroying(); - var isSceneObject = networkObject.IsSceneObject; // Only destroy non-scene placed NetworkObjects to avoid warnings about destroying in-scene placed NetworkObjects. // (MoveObjectsToDontDestroyOnLoad is only invoked during a scene event type of load and the load scene mode is single) - networkObject.Despawn(isSceneObject.HasValue && isSceneObject.Value == false); + networkObject.Despawn(!networkObject.InScenePlaced); } } } @@ -2745,19 +2759,33 @@ internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearSceneP { ScenePlacedObjects.Clear(); } - var networkObjects = FindObjects.ByType(); + var sceneHandle = sceneToFilterBy.handle; // Just add every NetworkObject found that isn't already in the list // With additive scenes, we can have multiple in-scene placed NetworkObjects with the same GlobalObjectIdHash value // During Client Side Synchronization: We add them on a FIFO basis, for each scene loaded without clearing, and then // at the end of scene loading we use this list to soft synchronize all in-scene placed NetworkObjects - foreach (var networkObjectInstance in networkObjects) + foreach (var networkObjectInstance in FindObjects.FromSceneByType(sceneToFilterBy, true)) { + if (!networkObjectInstance.InScenePlaced) + { + continue; + } + + if (networkObjectInstance.NetworkManagerOwner == null) + { + networkObjectInstance.NetworkManagerOwner = NetworkManager; + } + var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash; - var sceneHandle = networkObjectInstance.gameObject.scene.handle; - // We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (for additive scenes) - if (networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == NetworkManager || - networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle) + + // We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per-scene basis (for additive scenes) +#if UNIFIED_NETCODE + // TODO: When we solve for GhostObject in-scene placed. + if (!networkObjectInstance.HasGhost && networkObjectInstance.NetworkManagerOwner == NetworkManager && networkObjectInstance.isActiveAndEnabled) +#else + if (networkObjectInstance.NetworkManagerOwner == NetworkManager && networkObjectInstance.isActiveAndEnabled) +#endif { if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash)) { @@ -2795,7 +2823,7 @@ internal void MoveObjectsFromDontDestroyOnLoadToScene(Scene scene) { // only move dynamically spawned network objects, with no parent as child objects will follow, // back into the currently active scene - if (networkObject.gameObject.transform.parent == null && networkObject.IsSceneObject != null && !networkObject.IsSceneObject.Value) + if (networkObject.gameObject.transform.parent == null && !networkObject.InScenePlaced) { if (NetworkManager.DistributedAuthorityMode) { @@ -2868,6 +2896,12 @@ internal bool IsSceneUnloading(NetworkObject networkObject) /// internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject) { + if (networkObject.NetworkManagerOwner != NetworkManager) + { + Debug.Log($"!!!!!!!!!!!!! Integration test is registering for scene migration for instances outside of the bounds of this NetworkManager context !!!!!!!!!!!!!"); + return; + } + // Really, this should never happen but in case it does if (!networkObject.HasAuthority) { @@ -2879,7 +2913,7 @@ internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject) } // Ignore in-scene placed NetworkObjects - if (networkObject.IsSceneObject != false) + if (networkObject.InScenePlaced) { // Really, this should ever happen but in case it does if (NetworkManager.LogLevel == LogLevel.Developer) @@ -2891,7 +2925,7 @@ internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject) // Ignore if the scene is the currently active scene and the NetworkObject is auto synchronizing/migrating // to the currently active scene. - if (networkObject.gameObject.scene == SceneManager.GetActiveScene() && networkObject.ActiveSceneSynchronization) + if (networkObject.gameObject.scene.name == SceneManager.GetActiveScene().name && networkObject.ActiveSceneSynchronization) { return; } @@ -2900,6 +2934,13 @@ internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject) // Note: This does not apply to SceneEventType.Synchronize since synchronization isn't a global connected client event. if (IsSceneEventInProgress()) { + Debug.Log($"{networkObject.name} scene event in progress -- ignoring!"); + return; + } + + if (IsSceneUnloading(networkObject)) + { + Debug.Log($"{networkObject.name} scene unloading in progress -- ignoring!"); return; } @@ -3024,7 +3065,15 @@ internal void CheckForAndSendNetworkObjectSceneChanged() // Some NetworkObjects still exist, send the message var sceneEvent = BeginSceneEvent(); sceneEvent.SceneEventType = SceneEventType.ObjectSceneChanged; - SendSceneEventData(sceneEvent.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.LocalClientId).ToArray()); + // SendSceneEventData can throw an exception. We need to wrap this and recover from the exception gracefully. + try + { + SendSceneEventData(sceneEvent.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.LocalClientId).ToArray()); + } + catch (Exception ex) + { + Debug.LogException(ex); + } ObjectsMigratedIntoNewScene.Clear(); EndSceneEvent(sceneEvent.SceneEventId); } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 0fff313574..83974d882d 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -5,6 +5,7 @@ using Unity.Collections; using UnityEngine.SceneManagement; + namespace Unity.Netcode { /// @@ -130,22 +131,21 @@ internal class SceneEventData : IDisposable /// was synchronizing (if so server will send another message back to the client informing the client of NetworkObjects to remove) /// spawned during an initial synchronization. /// - private List m_NetworkObjectsSync = new List(); + private readonly List m_NetworkObjectsSync = new List(); - private List m_DespawnedInSceneObjectsSync = new List(); - private Dictionary> m_DespawnedInSceneObjects = new Dictionary>(); + private readonly List m_DespawnedInSceneObjectsSync = new List(); /// /// Server Side Re-Synchronization: /// If there happens to be NetworkObjects in the final Event_Sync_Complete message that are no longer spawned, /// the server will compile a list and send back an Event_ReSync message to the client. /// - private List m_NetworkObjectsToBeRemoved = new List(); + private readonly List m_NetworkObjectsToBeRemoved = new List(); private bool m_HasInternalBuffer; - internal FastBufferReader InternalBuffer; + private FastBufferReader m_InternalBuffer; - private NetworkManager m_NetworkManager; + private readonly NetworkManager m_NetworkManager; internal List ClientsCompleted; internal List ClientsTimedOut; @@ -165,10 +165,10 @@ internal class SceneEventData : IDisposable /// we must distinguish which scene we are talking about when the server tells the client to unload a scene. /// The server will always communicate its local relative scene's handle and the client will determine its /// local relative handle from the table being built. - /// Look for usage to see where + /// Look for usage to see where /// entries are being added to or removed from the table ///
- /// + /// /// internal void AddSceneToSynchronize(uint sceneHash, NetworkSceneHandle sceneHandle) { @@ -316,8 +316,6 @@ private void SortParentedNetworkObjects() } } - internal static bool LogSerializationOrder = false; - internal void AddSpawnedNetworkObjects() { m_NetworkObjectsSync.Clear(); @@ -353,7 +351,7 @@ private void SortObjectsToSync() // This is useful to know what NetworkObjects a client is going to be synchronized with // as well as the order in which they will be deserialized - if (LogSerializationOrder && m_NetworkManager.LogLevel == LogLevel.Developer) + if (NetworkLog.Config.LogSerializationOrder && m_NetworkManager.LogLevel == LogLevel.Developer) { var messageBuilder = new StringBuilder(0xFFFF); messageBuilder.AppendLine("[Server-Side Client-Synchronization] NetworkObject serialization order:"); @@ -369,13 +367,19 @@ internal void AddDespawnedInSceneNetworkObjects() { m_DespawnedInSceneObjectsSync.Clear(); // Find all active and non-active in-scene placed NetworkObjects - var inSceneNetworkObjects = FindObjects.ByType(true, true).Where((c) => c.NetworkManager == m_NetworkManager); - foreach (var sobj in inSceneNetworkObjects) + foreach (var scene in m_NetworkManager.SceneManager.ScenesLoaded.Values) { - if (sobj.IsSceneObject.HasValue && sobj.IsSceneObject.Value && !sobj.IsSpawned) + // Ignore invalid scenes + if (!scene.IsValid()) + { + continue; + } + foreach (var networkObject in FindObjects.FromSceneByType(scene, true)) { - sobj.NetworkManagerOwner = m_NetworkManager; - m_DespawnedInSceneObjectsSync.Add(sobj); + if (networkObject.InScenePlaced && networkObject.NetworkManagerOwner == m_NetworkManager && !networkObject.IsSpawned) + { + m_DespawnedInSceneObjectsSync.Add(networkObject); + } } } } @@ -388,12 +392,12 @@ internal void AddDespawnedInSceneNetworkObjects() /// internal void AddNetworkObjectForSynch(uint sceneIndex, NetworkObject networkObject) { - if (!m_SceneNetworkObjects.ContainsKey(sceneIndex)) + if (!m_SceneNetworkObjects.TryGetValue(sceneIndex, out var sceneNetworkObject)) { - m_SceneNetworkObjects.Add(sceneIndex, new List()); + sceneNetworkObject = new List(); + m_SceneNetworkObjects.Add(sceneIndex, sceneNetworkObject); } - - m_SceneNetworkObjects[sceneIndex].Add(networkObject); + sceneNetworkObject.Add(networkObject); } /// @@ -568,7 +572,7 @@ internal void Serialize(FastBufferWriter writer) private unsafe void CopyInternalBuffer(ref FastBufferWriter writer) { - writer.WriteBytesSafe(InternalBuffer.GetUnsafePtrAtCurrentPosition(), InternalBuffer.Length); + writer.WriteBytesSafe(m_InternalBuffer.GetUnsafePtrAtCurrentPosition(), m_InternalBuffer.Length); } /// @@ -576,9 +580,9 @@ private unsafe void CopyInternalBuffer(ref FastBufferWriter writer) /// Called at the end of a event once the scene is loaded and scene placed NetworkObjects /// have been locally spawned /// - internal void WriteSceneSynchronizationData(FastBufferWriter writer) + private void WriteSceneSynchronizationData(FastBufferWriter writer) { - var builder = (StringBuilder)null; + StringBuilder builder = null; if (EnableSerializationLogs) { builder = new StringBuilder(); @@ -601,11 +605,10 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) return; } - // Size Place Holder -- Start + // Size Placeholder -- Start // !!NOTE!!: Since this is a placeholder to be set after we know how much we have written, // for stream offset purposes this MUST not be a packed value! writer.WriteValueSafe(0); - int totalBytes = 0; // Write the number of NetworkObjects we are serializing writer.WriteValueSafe(m_NetworkObjectsSync.Count); @@ -616,16 +619,14 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) var distributedAuthority = m_NetworkManager.DistributedAuthorityMode; // Serialize all NetworkObjects that are spawned - for (var i = 0; i < m_NetworkObjectsSync.Count; ++i) + foreach (var networkObject in m_NetworkObjectsSync) { - var networkObject = m_NetworkObjectsSync[i]; var noStart = writer.Position; // In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized. - var serializedObject = m_NetworkObjectsSync[i].Serialize(TargetClientId, distributedAuthority); + var serializedObject = networkObject.SerializeSpawnedObject(TargetClientId, distributedAuthority); serializedObject.Serialize(writer); var noStop = writer.Position; - totalBytes += noStop - noStart; if (EnableSerializationLogs) { var offStart = noStart - (positionStart + sizeof(int)); @@ -642,16 +643,13 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) // Write the number of despawned in-scene placed NetworkObjects writer.WriteValueSafe(m_DespawnedInSceneObjectsSync.Count); // Write the scene handle and GlobalObjectIdHash value - for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i) + foreach (var despawnedInSceneObject in m_DespawnedInSceneObjectsSync) { - var noStart = writer.Position; - writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle()); - writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash); - var noStop = writer.Position; - totalBytes += noStop - noStart; + writer.WriteValueSafe(despawnedInSceneObject.GetSceneOriginHandle()); + writer.WriteValueSafe(despawnedInSceneObject.GlobalObjectIdHash); } - // Size Place Holder -- End + // Size Placeholder -- End var positionEnd = writer.Position; var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); writer.Seek(positionStart); @@ -670,12 +668,12 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) /// have been locally spawned /// Maximum number of objects that could theoretically be synchronized is 65536 /// - internal void SerializeScenePlacedObjects(FastBufferWriter writer) + private void SerializeScenePlacedObjects(FastBufferWriter writer) { - var numberOfObjects = (ushort)0; + ushort numberOfObjects = 0; var headPosition = writer.Position; - // Write our count place holder (must not be packed!) + // Write our count placeholder (must not be packed!) writer.WriteValueSafe((ushort)0); var distributedAuthority = m_NetworkManager.DistributedAuthorityMode; // If distributed authority mode and sending to the service, then ignore observers @@ -698,10 +696,10 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer) SortObjectsToSync(); // Serialize the sorted objects to sync. - foreach (var objectToSycn in m_NetworkObjectsSync) + foreach (var objectToSync in m_NetworkObjectsSync) { // Serialize the NetworkObject - var serializedObject = objectToSycn.Serialize(TargetClientId, distributedAuthority); + var serializedObject = objectToSync.SerializeSpawnedObject(TargetClientId, distributedAuthority); serializedObject.Serialize(writer); numberOfObjects++; } @@ -709,10 +707,10 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer) // Write the number of despawned in-scene placed NetworkObjects writer.WriteValueSafe(m_DespawnedInSceneObjectsSync.Count); // Write the scene handle and GlobalObjectIdHash value - for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i) + foreach (var despawnedInSceneObject in m_DespawnedInSceneObjectsSync) { - writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle()); - writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash); + writer.WriteValueSafe(despawnedInSceneObject.GetSceneOriginHandle()); + writer.WriteValueSafe(despawnedInSceneObject.GlobalObjectIdHash); } var tailPosition = writer.Position; @@ -799,7 +797,7 @@ internal void Deserialize(FastBufferReader reader) // be processed once we are done loading. m_HasInternalBuffer = true; // We use Allocator.Persistent since scene loading could take longer than 4 frames - InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, reader.Length - reader.Position); + m_InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, reader.Length - reader.Position); } break; } @@ -825,7 +823,7 @@ internal void Deserialize(FastBufferReader reader) /// into the internal buffer to be used throughout the synchronization process. ///
/// - internal void CopySceneSynchronizationData(FastBufferReader reader) + private void CopySceneSynchronizationData(FastBufferReader reader) { m_NetworkObjectsSync.Clear(); reader.ReadValueSafe(out uint[] scenesToSynchronize); @@ -847,10 +845,10 @@ internal void CopySceneSynchronizationData(FastBufferReader reader) m_HasInternalBuffer = true; // We use Allocator.Persistent since scene synchronization will most likely take longer than 4 frames - InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, sizeToCopy); + m_InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, sizeToCopy); if (EnableSerializationLogs) { - LogArray(InternalBuffer.ToArray()); + LogArray(m_InternalBuffer.ToArray()); } } } @@ -865,12 +863,12 @@ internal void DeserializeScenePlacedObjects() try { // is not packed! - InternalBuffer.ReadValueSafe(out ushort newObjectsCount); + m_InternalBuffer.ReadValueSafe(out ushort newObjectsCount); var sceneObjects = new List(); for (ushort i = 0; i < newObjectsCount; i++) { var serializedObject = new NetworkObject.SerializedObject(); - serializedObject.Deserialize(InternalBuffer); + serializedObject.Deserialize(m_InternalBuffer); if (serializedObject.IsSceneObject) { @@ -878,9 +876,9 @@ internal void DeserializeScenePlacedObjects() m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(serializedObject.NetworkSceneHandle); } - var networkObject = NetworkObject.Deserialize(serializedObject, InternalBuffer, m_NetworkManager); + var networkObject = NetworkObject.DeserializeAndSpawnObject(serializedObject, m_InternalBuffer, m_NetworkManager); - if (serializedObject.IsSceneObject) + if (serializedObject.IsSceneObject && networkObject != null) { sceneObjects.Add(networkObject); } @@ -897,7 +895,7 @@ internal void DeserializeScenePlacedObjects() } finally { - InternalBuffer.Dispose(); + m_InternalBuffer.Dispose(); m_HasInternalBuffer = false; } } @@ -909,7 +907,7 @@ internal void DeserializeScenePlacedObjects() /// client handles any returned values by the server. ///
/// - internal void ReadClientReSynchronizationData(FastBufferReader reader) + private void ReadClientReSynchronizationData(FastBufferReader reader) { reader.ReadValueSafe(out uint[] networkObjectsToRemove); @@ -946,7 +944,7 @@ internal void ReadClientReSynchronizationData(FastBufferReader reader) /// the server will compile a list and send back an Event_ReSync message to the client. ///
/// - internal void WriteClientReSynchronizationData(FastBufferWriter writer) + private void WriteClientReSynchronizationData(FastBufferWriter writer) { //Write how many objects need to be removed writer.WriteValueSafe(m_NetworkObjectsToBeRemoved.ToArray()); @@ -960,7 +958,7 @@ internal void WriteClientReSynchronizationData(FastBufferWriter writer) /// internal bool ClientNeedsReSynchronization() { - return (m_NetworkObjectsToBeRemoved.Count > 0); + return m_NetworkObjectsToBeRemoved.Count > 0; } /// @@ -970,7 +968,7 @@ internal bool ClientNeedsReSynchronization() /// have since been despawned. /// /// - internal void CheckClientSynchronizationResults(FastBufferReader reader) + private void CheckClientSynchronizationResults(FastBufferReader reader) { m_NetworkObjectsToBeRemoved.Clear(); reader.ReadValueSafe(out uint networkObjectIdCount); @@ -987,12 +985,12 @@ internal void CheckClientSynchronizationResults(FastBufferReader reader) /// /// Client Side: /// During the deserialization process of the servers Event_Sync, the client builds a list of - /// all NetworkObjectIds that were spawned. Upon responding to the server with the Event_Sync_Complete + /// all NetworkObjectIds that were spawned. Upon responding to the server with the Event_Sync_Complete, /// this list is included for the server to review over and determine if the client needs a minor resynchronization /// of NetworkObjects that might have been despawned while the client was processing the Event_Sync. /// /// - internal void WriteClientSynchronizationResults(FastBufferWriter writer) + private void WriteClientSynchronizationResults(FastBufferWriter writer) { //Write how many objects were spawned writer.WriteValueSafe((uint)m_NetworkObjectsSync.Count); @@ -1009,34 +1007,29 @@ internal void WriteClientSynchronizationResults(FastBufferWriter writer) private void DeserializeDespawnedInScenePlacedNetworkObjects() { // Process all de-spawned in-scene NetworkObjects for this network session - m_DespawnedInSceneObjects.Clear(); - InternalBuffer.ReadValueSafe(out int despawnedObjectsCount); + m_InternalBuffer.ReadValueSafe(out int despawnedObjectsCount); var sceneCache = new Dictionary>(); for (int i = 0; i < despawnedObjectsCount; i++) { // We just need to get the scene - InternalBuffer.ReadValueSafe(out NetworkSceneHandle networkSceneHandle); - InternalBuffer.ReadValueSafe(out uint globalObjectIdHash); - var sceneRelativeNetworkObjects = new Dictionary(); - if (!sceneCache.ContainsKey(networkSceneHandle)) + m_InternalBuffer.ReadValueSafe(out NetworkSceneHandle networkSceneHandle); + m_InternalBuffer.ReadValueSafe(out uint globalObjectIdHash); + + // Check if we already have processed the objects in this scene + if (!sceneCache.TryGetValue(networkSceneHandle, out var sceneRelativeNetworkObjects)) { - if (m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(networkSceneHandle)) + // If we haven't already cached the objects in this scene, build the cache + sceneRelativeNetworkObjects = new Dictionary(); + if (m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle.TryGetValue(networkSceneHandle, out var localSceneHandle)) { - var localSceneHandle = m_NetworkManager.SceneManager.ServerSceneHandleToClientSceneHandle[networkSceneHandle]; - if (m_NetworkManager.SceneManager.ScenesLoaded.ContainsKey(localSceneHandle)) + if (m_NetworkManager.SceneManager.ScenesLoaded.TryGetValue(localSceneHandle, out var objectRelativeScene)) { - var objectRelativeScene = m_NetworkManager.SceneManager.ScenesLoaded[localSceneHandle]; - - // Find all active and non-active in-scene placed NetworkObjects - var inSceneNetworkObjects = FindObjects.ByType(true, true).Where((c) => - c.GetSceneOriginHandle() == localSceneHandle && (c.IsSceneObject != false)).ToList(); - - foreach (var inSceneObject in inSceneNetworkObjects) + foreach (var networkObject in FindObjects.FromSceneByType(objectRelativeScene, true)) { - if (!sceneRelativeNetworkObjects.ContainsKey(inSceneObject.GlobalObjectIdHash)) + if (networkObject.InScenePlaced) { - sceneRelativeNetworkObjects.Add(inSceneObject.GlobalObjectIdHash, inSceneObject); + sceneRelativeNetworkObjects.TryAdd(networkObject.GlobalObjectIdHash, networkObject); } } // Add this to a cache so we don't have to run this potentially multiple times (nothing will spawn or despawn during this time @@ -1052,10 +1045,6 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() UnityEngine.Debug.LogError($"In-Scene NetworkObject GlobalObjectIdHash ({globalObjectIdHash}) cannot find its relative NetworkSceneHandle {networkSceneHandle}!"); } } - else // Use the cached NetworkObjects if they exist - { - sceneRelativeNetworkObjects = sceneCache[networkSceneHandle]; - } // Now find the in-scene NetworkObject with the current GlobalObjectIdHash we are looking for if (sceneRelativeNetworkObjects.TryGetValue(globalObjectIdHash, out var despawnedObject)) @@ -1066,15 +1055,10 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() // Since this is a NetworkObject that was never spawned, we just need to send a notification // out that it was despawned so users can make adjustments despawnedObject.InvokeBehaviourNetworkDespawn(); - if (!m_NetworkManager.SceneManager.ScenePlacedObjects.ContainsKey(globalObjectIdHash)) - { - m_NetworkManager.SceneManager.ScenePlacedObjects.Add(globalObjectIdHash, new Dictionary()); - } - if (!m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].ContainsKey(despawnedObject.GetSceneOriginHandle())) - { - m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].Add(despawnedObject.GetSceneOriginHandle(), despawnedObject); - } + m_NetworkManager.SceneManager.ScenePlacedObjects.TryAdd(globalObjectIdHash, new Dictionary()); + + m_NetworkManager.SceneManager.ScenePlacedObjects[globalObjectIdHash].TryAdd(despawnedObject.GetSceneOriginHandle(), despawnedObject); } else { @@ -1092,7 +1076,7 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() /// internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { - var builder = (StringBuilder)null; + StringBuilder builder = null; if (EnableSerializationLogs) { builder = new StringBuilder(); @@ -1101,38 +1085,55 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) try { // Process all spawned NetworkObjects for this network session - InternalBuffer.ReadValueSafe(out int newObjectsCount); + m_InternalBuffer.ReadValueSafe(out int newObjectsCount); if (EnableSerializationLogs) { - builder.AppendLine($"[Read][Synchronize Objects][WPos: {InternalBuffer.Position}][NO-Count: {newObjectsCount}] Begin:"); + builder.AppendLine($"[Read][Synchronize Objects][WPos: {m_InternalBuffer.Position}][NO-Count: {newObjectsCount}] Begin:"); } for (int i = 0; i < newObjectsCount; i++) { - var noStart = InternalBuffer.Position; + var noStart = m_InternalBuffer.Position; var serializedObject = new NetworkObject.SerializedObject(); - serializedObject.Deserialize(InternalBuffer); + serializedObject.Deserialize(m_InternalBuffer); + +#if UNIFIED_NETCODE + // This handles the case where a NetworkObject is serialized with a ghost component but the ghost isn't actually included in + // the spawn message and won't be spawned by the client until later in the N4E synchronization process. In this case, we need + // to defer the deserialization of the NetworkObject until the ghost is spawned and we have an instance to deserialize this + // information during synchronization. + if (serializedObject.HasGhost) + { + if (networkManager.SpawnManager.GhostSpawnManager.ShouldDeferGhostSceneObject(serializedObject, m_InternalBuffer)) + { + continue; + } + } +#endif // If the sceneObject is in-scene placed, then set the scene being synchronized if (serializedObject.IsSceneObject) { m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(serializedObject.NetworkSceneHandle); } - var spawnedNetworkObject = NetworkObject.Deserialize(serializedObject, InternalBuffer, networkManager); - var noStop = InternalBuffer.Position; + var spawnedNetworkObject = NetworkObject.DeserializeAndSpawnObject(serializedObject, m_InternalBuffer, networkManager); + if (spawnedNetworkObject == null) + { + continue; + } + + var noStop = m_InternalBuffer.Position; + if (EnableSerializationLogs) { builder.AppendLine($"[Head: {noStart}][Tail: {noStop}][Size: {noStop - noStart}][{spawnedNetworkObject.name}][NID-{spawnedNetworkObject.NetworkObjectId}][Children: {spawnedNetworkObject.ChildNetworkBehaviours.Count}]"); - LogArray(InternalBuffer.ToArray(), noStart, noStop, builder); + LogArray(m_InternalBuffer.ToArray(), noStart, noStop, builder); } - // If we failed to deserialize the NetowrkObject then don't add null to the list - if (spawnedNetworkObject != null) + // If we failed to deserialize the NetworkObject then don't add null to the list + if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) { - if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) - { - m_NetworkObjectsSync.Add(spawnedNetworkObject); - } + m_NetworkObjectsSync.Add(spawnedNetworkObject); } } if (EnableSerializationLogs) @@ -1143,7 +1144,7 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) // Notify that all in-scene placed NetworkObjects have been spawned foreach (var networkObject in m_NetworkObjectsSync) { - if (networkObject.IsSceneObject.HasValue && networkObject.IsSceneObject.Value) + if (networkObject.IsSpawned && networkObject.InScenePlaced) { networkObject.InternalInSceneNetworkObjectsSpawned(); } @@ -1156,11 +1157,14 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) catch (Exception ex) { UnityEngine.Debug.LogException(ex); - UnityEngine.Debug.Log(builder.ToString()); + if (EnableSerializationLogs) + { + UnityEngine.Debug.Log(builder.ToString()); + } } finally { - InternalBuffer.Dispose(); + m_InternalBuffer.Dispose(); m_HasInternalBuffer = false; } } @@ -1169,7 +1173,7 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) /// Writes the all clients loaded or unloaded completed and timed out lists ///
/// - internal void WriteSceneEventProgressDone(FastBufferWriter writer) + private void WriteSceneEventProgressDone(FastBufferWriter writer) { writer.WriteValueSafe((ushort)ClientsCompleted.Count); foreach (var clientId in ClientsCompleted) @@ -1188,7 +1192,7 @@ internal void WriteSceneEventProgressDone(FastBufferWriter writer) /// Reads the all clients loaded or unloaded completed and timed out lists ///
/// - internal void ReadSceneEventProgressDone(FastBufferReader reader) + private void ReadSceneEventProgressDone(FastBufferReader reader) { reader.ReadValueSafe(out ushort completedCount); ClientsCompleted = new List(); @@ -1217,31 +1221,51 @@ internal void ReadSceneEventProgressDone(FastBufferReader reader) private void SerializeObjectsMovedIntoNewScene(FastBufferWriter writer) { var sceneManager = m_NetworkManager.SceneManager; - var ownerId = m_NetworkManager.LocalClientId; + var networkManagerClientId = m_NetworkManager.LocalClientId; if (IsForwarding) { - ownerId = m_OwnerId; + networkManagerClientId = m_OwnerId; } // Write the owner identifier - writer.WriteValueSafe(ownerId); + writer.WriteValueSafe(networkManagerClientId); - // Write the number of scene handles - writer.WriteValueSafe(sceneManager.ObjectsMigratedIntoNewScene.Count); + // Create a place holder for the number of entries written. + // Distributed authority this could end up being just a single entry for + // one of several scenes loaded. As such, we need to count how many entries + // are actually written. + var countPosition = writer.Position; + writer.WriteValueSafe(0); + var entriesWritten = 0; foreach (var sceneHandleObjects in sceneManager.ObjectsMigratedIntoNewScene) { - if (!sceneManager.ObjectsMigratedIntoNewScene[sceneHandleObjects.Key].ContainsKey(ownerId)) + // Since these are separated by scene then owner, there could be scenes that have + // no changes. + if (!sceneHandleObjects.Value.ContainsKey(networkManagerClientId)) { - throw new Exception($"Trying to send object scene migration for Client-{ownerId} but the client has no entries to send!"); + continue; } // Write the scene handle writer.WriteValueSafe(sceneHandleObjects.Key); // Write the number of NetworkObjectIds to expect - writer.WriteValueSafe(sceneHandleObjects.Value[ownerId].Count); - foreach (var networkObject in sceneHandleObjects.Value[ownerId]) + writer.WriteValueSafe(sceneHandleObjects.Value[networkManagerClientId].Count); + foreach (var networkObject in sceneHandleObjects.Value[networkManagerClientId]) { writer.WriteValueSafe(networkObject.NetworkObjectId); } + entriesWritten++; + } + if (entriesWritten == 0) + { + throw new Exception($"Trying to send object scene migration for Client-{networkManagerClientId} but the client has no entries to send!"); + } + else + { + // Write the number of entries written + var endPosition = writer.Position; + writer.Seek(countPosition); + writer.WriteValueSafe(entriesWritten); + writer.Seek(endPosition); } } @@ -1254,47 +1278,37 @@ private void DeserializeObjectsMovedIntoNewScene(FastBufferReader reader) var sceneManager = m_NetworkManager.SceneManager; var spawnManager = m_NetworkManager.SpawnManager; - var numberOfScenes = 0; - NetworkSceneHandle sceneHandle; - var objectCount = 0; - var networkObjectId = (ulong)0; - - var ownerID = (ulong)0; - reader.ReadValueSafe(out ownerID); + reader.ReadValueSafe(out ulong ownerID); m_OwnerId = ownerID; - reader.ReadValueSafe(out numberOfScenes); + reader.ReadValueSafe(out int numberOfScenes); for (int i = 0; i < numberOfScenes; i++) { - reader.ReadValueSafe(out sceneHandle); + reader.ReadValueSafe(out NetworkSceneHandle sceneHandle); if (!sceneManager.ObjectsMigratedIntoNewScene.TryGetValue(sceneHandle, out var migratedObjects)) { migratedObjects = new Dictionary>(); sceneManager.ObjectsMigratedIntoNewScene.Add(sceneHandle, migratedObjects); } - if (!migratedObjects.ContainsKey(ownerID)) - { - migratedObjects.Add(ownerID, new List()); - } + migratedObjects.TryAdd(ownerID, new List()); - reader.ReadValueSafe(out objectCount); + reader.ReadValueSafe(out int objectCount); for (int j = 0; j < objectCount; j++) { - reader.ReadValueSafe(out networkObjectId); - if (!spawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + reader.ReadValueSafe(out ulong networkObjectId); + if (!spawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var networkObject)) { NetworkLog.LogError($"[Object Scene Migration] Trying to synchronize NetworkObjectId ({networkObjectId}) but it was not spawned or no longer exists!!"); continue; } - var networkObject = spawnManager.SpawnedObjects[networkObjectId]; + // Add NetworkObject scene migration to ObjectsMigratedIntoNewScene dictionary that is processed migratedObjects[ownerID].Add(networkObject); } } } - /// /// While a client is synchronizing ObjectSceneChanged messages could be received. /// This defers any ObjectSceneChanged message processing to occur after the client @@ -1304,15 +1318,8 @@ private void DeserializeObjectsMovedIntoNewScene(FastBufferReader reader) private void DeferObjectsMovedIntoNewScene(FastBufferReader reader) { var sceneManager = m_NetworkManager.SceneManager; - var spawnManager = m_NetworkManager.SpawnManager; - var ownerId = (ulong)0; - var numberOfScenes = 0; - NetworkSceneHandle sceneHandle; - var objectCount = 0; - var networkObjectId = (ulong)0; - - reader.ReadValueSafe(out ownerId); + reader.ReadValueSafe(out ulong ownerId); var deferredObjectsMovedEvent = new NetworkSceneManager.DeferredObjectsMovedEvent() { @@ -1320,17 +1327,16 @@ private void DeferObjectsMovedIntoNewScene(FastBufferReader reader) ObjectsMigratedTable = new Dictionary>(), }; - - reader.ReadValueSafe(out numberOfScenes); + reader.ReadValueSafe(out int numberOfScenes); for (int i = 0; i < numberOfScenes; i++) { - reader.ReadValueSafe(out sceneHandle); + reader.ReadValueSafe(out NetworkSceneHandle sceneHandle); var objectsMigrated = new List(); deferredObjectsMovedEvent.ObjectsMigratedTable.Add(sceneHandle, objectsMigrated); - reader.ReadValueSafe(out objectCount); + reader.ReadValueSafe(out int objectCount); for (int j = 0; j < objectCount; j++) { - reader.ReadValueSafe(out networkObjectId); + reader.ReadValueSafe(out ulong networkObjectId); objectsMigrated.Add(networkObjectId); } } @@ -1354,10 +1360,9 @@ internal void ProcessDeferredObjectSceneChangedEvents() migratedObjects = new Dictionary>(); sceneManager.ObjectsMigratedIntoNewScene.Add(keyEntry.Key, migratedObjects); } - if (!migratedObjects.ContainsKey(objectsMovedEvent.OwnerId)) - { - migratedObjects.Add(objectsMovedEvent.OwnerId, new List()); - } + + migratedObjects.TryAdd(objectsMovedEvent.OwnerId, new List()); + var objectList = migratedObjects[objectsMovedEvent.OwnerId]; foreach (var objectId in keyEntry.Value) { @@ -1367,9 +1372,9 @@ internal void ProcessDeferredObjectSceneChangedEvents() continue; } - if (!migratedObjects[objectsMovedEvent.OwnerId].Contains(networkObject)) + if (!objectList.Contains(networkObject)) { - migratedObjects[objectsMovedEvent.OwnerId].Add(networkObject); + objectList.Add(networkObject); } } } @@ -1385,7 +1390,7 @@ public void Dispose() { if (m_HasInternalBuffer) { - InternalBuffer.Dispose(); + m_InternalBuffer.Dispose(); m_HasInternalBuffer = false; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index dcb30a92dd..ab8c9bc5ce 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -15,7 +15,7 @@ public ref struct BitReader private readonly unsafe byte* m_BufferPointer; private readonly int m_Position; private int m_BitPosition; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG private int m_AllowedBitwiseReadMark; #endif @@ -39,7 +39,7 @@ internal unsafe BitReader(FastBufferReader reader) m_BufferPointer = m_Reader.Handle->BufferPointer + m_Reader.Handle->Position; m_Position = m_Reader.Handle->Position; m_BitPosition = 0; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG m_AllowedBitwiseReadMark = (m_Reader.Handle->AllowedReadMark - m_Position) * k_BitsPerByte; #endif } @@ -81,7 +81,7 @@ public unsafe bool TryBeginReadBits(uint bitCount) { return false; } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG m_AllowedBitwiseReadMark = (int)newBitPosition; #endif return true; @@ -94,7 +94,7 @@ public unsafe bool TryBeginReadBits(uint bitCount) /// Amount of bits to read public unsafe void ReadBits(out ulong value, uint bitCount) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (bitCount > 64) { throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!"); @@ -136,7 +136,7 @@ public unsafe void ReadBits(out ulong value, uint bitCount) /// Amount of bits to read. public void ReadBits(out byte value, uint bitCount) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { @@ -153,7 +153,7 @@ public void ReadBits(out byte value, uint bitCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBit(out bool bit) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseReadMark) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 0e3ccfb7e3..7b38bcadc9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -15,7 +15,7 @@ public ref struct BitWriter private unsafe byte* m_BufferPointer; private readonly int m_Position; private int m_BitPosition; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG private int m_AllowedBitwiseWriteMark; #endif private const int k_BitsPerByte = 8; @@ -37,7 +37,7 @@ internal unsafe BitWriter(FastBufferWriter writer) m_BufferPointer = writer.Handle->BufferPointer + writer.Handle->Position; m_Position = writer.Handle->Position; m_BitPosition = 0; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG m_AllowedBitwiseWriteMark = (m_Writer.Handle->AllowedWriteMark - m_Writer.Handle->Position) * k_BitsPerByte; #endif } @@ -97,7 +97,7 @@ public unsafe bool TryBeginWriteBits(int bitCount) return false; } } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG m_AllowedBitwiseWriteMark = newBitPosition; #endif return true; @@ -110,7 +110,7 @@ public unsafe bool TryBeginWriteBits(int bitCount) /// Amount of bits to write public unsafe void WriteBits(ulong value, uint bitCount) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (bitCount > 64) { throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write more than 64 bits from a 64-bit value!"); @@ -153,7 +153,7 @@ public unsafe void WriteBits(ulong value, uint bitCount) /// Amount of bits to write. public void WriteBits(byte value, uint bitCount) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { @@ -174,7 +174,7 @@ public void WriteBits(byte value, uint bitCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBit(bool bit) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseWriteMark) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index d0dfdca7cd..d132f775fd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -20,7 +20,7 @@ internal struct ReaderHandle internal int Position; internal int Length; internal Allocator Allocator; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG internal int AllowedReadMark; internal bool InBitwiseContext; #endif @@ -55,7 +55,7 @@ public unsafe int Length internal unsafe void CommitBitwiseReads(int amount) { Handle->Position += amount; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->InBitwiseContext = false; #endif } @@ -83,7 +83,7 @@ internal unsafe void CommitBitwiseReads(int amount) // When we dispose, we are really only interested in disposing Allocator.Persistent and Allocator.TempJob // as disposing Allocator.Temp and Allocator.None would do nothing. Therefore, make sure we dispose the readerHandle with the right Allocator label readerHandle->Allocator = copyAllocator == Allocator.None ? internalAllocator : copyAllocator; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG readerHandle->AllowedReadMark = 0; readerHandle->InBitwiseContext = false; #endif @@ -321,7 +321,7 @@ public unsafe void Seek(int where) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe void MarkBytesRead(int amount) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -343,7 +343,7 @@ internal unsafe void MarkBytesRead(int amount) /// A BitReader public unsafe BitReader EnterBitwiseContext() { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->InBitwiseContext = true; #endif return new BitReader(this); @@ -366,7 +366,7 @@ public unsafe BitReader EnterBitwiseContext() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TryBeginRead(int bytes) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -377,7 +377,7 @@ public unsafe bool TryBeginRead(int bytes) { return false; } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->AllowedReadMark = Handle->Position + bytes; #endif return true; @@ -401,7 +401,7 @@ public unsafe bool TryBeginRead(int bytes) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TryBeginReadValue(in T value) where T : unmanaged { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -413,7 +413,7 @@ public unsafe bool TryBeginReadValue(in T value) where T : unmanaged { return false; } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->AllowedReadMark = Handle->Position + len; #endif return true; @@ -429,7 +429,7 @@ public unsafe bool TryBeginReadValue(in T value) where T : unmanaged [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe bool TryBeginReadInternal(int bytes) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -440,7 +440,7 @@ internal unsafe bool TryBeginReadInternal(int bytes) { return false; } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->Position + bytes > Handle->AllowedReadMark) { Handle->AllowedReadMark = Handle->Position + bytes; @@ -564,14 +564,36 @@ public void ReadNetworkSerializableInPlace(ref T value) where T : INetworkSer } /// - /// Reads a string - /// NOTE: ALLOCATES + /// Validates the string's total byte count based on whether we are + /// using one or two byte characters. /// - /// Stores the read string - /// Whether or not to use one byte per character. This will only allow ASCII - public unsafe void ReadValue(out string s, bool oneByteChars = false) + /// + /// Will throw an overflow exception if the size is greater than . + /// + /// Character count + /// If false(default) 2 byte characters and if true 1 byte characters + /// total size in bytes to read + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ValidateStringByteCount(int length, bool oneByteChars) + { + var readSize = oneByteChars ? length : length * sizeof(char); + if (int.MaxValue < (uint)readSize) + { + throw new OverflowException($"Invalid reader position detected when trying to read a string of size {(uint)readSize}! This can result from an error in the serialization. Ensure deserialization exactly matches what was serialized!"); + } + return readSize; + } + + /// + /// Commonly shared string read method between . + /// + /// The output of the string read. + /// The number of characters in the string. + /// If false(default) 2 byte characters and if true 1 byte characters. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void ReadString(out string s, int length, bool oneByteChars) { - ReadLength(out int length); s = "".PadRight(length); int target = s.Length; fixed (char* native = s) @@ -592,56 +614,50 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) } /// - /// Reads a string. - /// NOTE: ALLOCATES - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling TryBeginRead. + /// Reads a string without bounds checking. + /// NOTE: This method ALLOCATES memory. /// + /// + /// This is the un-safe string read which requires invoking prior to invoking this method.
+ /// Using one byte characters only allows ASCII characters. + ///
/// Stores the read string - /// Whether or not to use one byte per character. This will only allow ASCII - public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) + /// If false(default) 2 byte characters and if true 1 byte characters. + public unsafe void ReadValue(out string s, bool oneByteChars = false) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (Handle->InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferReader in bytewise mode while in a bitwise context."); - } -#endif + ReadLength(out int length); - if (!TryBeginReadInternal(SizeOfLengthField())) - { - throw new OverflowException("Reading past the end of the buffer"); - } + // Validate the string's byte count based on the character count. + ValidateStringByteCount(length, oneByteChars); - ReadLength(out int length); + // Read the string + ReadString(out s, length, oneByteChars); + } + + /// + /// Reads a string after it performs bounds checking automatically. + /// NOTE: This method ALLOCATES memory. + /// + /// + /// This is the safe string read which invokes prior to reading the string.
+ /// Using one byte characters only allows ASCII characters. + ///
+ /// The string re the read string + /// If false(default) 2 byte characters and if true 1 byte characters. + public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) + { + ReadLengthSafe(out int length); - if (!TryBeginReadInternal(length * (oneByteChars ? 1 : sizeof(char)))) + // Validate the string's byte count based on the character count and if it is valid begin reading based on the returned + // byte count. + if (!TryBeginReadInternal(ValidateStringByteCount(length, oneByteChars))) { throw new OverflowException("Reading past the end of the buffer"); } - s = "".PadRight(length); - int target = s.Length; - fixed (char* native = s) - { - if (oneByteChars) - { - for (int i = 0; i < target; ++i) - { - ReadByte(out byte b); - native[i] = (char)b; - } - } - else - { - ReadBytes((byte*)native, target * sizeof(char)); - } - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int SizeOfLengthField() => sizeof(uint); + // Read the string + ReadString(out s, length, oneByteChars); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadLengthSafe(out uint length) => ReadUnmanagedSafe(out length); @@ -679,7 +695,7 @@ private void ReadLength(out int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T : unmanaged { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -707,7 +723,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadByte(out byte value) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -731,7 +747,7 @@ public unsafe void ReadByte(out byte value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadByteSafe(out byte value) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -755,7 +771,7 @@ public unsafe void ReadByteSafe(out byte value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBytes(byte* value, int size, int offset = 0) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -782,7 +798,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index bea537965e..343822fe32 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -23,7 +23,7 @@ internal struct WriterHandle internal int MaxCapacity; internal Allocator Allocator; internal bool BufferGrew; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG internal int AllowedWriteMark; internal bool InBitwiseContext; #endif @@ -31,7 +31,7 @@ internal struct WriterHandle internal unsafe WriterHandle* Handle; - private static byte[] s_ByteArrayCache = new byte[65535]; + private static readonly byte[] k_ByteArrayCache = new byte[65535]; /// /// The current write position @@ -79,7 +79,7 @@ public unsafe int Length internal unsafe void CommitBitwiseWrites(int amount) { Handle->Position += amount; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->InBitwiseContext = false; #endif } @@ -97,7 +97,7 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) // If the buffer grows, a new buffer will be allocated and the handle pointer pointed at the new location... // The original buffer won't be deallocated until the writer is destroyed since it's part of the handle allocation. Handle = (WriterHandle*)UnsafeUtility.Malloc(sizeof(WriterHandle) + size, UnsafeUtility.AlignOf(), allocator); -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG UnsafeUtility.MemSet(Handle, 0, sizeof(WriterHandle) + size); #endif Handle->BufferPointer = (byte*)(Handle + 1); @@ -107,7 +107,7 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) Handle->Allocator = allocator; Handle->MaxCapacity = maxSize < size ? size : maxSize; Handle->BufferGrew = false; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->AllowedWriteMark = 0; Handle->InBitwiseContext = false; #endif @@ -185,7 +185,7 @@ public unsafe void Truncate(int where = -1) /// A BitWriter public unsafe BitWriter EnterBitwiseContext() { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->InBitwiseContext = true; #endif return new BitWriter(this); @@ -201,7 +201,7 @@ internal unsafe void Grow(int additionalSizeRequired) var newSize = Math.Min(desiredSize, Handle->MaxCapacity); byte* newBuffer = (byte*)UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), Handle->Allocator); -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG UnsafeUtility.MemSet(newBuffer, 0, newSize); #endif UnsafeUtility.MemCpy(newBuffer, Handle->BufferPointer, Length); @@ -232,7 +232,7 @@ internal unsafe void Grow(int additionalSizeRequired) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TryBeginWrite(int bytes) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -255,7 +255,7 @@ public unsafe bool TryBeginWrite(int bytes) return false; } } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->AllowedWriteMark = Handle->Position + bytes; #endif return true; @@ -280,7 +280,7 @@ public unsafe bool TryBeginWrite(int bytes) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -304,7 +304,7 @@ public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged return false; } } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG Handle->AllowedWriteMark = Handle->Position + len; #endif return true; @@ -320,7 +320,7 @@ public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TryBeginWriteInternal(int bytes) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -343,7 +343,7 @@ public unsafe bool TryBeginWriteInternal(int bytes) return false; } } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->Position + bytes > Handle->AllowedWriteMark) { Handle->AllowedWriteMark = Handle->Position + bytes; @@ -379,17 +379,17 @@ public unsafe byte[] ToArray() internal unsafe ArraySegment ToTempByteArray() { var length = Length; - if (length > s_ByteArrayCache.Length) + if (length > k_ByteArrayCache.Length) { return new ArraySegment(ToArray(), 0, length); } - fixed (byte* b = s_ByteArrayCache) + fixed (byte* b = k_ByteArrayCache) { UnsafeUtility.MemCpy(b, Handle->BufferPointer, length); } - return new ArraySegment(s_ByteArrayCache, 0, length); + return new ArraySegment(k_ByteArrayCache, 0, length); } /// @@ -523,7 +523,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) /// Whether or not to use one byte per character. This will only allow ASCII public unsafe void WriteValueSafe(string s, bool oneByteChars = false) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -618,7 +618,7 @@ public static unsafe int GetWriteSize(NativeList array, int count = -1, in [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -644,7 +644,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteByte(byte value) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -668,7 +668,7 @@ public unsafe void WriteByte(byte value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteByteSafe(byte value) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -692,7 +692,7 @@ public unsafe void WriteByteSafe(byte value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte* value, int size, int offset = 0) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( @@ -719,7 +719,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG if (Handle->InBitwiseContext) { throw new InvalidOperationException( diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs index 19cecefc48..0b2e406e4f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs @@ -11,8 +11,7 @@ public struct NetworkBehaviourReference : INetworkSerializable, IEquatable /// Creates a new instance of the struct. @@ -24,7 +23,7 @@ public NetworkBehaviourReference(NetworkBehaviour networkBehaviour) if (networkBehaviour == null) { m_NetworkObjectReference = new NetworkObjectReference((NetworkObject)null); - m_NetworkBehaviourId = s_NullId; + m_NetworkBehaviourId = k_NullId; return; } if (networkBehaviour.NetworkObject == null) @@ -64,7 +63,7 @@ public bool TryGet(out T networkBehaviour, NetworkManager networkManager = nu [MethodImpl(MethodImplOptions.AggressiveInlining)] private static NetworkBehaviour GetInternal(NetworkBehaviourReference networkBehaviourRef, NetworkManager networkManager = null) { - if (networkBehaviourRef.m_NetworkBehaviourId == s_NullId) + if (networkBehaviourRef.m_NetworkBehaviourId == k_NullId) { return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs index 32ab0ed162..db5ed69ced 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs @@ -10,7 +10,7 @@ namespace Unity.Netcode public struct NetworkObjectReference : INetworkSerializable, IEquatable { private ulong m_NetworkObjectId; - private static ulong s_NullId = ulong.MaxValue; + private const ulong k_NullId = ulong.MaxValue; /// /// The of the referenced . @@ -31,7 +31,7 @@ public NetworkObjectReference(NetworkObject networkObject) { if (networkObject == null) { - m_NetworkObjectId = s_NullId; + m_NetworkObjectId = k_NullId; return; } @@ -53,7 +53,7 @@ public NetworkObjectReference(GameObject gameObject) { if (gameObject == null) { - m_NetworkObjectId = s_NullId; + m_NetworkObjectId = k_NullId; return; } @@ -92,7 +92,7 @@ public bool TryGet(out NetworkObject networkObject, NetworkManager networkManage [MethodImpl(MethodImplOptions.AggressiveInlining)] private static NetworkObject Resolve(NetworkObjectReference networkObjectRef, NetworkManager networkManager = null) { - if (networkObjectRef.m_NetworkObjectId == s_NullId) + if (networkObjectRef.m_NetworkObjectId == k_NullId) { return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs new file mode 100644 index 0000000000..ed6b512494 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs @@ -0,0 +1,287 @@ +#if UNIFIED_NETCODE +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Unity.Collections; +using Unity.Netcode.Logging; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Unity.Netcode +{ + /// + /// Handles the management of ghost spawns during the synchronization process. This includes tracking pending NetworkObject spawns + /// that are waiting for their associated ghost to be spawned before they can be fully deserialized. + /// + internal class GhostSpawnManager + { + private readonly NetworkManager m_NetworkManager; + private readonly ContextualLogger m_Log; + + public GhostSpawnManager(NetworkManager networkManager) + { + m_NetworkManager = networkManager; + m_Log = new ContextualLogger((Object)networkManager, networkManager); + } + + private readonly Dictionary m_GhostsPendingSpawn = new(); + + // TODO: We might want to make this a mock interface but temporary solution to validate + // the need to assure we are registering with the right NetworkManager instance when testing (everything + // will use the singleton during Awake and Start when we need to register). + internal delegate void RegisterPendingGhostDelegateHandler(NetworkObject networkObject, ulong networkObjectId); + + internal static RegisterPendingGhostDelegateHandler RegisterPendingGhost; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void RuntimeInitializeOnLoad() => RegisterPendingGhost = null; + + internal void RegisterGhostBridge(ulong networkObjectId, NetworkObject networkObject) + { + m_Log.Info(new Context(LogLevel.Developer, $"Registering {nameof(NetworkObject)} for ghost bridge").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + + // Set when running through integration tests in order to initially bypass the + // normal registration. This is because at this point in the instantiation process, + // NetworkObject's NetworkManager is pointing to the singleton which means all instances + // (even if intended to be for a specific client) will end up registering with whichever + // NetworkManager instance is being pointed to by the singleton. + if (RegisterPendingGhost != null) + { + RegisterPendingGhost(networkObject, networkObjectId); + } + else if (!m_NetworkManager.IsServer) + { + RegisterGhostPendingSpawn(networkObject, networkObjectId); + } + } + + internal void RegisterGhostPendingSpawn(NetworkObject networkObject, ulong networkObjectId) + { + m_Log.Info(new Context(LogLevel.Developer, $"Registering {nameof(NetworkObject)} for ghost spawn").AddTag(networkObject.name).AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + + if (!m_GhostsPendingSpawn.TryAdd(networkObjectId, networkObject)) + { + m_Log.Error(new Context(LogLevel.Normal, $"{nameof(NetworkObject)} has already been registered as a pending ghost!").AddTag(networkObject.name).AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + return; + } + + // TODO-REVIEW-BELOW: *** This is very likely no longer an issue with the new connection sequence *** + // TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn. + // Edge-Case scenario: During initial client synchronization (i.e. !m_NetworkManager.IsConnectedClient). + // + // Description: A client can receive snapshots before finishing the NGO synchronization process. + // This is when an edge case scenario can happen where the initial NGO synchronization information + // can include new scenes to load. If one of those scenes is configured to load in SingleMode, then + // any instantiated ghosts pending synchronization would be instantiated in whatever the currently + // active scene was when the client was processing the synchronization data. If the ghosts pending + // synchronization are in the currently active scene when the new scene is loaded in SingleMode, then + // they would be destroyed. + // + // Current Fix: + // If the client is not yet synchronized, then any ghost pending spawn get migrated into the DDOL. + // + // Further review: + // We need to make sure that we are migrating NetworkObjects into their assigned scene (if scene + // management is enabled). Currently, we assume all instances were in the DDOL and just migrate + // them into the currently active scene upon spawn. + if (!m_NetworkManager.IsConnectedClient && !m_GhostsPendingSynchronization.ContainsKey(networkObjectId)) + { + Object.DontDestroyOnLoad(networkObject.gameObject); + } + else // There is matching spawn data for this pending Ghost, process the pending spawn for this hybrid instance. + { + m_NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, networkObjectId); + if (m_GhostsPendingSynchronization.ContainsKey(networkObjectId)) + { + ProcessGhostPendingSynchronization(networkObjectId); + } + } + } + + internal bool TryGetGhostNetworkObjectForSpawn(NetworkObject.SerializedObject serializedObject, [NotNullWhen(true)] out NetworkObject networkObject) + { + var networkObjectId = serializedObject.NetworkObjectId; + + // TODO-UNIFIED: Get this working somehow (or if not possible prevent this from happening prior to getting to this point) + if (serializedObject.HasInstantiationData) + { + m_Log.Error(new Context(LogLevel.Error, $"{nameof(NetworkObject)} Pre-spawn instantiation data does not work in this version!").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + } + if (!m_GhostsPendingSpawn.Remove(networkObjectId, out networkObject) || networkObject == null) + { + m_Log.Error(new Context(LogLevel.Error, $"Attempting to spawn {nameof(NetworkObject)} with no instance to spawn!").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + return false; + } + + // TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn. + // NOTE: We might be able to use the NetworkSceneHandle to get the associated local scene handle to which we can use to get the targeted scene. + UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(networkObject.gameObject, UnityEngine.SceneManagement.SceneManager.GetActiveScene()); + return true; + } + + private bool m_GhostsArePendingSynchronization; + private readonly Dictionary m_GhostsPendingSynchronization = new(); + internal void RegisterGhostPendingSynchronization(PendingGhostSpawnEntry pendingGhostSpawnEntry) + { + var networkObjectId = pendingGhostSpawnEntry.SerializedObject.NetworkObjectId; + m_Log.Info(new Context(LogLevel.Developer, $"Registering {nameof(NetworkObject.SerializedObject)} for pending synchronization").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + + m_GhostsPendingSynchronization.TryAdd(networkObjectId, pendingGhostSpawnEntry); + m_GhostsArePendingSynchronization = true; + } + + internal NetworkObject ProcessGhostPendingSynchronization(ulong networkObjectId, bool removeUponSpawn = true) + { + var ghostPendingSync = m_GhostsPendingSynchronization[networkObjectId]; + var serializedObject = ghostPendingSync.SerializedObject; + var reader = ghostPendingSync.Buffer; + if (removeUponSpawn) + { + m_GhostsPendingSynchronization.Remove(networkObjectId); + } + + if (serializedObject.IsSceneObject) + { + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(serializedObject.NetworkSceneHandle); + } + var networkObject = NetworkObject.DeserializeAndSpawnObject(serializedObject, reader, m_NetworkManager); + + // TODO-UNIFIED: How do we handle the "all in-scene placed objects are spawned notification"? + //if (serializedObject.IsSceneObject) + //{ + // networkObject.InternalInSceneNetworkObjectsSpawned(); + //} + + // If removing, determine if we have any pending ghosts remaining and dispose of this ghost's pending synchronization buffer. + // If not removing, then we will keep the buffer around until we do remove it (either via spawn or timeout). + if (removeUponSpawn) + { + m_GhostsArePendingSynchronization = m_GhostsPendingSynchronization.Count > 0; + ghostPendingSync.Dispose(); + } + return networkObject; + } + + + private readonly HashSet m_GhostSynchronizationPendingRemoval = new(); + + internal void ProcessAllGhostsPendingSynchronization() + { + var spawnTimeout = m_NetworkManager.NetworkConfig.SpawnTimeout; + if (!m_GhostsArePendingSynchronization) + { + return; + } + foreach (var ghost in m_GhostsPendingSynchronization) + { + var networkObjectId = ghost.Value.SerializedObject.NetworkObjectId; + if (m_GhostsPendingSpawn.ContainsKey(networkObjectId)) + { + // Process it, but don't remove it as we handle that a little later + ProcessGhostPendingSynchronization(ghost.Value.SerializedObject.NetworkObjectId, false); + m_GhostSynchronizationPendingRemoval.Add(networkObjectId); + } + else + if ((ghost.Value.RegistrationTime + spawnTimeout) < Time.realtimeSinceStartup) + { + m_Log.Info(new Context(LogLevel.Developer, $"Registering {nameof(NetworkObject.SerializedObject)} for pending synchronization").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + + // Timed out entries are removed too + m_GhostSynchronizationPendingRemoval.Add(ghost.Key); + } + } + + foreach (var networkObjectId in m_GhostSynchronizationPendingRemoval) + { + var entry = m_GhostsPendingSynchronization[networkObjectId]; + m_GhostsPendingSynchronization.Remove(networkObjectId); + entry.Buffer.Dispose(); + } + m_GhostSynchronizationPendingRemoval.Clear(); + m_GhostsArePendingSynchronization = m_GhostsPendingSynchronization.Count > 0; + } + + internal void MarkNetworkObjectAsDestroying(ulong networkObjectId) + { + m_GhostsPendingSpawn.Remove(networkObjectId); + m_GhostsPendingSynchronization.Remove(networkObjectId); + } + + internal bool IsGhostPendingSpawn(ulong networkObjectId) + { + return m_GhostsPendingSpawn.ContainsKey(networkObjectId); + } + + /// + /// Used in scene managment synchronization. + /// Verifies if this should defer its spawn until the associated GhostObject is created. + /// + /// The object to check + /// 's to save the buffer with data for the deferred spawn. + /// True if the serialized object should should wait for the associated ghost object; false otherwise + internal bool ShouldDeferGhostSceneObject(NetworkObject.SerializedObject serializedObject, FastBufferReader internalBuffer) + { + if (!m_GhostsPendingSpawn.TryGetValue(serializedObject.NetworkObjectId, out var existingObject)) + { + m_Log.Info(new Context(LogLevel.Developer, $"Deferring creation of InScenePlaced {nameof(NetworkObject)} to wait for Ghost.").AddTag("SynchronizeSceneNetworkObjects").AddInfo(nameof(NetworkObject.NetworkObjectId), serializedObject.NetworkObjectId)); + + var newEntry = new PendingGhostSpawnEntry() + { + RegistrationTime = Time.realtimeSinceStartup, + SerializedObject = serializedObject, + Buffer = new FastBufferReader(internalBuffer, Allocator.Persistent, serializedObject.SynchronizationDataSize, internalBuffer.Position) + }; + + RegisterGhostPendingSynchronization(newEntry); + internalBuffer.Seek(internalBuffer.Position + serializedObject.SynchronizationDataSize); + return true; + } + + if (existingObject == null) + { + m_Log.Info(new Context(LogLevel.Developer, $"Dropping creation of InScenePlaced {nameof(NetworkObject)} as it has an entry but no longer exists!").AddTag("SynchronizeSceneNetworkObjects").AddInfo(nameof(NetworkObject.NetworkObjectId), serializedObject.NetworkObjectId)); + + // If it no longer exists, then just remove the entry and skip it. + internalBuffer.Seek(internalBuffer.Position + serializedObject.SynchronizationDataSize); + m_GhostsPendingSpawn.Remove(serializedObject.NetworkObjectId); + return true; + } + + return false; + } + + /// + /// Finalizer that ensures proper cleanup of manager resources + /// + ~GhostSpawnManager() + { + Shutdown(); + } + + internal void Shutdown() + { + m_GhostsPendingSpawn.Clear(); + m_GhostsPendingSynchronization.Clear(); + } + } + + /// + /// Used to store pending ghost spawns that are waiting for their associated (N4E) ghost to be spawned before they can be fully deserialized and + /// spawned during the scene synchronization process. This is necessary because in unified mode we allow for NetworkObjects with ghost components + /// to be synchronized during the scene synchronization process but we can't guarantee the order of messages that the client receives so we + /// need to defer the deserialization of any NetworkObject that has a ghost component until we have received the message that the ghost has + /// been spawned and we have an instance to deserialize this information into. + /// + internal struct PendingGhostSpawnEntry : IDisposable + { + public float RegistrationTime; + public FastBufferReader Buffer; + public NetworkObject.SerializedObject SerializedObject; + + public void Dispose() + { + Buffer.Dispose(); + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs.meta b/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs.meta new file mode 100644 index 0000000000..0d6ca6acb6 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f9f76edcf67a469bb7cd094e59343ce9 +timeCreated: 1780070241 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs index c8aab939f4..7b902a3a52 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +#if UNIFIED_NETCODE +using Unity.NetCode; +#endif using UnityEngine; namespace Unity.Netcode @@ -341,8 +344,8 @@ internal void HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) prefabInstanceHandler.Destroy(networkObjectInstance); } } - else // Otherwise the NetworkObject is the source NetworkPrefab - if (m_PrefabAssetToPrefabHandler.TryGetValue(networkObjectInstanceHash, out var prefabInstanceHandler)) + // Otherwise the NetworkObject is the source NetworkPrefab + else if (m_PrefabAssetToPrefabHandler.TryGetValue(networkObjectInstanceHash, out var prefabInstanceHandler)) { prefabInstanceHandler.Destroy(networkObjectInstance); } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 52e891f8e1..2cf076e1db 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -352,6 +352,10 @@ public NetworkObject[] GetClientOwnedObjects(ulong clientId) /// public NetworkManager NetworkManager { get; } +#if UNIFIED_NETCODE + internal GhostSpawnManager GhostSpawnManager { get; } +#endif + internal readonly Queue ReleasedNetworkObjectIds = new Queue(); private ulong m_NetworkObjectIdCounter; @@ -448,6 +452,7 @@ private bool TryGetNetworkClient(ulong clientId, out NetworkClient networkClient /// /// not used /// not used + [Obsolete("This method is no longer used and will be removed in a future version.")] protected virtual void InternalOnOwnershipChanged(ulong perviousOwner, ulong newOwner) { @@ -906,10 +911,7 @@ internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ow // If not, then there is an issue (user possibly didn't register the prefab properly?) if (networkPrefabReference == null) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Error) - { - NetworkLog.LogErrorServer($"[{nameof(globalObjectIdHash)}={globalObjectIdHash}] Failed to create object locally. {nameof(NetworkPrefab)} could not be found. Is the prefab registered with {NetworkManager.name}?"); - } + NetworkManager.Log.ErrorServer(new Context(LogLevel.Error, $"Failed to create object locally. {nameof(NetworkPrefab)} could not be found. Is the prefab registered with this {nameof(NetworkManager)}").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), globalObjectIdHash).AddTag(NetworkManager.name)); return null; } @@ -956,6 +958,7 @@ internal NetworkObject InstantiateNetworkPrefab([NotNull] GameObject networkPref /// /// For most cases this is client-side only, except when the server is spawning a player. /// + [return: MaybeNull] internal NetworkObject CreateLocalNetworkObject(NetworkObject.SerializedObject serializedObject, byte[] instantiationData = null) { NetworkObject networkObject = null; @@ -966,6 +969,17 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SerializedObject s var parentNetworkId = serializedObject.HasParent ? serializedObject.ParentObjectId : default; var worldPositionStays = (!serializedObject.HasParent) || serializedObject.WorldPositionStays; +#if UNIFIED_NETCODE + if (serializedObject.HasGhost) + { + if (!GhostSpawnManager.TryGetGhostNetworkObjectForSpawn(serializedObject, out networkObject)) + { + // Don't need to log because the inner function will log + return null; + } + } + else +#endif // If scene management is disabled or the NetworkObject was dynamically spawned if (!NetworkManager.NetworkConfig.EnableSceneManagement || !serializedObject.IsSceneObject) { @@ -976,11 +990,7 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SerializedObject s networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(globalObjectIdHash, serializedObject.NetworkSceneHandle); if (networkObject == null) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure for Hash: {globalObjectIdHash}!"); - } - + NetworkLog.LogErrorServer(new Context(LogLevel.Error, $"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure!").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), globalObjectIdHash)); return null; } @@ -991,7 +1001,6 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SerializedObject s networkObject.gameObject.SetActive(true); } } - if (networkObject == null) { return null; @@ -1121,7 +1130,14 @@ internal bool AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n return false; } - if (!sceneObject && NetworkManager.LogLevel <= LogLevel.Error) + if (playerObject && networkObject.InScenePlaced) + { + NetworkLog.LogError(new Context(LogLevel.Developer, "Player prefab is marked as belonging to a scene. This may cause issues.").AddNetworkObject(networkObject).AddInfo("SceneName", networkObject.SceneOrigin.name)); + networkObject.InScenePlaced = false; + } + NetworkLog.InternalAssert(sceneObject == networkObject.InScenePlaced, "Legacy sceneObject value should match calculated InScenePlaced value."); + + if (!networkObject.InScenePlaced && NetworkManager.LogLevel <= LogLevel.Error) { var networkObjectChildren = networkObject.GetComponentsInChildren(); if (networkObjectChildren.Length > 1) @@ -1136,48 +1152,13 @@ internal bool AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n networkObject.NetworkManagerOwner = NetworkManager; networkObject.InvokeBehaviourNetworkPreSpawn(); - // DANGO-TODO: It would be nice to allow users to specify which clients are observers prior to spawning - // For now, this is the best place I could find to add all connected clients as observers for newly - // instantiated and spawned NetworkObjects on the authoritative side. - if (NetworkManager.DistributedAuthorityMode) + if (NetworkManager.DistributedAuthorityMode && NetworkManager.NetworkConfig.EnableSceneManagement && networkObject.InScenePlaced) { - if (NetworkManager.NetworkConfig.EnableSceneManagement && sceneObject) - { - networkObject.SceneOriginHandle = networkObject.gameObject.scene.handle; - networkObject.NetworkSceneHandle = NetworkManager.SceneManager.ClientSceneHandleToServerSceneHandle[networkObject.gameObject.scene.handle]; - } - - // Always add the owner/authority even if SpawnWithObservers is false - // (authority should not take into consideration networkObject.CheckObjectVisibility when SpawnWithObservers is false) - if (!networkObject.SpawnWithObservers) - { - networkObject.AddObserver(ownerClientId); - } - else - { - foreach (var clientId in NetworkManager.ConnectionManager.ConnectedClientIds) - { - // If SpawnWithObservers is enabled, then authority does take networkObject.CheckObjectVisibility into consideration - if (networkObject.CheckObjectVisibility != null && !networkObject.CheckObjectVisibility.Invoke(clientId)) - { - continue; - } - networkObject.AddObserver(clientId); - } - - // Sanity check to make sure the owner is always included - // Intentionally checking as opposed to just assigning in order to generate notification. - if (!networkObject.Observers.Contains(ownerClientId)) - { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"Client-{ownerClientId} is the owner of {networkObject.name} but is not an observer! Adding owner, but there is a bug in observer synchronization!"); - } - networkObject.AddObserver(ownerClientId); - } - } + networkObject.SceneOriginHandle = networkObject.gameObject.scene.handle; + networkObject.NetworkSceneHandle = NetworkManager.SceneManager.ClientSceneHandleToServerSceneHandle[networkObject.gameObject.scene.handle]; } + if (!SpawnNetworkObjectLocallyCommon(networkObject, networkId, sceneObject, playerObject, ownerClientId, destroyWithScene)) { if (NetworkManager.LogLevel <= LogLevel.Error) @@ -1189,6 +1170,16 @@ internal bool AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n return false; } +#if UNIFIED_NETCODE + // If this is a hybrid prefab, the spawn authority is responsible for assigning the network object id to the network object bridge so that it + // can be used to link the N4E ghost to the NetworkObject. This is needed because in the hybrid prefab case, the ghost can be spawned before + // the NetworkObject is fully spawned. + if (networkObject.HasGhost) + { + networkObject.NetworkObjectBridge.NetworkObjectId.Value = networkObject.NetworkObjectId; + } +#endif + // When done spawning invoke post spawn networkObject.InvokeBehaviourNetworkPostSpawn(); @@ -1199,20 +1190,17 @@ internal bool AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n /// /// Only spawn non-authority instances should invoke this. /// This is invoked to instantiate an authority spawned , and - /// is only invoked by: + /// is only invoked by: /// /// - /// IMPORTANT: Pre spawn methods need to be invoked from within . + /// IMPORTANT: Pre spawn methods need to be invoked from within . /// /// boolean indicating whether the spawn succeeded - internal bool NonAuthorityLocalSpawn(in NetworkObject.SerializedObject serializedObject, out NetworkObject networkObject, FastBufferReader reader, bool destroyWithScene) + internal bool NonAuthorityLocalSpawn(in NetworkObject.SerializedObject serializedObject, [MaybeNullWhen(false)] out NetworkObject networkObject, FastBufferReader reader, bool destroyWithScene) { if (SpawnedObjects.ContainsKey(serializedObject.NetworkObjectId)) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogWarning($"Trying to spawn a {nameof(NetworkObject)} with a {nameof(NetworkObject.NetworkObjectId)} of {serializedObject.NetworkObjectId} but an object with that id is already in the spawned list. This should not happen!"); - } + NetworkManager.Log.Warning(new Context(LogLevel.Error, $"Trying to spawn a {nameof(NetworkObject)} but an object with that {nameof(NetworkObject.NetworkObjectId)} is already in the spawned list. This should not happen!")); networkObject = null; return false; } @@ -1229,14 +1217,11 @@ internal bool NonAuthorityLocalSpawn(in NetworkObject.SerializedObject serialize // Log the error that the NetworkObject failed to construct if (networkObject == null) { - if (NetworkManager.LogLevel <= LogLevel.Normal) - { - NetworkLog.LogError($"[{nameof(NetworkObject.GlobalObjectIdHash)}={serializedObject.Hash}] Failed to spawn {nameof(NetworkObject)}!"); - } - + NetworkManager.Log.ErrorServer(new Context(LogLevel.Normal, $"Failed to spawn {nameof(NetworkObject)}!").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), serializedObject.Hash)); return false; } + networkObject.InScenePlaced = serializedObject.IsSceneObject; networkObject.NetworkManagerOwner = NetworkManager; // This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning @@ -1261,11 +1246,7 @@ internal bool NonAuthorityLocalSpawn(in NetworkObject.SerializedObject serialize if (networkObject.IsSpawned) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogErrorServer($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!"); - } - + NetworkManager.Log.ErrorServer(new Context(LogLevel.Normal, $"{nameof(NetworkObject)} is already spawned!").AddNetworkObject(networkObject)); // Mark the spawn as a success if the object is already spawned return true; } @@ -1294,9 +1275,10 @@ internal bool NonAuthorityLocalSpawn(in NetworkObject.SerializedObject serialize } /// - /// Handles the all the final setup and spawning needed for + /// Handles all the final setup and spawning needed for spawning a NetworkObject locally. /// - /// boolean indicating whether the spawn succeeded. Internal dev note: THIS IS A CATCH FOR OURSELVES. DON'T PULL OUT + /// boolean indicating whether the spawn succeeded. + // Internal dev note: THIS IS A CATCH FOR OURSELVES. DON'T PULL OUT internal bool SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene) { // TODO: Replace the following checks with internal Netcode asserts @@ -1310,7 +1292,7 @@ internal bool SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong return false; } - if (networkId == default) + if (networkId == 0) { if (NetworkManager.LogLevel <= LogLevel.Error) { @@ -1319,95 +1301,29 @@ internal bool SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong return false; } - networkObject.IsSceneObject = sceneObject; - - // Always check to make sure our scene of origin is properly set for in-scene placed NetworkObjects - // Note: Always check SceneOriginHandle directly at this specific location. - if (networkObject.IsSceneObject != false && networkObject.SceneOriginHandle.IsEmpty()) - { - networkObject.SceneOrigin = networkObject.gameObject.scene; - } - - networkObject.NetworkObjectId = networkId; - - networkObject.DestroyWithScene = sceneObject || destroyWithScene; +#pragma warning disable CS0618 // Type or member is obsolete + // Obsolete with warning means we need the underlying behaviour to keep existing + // TODO: remove in the 3.x branch + networkObject.SetSceneObjectStatus(sceneObject); +#pragma warning restore CS0618 // Type or member is obsolete - networkObject.IsPlayerObject = playerObject; + networkObject.SetupOnSpawn(networkId, playerObject, ownerClientId, destroyWithScene); - networkObject.OwnerClientId = ownerClientId; - - // When spawned, previous owner is always the first assigned owner - networkObject.PreviousOwnerId = ownerClientId; - - // If this the player and the client is the owner, then lock ownership by default - if (NetworkManager.DistributedAuthorityMode && NetworkManager.LocalClientId == ownerClientId && playerObject) - { - networkObject.AddOwnershipExtended(NetworkObject.OwnershipStatusExtended.Locked); - } - - networkObject.IsSpawned = true; SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject); SpawnedObjectsList.Add(networkObject); - - // If we are not running in DA mode, this is the server, and the NetworkObject has SpawnWithObservers set, - // then add all connected clients as observers - if (!NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer && networkObject.SpawnWithObservers) - { - // If running as a server only, then make sure to always add the server's client identifier - if (!NetworkManager.IsHost) - { - networkObject.AddObserver(NetworkManager.LocalClientId); - } - - // Add client observers - for (int i = 0; i < NetworkManager.ConnectedClientsIds.Count; i++) - { - // If CheckObjectVisibility has a callback, then allow that method determine who the observers are. - if (networkObject.CheckObjectVisibility != null && !networkObject.CheckObjectVisibility(NetworkManager.ConnectedClientsIds[i])) - { - continue; - } - networkObject.AddObserver(NetworkManager.ConnectedClientsIds[i]); - } - } - networkObject.ApplyNetworkParenting(); + NetworkObject.CheckOrphanChildren(); AddNetworkObjectToSceneChangedUpdates(networkObject); - + UpdateOwnershipTable(networkObject, ownerClientId); networkObject.InvokeBehaviourNetworkSpawn(); - // propagate the IsSceneObject setting to child NetworkObjects - var children = networkObject.GetComponentsInChildren(); - foreach (var childObject in children) - { - // Do not propagate the in-scene object setting if a child was dynamically spawned. - if (childObject.IsSceneObject.HasValue && !childObject.IsSceneObject.Value) - { - continue; - } - childObject.IsSceneObject = sceneObject; - } - - // Only dynamically spawned NetworkObjects are allowed - if (!sceneObject) - { - networkObject.SubscribeToActiveSceneForSynch(); - } - if (networkObject.IsPlayerObject) { UpdateNetworkClientPlayer(networkObject); } - // If we are an in-scene placed NetworkObject and our InScenePlacedSourceGlobalObjectIdHash is set - // then assign this to the PrefabGlobalObjectIdHash - if (networkObject.IsSceneObject.Value && networkObject.InScenePlacedSourceGlobalObjectIdHash != 0) - { - networkObject.PrefabGlobalObjectIdHash = networkObject.InScenePlacedSourceGlobalObjectIdHash; - } - return true; } @@ -1467,7 +1383,7 @@ internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject } var message = new CreateObjectMessage { - ObjectInfo = networkObject.Serialize(clientId, NetworkManager.DistributedAuthorityMode), + ObjectInfo = networkObject.SerializeSpawnedObject(clientId, NetworkManager.DistributedAuthorityMode), IncludesSerializedObject = true, UpdateObservers = NetworkManager.DistributedAuthorityMode, ObserverIds = NetworkManager.DistributedAuthorityMode ? networkObject.Observers.ToArray() : null, @@ -1493,7 +1409,7 @@ internal void SendSpawnCallForObserverUpdate(ulong[] newObservers, NetworkObject var message = new CreateObjectMessage { - ObjectInfo = networkObject.Serialize(), + ObjectInfo = networkObject.SerializeSpawnedObject(), ObserverIds = networkObject.Observers.ToArray(), NewObserverIds = newObservers.ToArray(), IncludesSerializedObject = true, @@ -1551,14 +1467,16 @@ internal void DespawnObject(NetworkObject networkObject, bool destroyObject = fa } // Makes scene objects ready to be reused - internal void ServerResetShudownStateForSceneObjects() + internal void ServerResetShutdownStateForSceneObjects() { - var networkObjects = FindObjects.ByType(orderByIdentifier: true).Where((c) => c.IsSceneObject != null && c.IsSceneObject == true); - foreach (var sobj in networkObjects) + var networkObjects = FindObjects.ByType(orderByIdentifier: true, includeInactive: true); + foreach (var obj in networkObjects) { - sobj.IsSpawned = false; - sobj.DestroyWithScene = false; - sobj.IsSceneObject = null; + // Only reset things that have been spawned are in-scene placed, and is assigned to the relative NetworkManager (for integration testing purposes) + if (obj.HasBeenSpawned && obj.InScenePlaced && obj.NetworkManagerOwner == NetworkManager) + { + obj.ResetOnShutdown(); + } } } @@ -1574,7 +1492,7 @@ internal void ServerDestroySpawnedSceneObjects() foreach (var networkObject in spawnedObjects) { - if (networkObject.IsSceneObject != null && networkObject.IsSceneObject.Value && networkObject.DestroyWithScene + if (networkObject.InScenePlaced && networkObject.DestroyWithScene && networkObject.gameObject.scene != NetworkManager.SceneManager.DontDestroyOnLoadScene) { if (networkObject.IsSpawned && networkObject.HasAuthority) @@ -1618,7 +1536,7 @@ internal void DespawnAndDestroyNetworkObjects() { // If it is an in-scene placed NetworkObject then just despawn and let it be destroyed when the scene // is unloaded. Otherwise, despawn and destroy it. - var shouldDestroy = !(networkObject.IsSceneObject == null || (networkObject.IsSceneObject != null && networkObject.IsSceneObject.Value)); + var shouldDestroy = !networkObject.InScenePlaced; // If we are going to destroy this NetworkObject, check for any in-scene placed children that need to be removed if (shouldDestroy) @@ -1634,7 +1552,7 @@ internal void DespawnAndDestroyNetworkObjects() // If the child is an in-scene placed NetworkObject then remove the child from the parent (which was dynamically spawned) // and set its parent to root - if (childObject.IsSceneObject != null && childObject.IsSceneObject.Value) + if (childObject.InScenePlaced) { childObject.TryRemoveParentCachedWorldPositionStays(); } @@ -1653,31 +1571,37 @@ internal void DestroySceneObjects() for (int i = 0; i < networkObjects.Length; i++) { - if (networkObjects[i].NetworkManager == NetworkManager) + if (networkObjects[i].NetworkManager == NetworkManager && networkObjects[i].InScenePlaced) { - if (networkObjects[i].IsSceneObject == null || networkObjects[i].IsSceneObject.Value == true) + if (NetworkManager.PrefabHandler.ContainsHandler(networkObjects[i])) { - if (NetworkManager.PrefabHandler.ContainsHandler(networkObjects[i])) + if (SpawnedObjects.ContainsKey(networkObjects[i].NetworkObjectId)) { - if (SpawnedObjects.ContainsKey(networkObjects[i].NetworkObjectId)) - { - // This method invokes HandleNetworkPrefabDestroy, we only want to handle this once. - OnDespawnObject(networkObjects[i], false); - } - else // If not spawned, then just invoke the handler - { - NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObjects[i]); - } + // This method invokes HandleNetworkPrefabDestroy, we only want to handle this once. + OnDespawnObject(networkObjects[i], false); } - else + else // If not spawned, then just invoke the handler { - Object.Destroy(networkObjects[i].gameObject); + NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObjects[i]); } } + else + { + Object.Destroy(networkObjects[i].gameObject); + } } } } + internal void MarkNetworkObjectAsDestroying(NetworkObject networkObject) + { + // Always attempt to remove from scene changed updates + RemoveNetworkObjectFromSceneChangedUpdates(networkObject); +#if UNIFIED_NETCODE + GhostSpawnManager.MarkNetworkObjectAsDestroying(networkObject.NetworkObjectId); +#endif + } + internal void ServerSpawnSceneObjectsOnStartSweep() { var networkObjects = FindObjects.ByType(orderByIdentifier: true); @@ -1692,7 +1616,7 @@ internal void ServerSpawnSceneObjectsOnStartSweep() // This used to be two loops. // The first added all NetworkObjects to a list and the second spawned all NetworkObjects in the list. // Now, a parent will set its children's IsSceneObject value when spawned, so we check for null or for true. - if (networkObject.IsSceneObject == null || (networkObject.IsSceneObject.HasValue && networkObject.IsSceneObject.Value)) + if (networkObject.InScenePlaced) { var ownerId = networkObject.OwnerClientId; if (NetworkManager.DistributedAuthorityMode) @@ -1743,7 +1667,7 @@ internal void OnDespawnNonAuthorityObject([NotNull] NetworkObject networkObject, } } - if (networkObject.IsSceneObject == false) + if (!networkObject.InScenePlaced) { // If the object is not an in-scene placed NetworkObject, then we always destroy the object on the non-authority side destroyGameObject = true; @@ -1785,7 +1709,7 @@ internal void OnDespawnObject([NotNull] NetworkObject networkObject, bool destro // DistributedAuthorityMode: All clients need to remove the parent locally due to mixed-authority hierarchies and race-conditions if (!NetworkManager.ShutdownInProgress && (NetworkManager.IsServer || distributedAuthority)) { - if (destroyGameObject && networkObject.IsSceneObject == true && !NetworkManager.SceneManager.IsSceneUnloading(networkObject)) + if (destroyGameObject && networkObject.InScenePlaced && !NetworkManager.SceneManager.IsSceneUnloading(networkObject)) { if (NetworkManager.LogLevel <= LogLevel.Normal) { @@ -1825,8 +1749,7 @@ internal void OnDespawnObject([NotNull] NetworkObject networkObject, bool destro NetworkLog.LogError($"{nameof(NetworkObject)} #{spawnedNetObj.NetworkObjectId} could not be moved to the root when its parent {nameof(NetworkObject)} #{networkObject.NetworkObjectId} was being destroyed"); } } - else - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) { NetworkLog.LogWarning($"{nameof(NetworkObject)} #{spawnedNetObj.NetworkObjectId} moved to the root because its parent {nameof(NetworkObject)} #{networkObject.NetworkObjectId} is destroyed"); } @@ -1914,7 +1837,14 @@ internal void OnDespawnObject([NotNull] NetworkObject networkObject, bool destro { RemovePlayerObject(networkObject, destroyGameObject); } - +#if UNIFIED_NETCODE + // Unified netcode handles destroying the instance of the object on the non-authority side when the object has a ghost representation. + if (destroyGameObject && networkObject.HasGhost && !NetworkManager.IsServer) + { + // exit early + return; + } +#endif var gobj = networkObject.gameObject; if (destroyGameObject && gobj != null) { @@ -2045,6 +1975,9 @@ internal void HandleNetworkObjectShow(bool forceSend = false) internal NetworkSpawnManager(NetworkManager networkManager) { NetworkManager = networkManager; +#if UNIFIED_NETCODE + GhostSpawnManager = new GhostSpawnManager(networkManager); +#endif } /// @@ -2112,7 +2045,7 @@ internal void GetObjectDistribution(ulong clientId, ref Dictionary private const double k_DefaultAdjustmentRatio = 0.01d; -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG private static ProfilerMarker s_SyncTime = new ProfilerMarker($"{nameof(NetworkManager)}.SyncTime"); #endif private double m_PreviousTimeSec; @@ -182,7 +182,7 @@ internal void UpdateTime() /// private void OnTickSyncTime() { -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_SyncTime.Begin(); #endif @@ -196,7 +196,7 @@ private void OnTickSyncTime() m_ConnectionManager.SendMessage(ref message, m_NetworkDelivery, m_ConnectionManager.ConnectedClientIds); } -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG s_SyncTime.End(); #endif } diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs index 7b2d6b1719..e94955618c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/SinglePlayer/SinglePlayerTransport.cs @@ -20,7 +20,7 @@ public class SinglePlayerTransport : NetworkTransport /// public override ulong ServerClientId { get; } = 0; - internal static string NotStartingAsHostErrorMessage = $"When using {nameof(SinglePlayerTransport)}, you must start a hosted session so both client and server are available locally."; + internal static readonly string NotStartingAsHostErrorMessage = $"When using {nameof(SinglePlayerTransport)}, you must start a hosted session so both client and server are available locally."; private struct MessageData { @@ -31,6 +31,10 @@ private struct MessageData } private static Dictionary> s_MessageQueue = new Dictionary>(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() => s_MessageQueue = new Dictionary>(); +#endif private ulong m_TransportId = 0; private NetworkManager m_NetworkManager; diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs index 8e07c0d3e4..c07c8c1484 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/INetworkStreamDriverConstructor.cs @@ -36,9 +36,8 @@ namespace Unity.Netcode.Transports.UTP /// var settings = transport.GetDefaultNetworkSettings(); /// driver = NetworkDriver.Create(new IPCNetworkInterface(), settings); /// - /// driver.RegisterPipelineStage(new NetworkMetricsPipelineStage()); - /// /// transport.GetDefaultPipelineConfigurations( + /// ref driver, /// out var unreliableFragmentedPipelineStages, /// out var unreliableSequencedFragmentedPipelineStages, /// out var reliableSequencedPipelineStages); diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs index c602aef739..de1e7899b9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs @@ -1,5 +1,3 @@ -#if MULTIPLAYER_TOOLS -#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 using AOT; using Unity.Burst; using Unity.Collections.LowLevel.Unsafe; @@ -70,5 +68,3 @@ private static void InitializeConnection(byte* staticInstanceBuffer, int staticI } } } -#endif -#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index b39fe2bc94..cac615f394 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -1,7 +1,7 @@ // NetSim Implementation compilation boilerplate // All references to UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED should be defined in the same way, // as any discrepancies are likely to result in build failures -#if UNITY_EDITOR || (DEVELOPMENT_BUILD && !UNITY_MP_TOOLS_NETSIM_DISABLED_IN_DEVELOP) || (!DEVELOPMENT_BUILD && UNITY_MP_TOOLS_NETSIM_ENABLED_IN_RELEASE) +#if UNITY_EDITOR || (DEBUG && !UNITY_MP_TOOLS_NETSIM_DISABLED_IN_DEVELOP) || (!DEBUG && UNITY_MP_TOOLS_NETSIM_ENABLED_IN_RELEASE) #define UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED #endif @@ -68,7 +68,7 @@ public enum ProtocolType // frame at 60 FPS. This will be a large over-estimation in any realistic scenario. private const int k_MaxReliableThroughput = (NetworkParameterConstants.MTU * 64 * 60) / 1000; // bytes per millisecond - private static ConnectionAddressData s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, WebSocketPath = "/", ServerListenAddress = string.Empty }; + private static readonly ConnectionAddressData k_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, WebSocketPath = "/", ServerListenAddress = string.Empty }; #pragma warning disable IDE1006 // Naming Styles /// @@ -308,7 +308,7 @@ public NetworkEndpoint ListenEndPoint /// This is where you can change IP Address, Port, or server's listen address. /// /// - public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData; + public ConnectionAddressData ConnectionData = k_DefaultConnectionAddressData; /// /// Parameters for the Network Simulator @@ -369,6 +369,19 @@ private struct PacketLossCache #endif internal static event Action TransportInitialized; internal static event Action TransportDisposed; +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + s_DriverConstructor = null; +#if UNITY_6000_2_OR_NEWER + OnDriverInitialized = null; + OnDisposingDriver = null; +#endif + TransportInitialized = null; + TransportDisposed = null; + } +#endif /// /// Provides access to the for this instance. @@ -657,6 +670,23 @@ public void GetDefaultPipelineConfigurations( reliableSequencedPipelineStages = new(reliableSequenced, Allocator.Temp); } + /// + /// Driver for which the pipeline configurations are being retrieved. + public void GetDefaultPipelineConfigurations( + ref NetworkDriver driver, + out NativeArray unreliableFragmentedPipelineStages, + out NativeArray unreliableSequencedFragmentedPipelineStages, + out NativeArray reliableSequencedPipelineStages) + { +#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 + driver.RegisterPipelineStage(new NetworkMetricsPipelineStage()); +#endif + GetDefaultPipelineConfigurations( + out unreliableFragmentedPipelineStages, + out unreliableSequencedFragmentedPipelineStages, + out reliableSequencedPipelineStages); + } + private NetworkPipeline SelectSendPipeline(NetworkDelivery delivery) { switch (delivery) @@ -839,7 +869,7 @@ private bool ParseCommandLineOptionsPort(out ushort port) return true; } #else - if (CommandLineOptions.Instance.GetArg(k_OverridePortArg) is string argValue) + if (CommandLineOptions.TryGetArg(k_OverridePortArg, out var argValue)) { port = (ushort)Convert.ChangeType(argValue, typeof(ushort)); return true; @@ -851,7 +881,7 @@ private bool ParseCommandLineOptionsPort(out ushort port) private bool ParseCommandLineOptionsAddress(out string ipValue) { - if (CommandLineOptions.Instance.GetArg(k_OverrideIpAddressArg) is string argValue) + if (CommandLineOptions.TryGetArg(k_OverrideIpAddressArg, out var argValue)) { ipValue = argValue; return true; @@ -1860,11 +1890,8 @@ public void CreateDriver( #endif } -#if MULTIPLAYER_TOOLS_1_0_0_PRE_7 - driver.RegisterPipelineStage(new NetworkMetricsPipelineStage()); -#endif - GetDefaultPipelineConfigurations( + ref driver, out var unreliableFragmentedPipelineStages, out var unreliableSequencedFragmentedPipelineStages, out var reliableSequencedPipelineStages); diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified.meta b/com.unity.netcode.gameobjects/Runtime/Transports/Unified.meta new file mode 100644 index 0000000000..b4340f3a18 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5ce5c8d0dc8948aab8482e617eccabcc +timeCreated: 1772129529 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs new file mode 100644 index 0000000000..bdaebdc3ca --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -0,0 +1,476 @@ +#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +using System; +using System.Collections.Generic; +using Unity.Burst; +using Unity.Burst.Intrinsics; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Entities; +using Unity.NetCode; +using Unity.Netcode.Transports.UTP; +using UnityEngine; + +namespace Unity.Netcode.Unified +{ + [BurstCompile] + internal unsafe struct FixedBytes1280 + { + public fixed byte Buffer[1280]; + public int Length; + + // Returns a direct pointer to the data in the buffer. + // Implemented as a static with an in-parameter to avoid the buffer being copied while keeping its memory allocation fixed/non-heap + // Note that the buffer MUST outlive the returned pointer, as it is an alias. + public static byte* GetUnsafePtr(in FixedBytes1280 data) + { + fixed (byte* buffer = data.Buffer) + { + return buffer; + } + } + + // Returns a native array that is an alias of the existing data without copying it + // Implemented as a static with an in-parameter to avoid the buffer being copied while keeping its memory allocation fixed/non-heap + // Note that the buffer MUST outlive the returned array, as it is an alias. + public static NativeArray ToNativeArray(in FixedBytes1280 data) + { + var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(GetUnsafePtr(data), data.Length, Allocator.None); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + var safety = CollectionHelper.CreateSafetyHandle(Allocator.None); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, safety); +#endif + return array; + } + } + + internal struct TransportRpcData : IBufferElementData + { + public FixedBytes1280 Buffer; + } + + [BurstCompile] + internal struct TransportRpc : IOutOfBandRpcCommand, IRpcCommandSerializer + { + public TransportRpcData Value; + + public unsafe void Serialize(ref DataStreamWriter writer, in RpcSerializerState state, in TransportRpc data) + { + writer.WriteInt(data.Value.Buffer.Length); + var span = new Span(FixedBytes1280.GetUnsafePtr(data.Value.Buffer), data.Value.Buffer.Length); + writer.WriteBytes(span); + } + + public unsafe void Deserialize(ref DataStreamReader reader, in RpcDeserializerState state, ref TransportRpc data) + { + var length = reader.ReadInt(); + data.Value.Buffer = new FixedBytes1280 + { + Length = length + }; + + var span = new Span(FixedBytes1280.GetUnsafePtr(data.Value.Buffer), length); + reader.ReadBytes(span); + } + + [BurstCompile(DisableDirectCall = true)] + private static void InvokeExecute(ref RpcExecutor.Parameters parameters) + { + var element = new TransportRpc(); + element.Deserialize(ref parameters.Reader, parameters.DeserializerState, ref element); + parameters.CommandBuffer.AppendToBuffer(parameters.JobIndex, parameters.Connection, element.Value); + } + + private static readonly PortableFunctionPointer k_InvokeExecuteFunctionPointer = new PortableFunctionPointer(InvokeExecute); + + public PortableFunctionPointer CompileExecute() + { + return k_InvokeExecuteFunctionPointer; + } + } + + [UpdateInGroup(typeof(RpcCommandRequestSystemGroup))] + [CreateAfter(typeof(RpcSystem))] + [BurstCompile] + internal partial struct TransportRpcCommandRequestSystem : ISystem + { + private RpcCommandRequest m_Request; + + [BurstCompile] + internal struct SendRpc : IJobChunk + { + public RpcCommandRequest.SendRpcData Data; + + public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + Data.Execute(chunk, unfilteredChunkIndex); + } + } + + public void OnCreate(ref SystemState state) + { + m_Request.OnCreate(ref state); + } + + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + var sendJob = new SendRpc { Data = m_Request.InitJobData(ref state) }; + state.Dependency = sendJob.Schedule(m_Request.Query, state.Dependency); + } + } + + [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation | WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation)] + [UpdateInGroup(typeof(SimulationSystemGroup), OrderLast = true)] + [UpdateBefore(typeof(RpcSystem))] + internal partial class UnifiedNetcodeUpdateSystem : SystemBase + { + public void OnCreate(ref SystemState state) + { + state.RequireForUpdate(); + state.RequireForUpdate(); + } + + public UnifiedNetcodeTransport Transport; + public NetworkManager NetworkManager; + + public List DisconnectQueue = new List(); + + public void Disconnect(Connection connection) + { + DisconnectQueue.Add(connection); + } + + public void SendRpc(TransportRpc rpc, Entity connectionEntity) + { + var rpcQueue = SystemAPI.GetSingleton().GetRpcQueue(); + if (!EntityManager.HasComponent(connectionEntity)) + { + // Disconnected, can't send. + return; + } + var ghostInstance = GetComponentLookup(); + var rpcDataStreamBuffer = EntityManager.GetBuffer(connectionEntity); + rpcQueue.Schedule(rpcDataStreamBuffer, ghostInstance, rpc); + } + + protected override void OnUpdate() + { + NetworkManager.MessageManager.ProcessSendQueues(); + + using var commandBuffer = new EntityCommandBuffer(Allocator.Temp); + foreach(var (networkId, _, entity) in SystemAPI.Query, RefRO>().WithEntityAccess()) + { + var connectionId = networkId.ValueRO.Value; + DynamicBuffer rpcs = EntityManager.GetBuffer(entity); + foreach (var rpc in rpcs) + { + var buffer = rpc.Buffer; + try + { + Transport.DispatchMessage(connectionId, buffer); + } + catch(Exception e) + { + Debug.LogException(e); + } + } + rpcs.Clear(); + } + + foreach (var connection in DisconnectQueue) + { + commandBuffer.AddComponent(connection.ConnectionEntity); + } + DisconnectQueue.Clear(); + + commandBuffer.Playback(EntityManager); + + } + } + + internal class UnifiedNetcodeTransport : NetworkTransport + { + private const int k_MaxPacketSize = 1280; + + private int m_ServerClientId = -1; + public override ulong ServerClientId => (ulong)m_ServerClientId; + + private NetworkManager m_NetworkManager; + + private IRealTimeProvider m_RealTimeProvider; + + private class ConnectionInfo + { + public BatchedSendQueue SendQueue; + public BatchedReceiveQueue ReceiveQueue; + public Connection Connection; + public Dictionary DeferredMessages; + } + + private Dictionary m_Connections; + + internal void DispatchMessage(int connectionId, in FixedBytes1280 buffer) + { + var connectionInfo = m_Connections[connectionId]; + + using var arr = FixedBytes1280.ToNativeArray(buffer); + var reader = new DataStreamReader(arr); + if (connectionInfo.ReceiveQueue == null) + { + connectionInfo.ReceiveQueue = new BatchedReceiveQueue(reader); + } + else + { + connectionInfo.ReceiveQueue.PushReader(reader); + } + + var message = connectionInfo.ReceiveQueue.PopMessage(); + while (message.Count != 0) + { + InvokeOnTransportEvent(NetworkEvent.Data, (ulong)connectionId, message, + m_RealTimeProvider.RealTimeSinceStartup); + message = connectionInfo.ReceiveQueue.PopMessage(); + } + } + + public override unsafe void Send(ulong clientId, ArraySegment payload, NetworkDelivery networkDelivery) + { + if (!m_Connections.TryGetValue((int)clientId, out ConnectionInfo connectionInfo)) + { + return; + } + + connectionInfo.SendQueue.PushMessage(payload); + + while (!connectionInfo.SendQueue.IsEmpty) + { + var rpc = new TransportRpc(); + + var writer = new DataStreamWriter(FixedBytes1280.GetUnsafePtr(rpc.Value.Buffer), k_MaxPacketSize); + + var amount = connectionInfo.SendQueue.FillWriterWithBytes(ref writer, k_MaxPacketSize); + rpc.Value.Buffer.Length = amount; + + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.SendRpc(rpc, connectionInfo.Connection.ConnectionEntity); + + connectionInfo.SendQueue.Consume(amount); + } + } + + public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment payload, out float receiveTime) + { + clientId = 0; + payload = default; + receiveTime = 0; + return NetworkEvent.Nothing; + } + + private void OnClientConnectedToServer(Connection connection, NetCodeConnectionEvent connectionEvent) + { + m_Connections[connectionEvent.Id.Value] = new ConnectionInfo + { + ReceiveQueue = null, + SendQueue = new BatchedSendQueue(BatchedSendQueue.MaximumMaximumCapacity), + Connection = connection + }; + m_ServerClientId = connectionEvent.Id.Value; + InvokeOnTransportEvent(NetworkEvent.Connect, (ulong)connectionEvent.Id.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.EntityManager.AddBuffer(connection.ConnectionEntity); + } + + private void OnServerNewClientConnection(Connection connection, NetCodeConnectionEvent connectionEvent) + { + m_Connections[connectionEvent.Id.Value] = new ConnectionInfo + { + ReceiveQueue = null, + SendQueue = new BatchedSendQueue(BatchedSendQueue.MaximumMaximumCapacity), + Connection = connection + }; ; + InvokeOnTransportEvent(NetworkEvent.Connect, (ulong)connectionEvent.Id.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.EntityManager.AddBuffer(connection.ConnectionEntity); + } + + private const string k_InvalidRpcMessage = "An invalid RPC was received"; + private const string k_HandshakeTimeoutMessage = "The connection was closed because the handshake timed out."; + private const string k_ApprovalFailureMessage = "The connection was closed because the connection was not approved by the server."; + private const string k_ApprovalTimeoutMessage = "The connection was closed because the connection approval process timed out."; + + private string GetDisconnectMessageFromNetworkStreamDisconnectReason(NetworkStreamDisconnectReason reason) + { + switch (reason) + { + case NetworkStreamDisconnectReason.ConnectionClose: + return UnityTransportNotificationHandler.DisconnectedMessage; + case NetworkStreamDisconnectReason.Timeout: + return UnityTransportNotificationHandler.TimeoutMessage; + case NetworkStreamDisconnectReason.MaxConnectionAttempts: + return UnityTransportNotificationHandler.MaxConnectionAttemptsMessage; + case NetworkStreamDisconnectReason.ClosedByRemote: + return UnityTransportNotificationHandler.ClosedRemoteConnectionMessage; + case NetworkStreamDisconnectReason.BadProtocolVersion: + return UnityTransportNotificationHandler.ProtocolErrorMessage; + case NetworkStreamDisconnectReason.InvalidRpc: + return k_InvalidRpcMessage; + case NetworkStreamDisconnectReason.AuthenticationFailure: + return UnityTransportNotificationHandler.AuthenticationFailureMessage; + case NetworkStreamDisconnectReason.ProtocolError: + return UnityTransportNotificationHandler.ProtocolErrorMessage; + case NetworkStreamDisconnectReason.HandshakeTimeout: + return k_HandshakeTimeoutMessage; + case NetworkStreamDisconnectReason.ApprovalFailure: + return k_ApprovalFailureMessage; + case NetworkStreamDisconnectReason.ApprovalTimeout: + return k_ApprovalTimeoutMessage; + } + return "Unknown reason"; + } + + private DisconnectEvents GetDisconnectEventFromNetworkStreamDisconnectReason(NetworkStreamDisconnectReason reason) + { + switch (reason) + { + case NetworkStreamDisconnectReason.ConnectionClose: + return DisconnectEvents.Disconnected; + case NetworkStreamDisconnectReason.Timeout: + return DisconnectEvents.ProtocolTimeout; + case NetworkStreamDisconnectReason.MaxConnectionAttempts: + return DisconnectEvents.MaxConnectionAttempts; + case NetworkStreamDisconnectReason.ClosedByRemote: + return DisconnectEvents.ClosedByRemote; + case NetworkStreamDisconnectReason.BadProtocolVersion: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.InvalidRpc: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.AuthenticationFailure: + return DisconnectEvents.AuthenticationFailure; + case NetworkStreamDisconnectReason.ProtocolError: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.HandshakeTimeout: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.ApprovalFailure: + return DisconnectEvents.AuthenticationFailure; + case NetworkStreamDisconnectReason.ApprovalTimeout: + return DisconnectEvents.ProtocolTimeout; + } + return DisconnectEvents.Disconnected; + } + + private void OnClientDisconnectFromServer(Connection connection, NetCodeConnectionEvent connectionEvent) + { + SetDisconnectEvent( + GetDisconnectEventFromNetworkStreamDisconnectReason(connectionEvent.DisconnectReason), + GetDisconnectMessageFromNetworkStreamDisconnectReason(connectionEvent.DisconnectReason) + ); + InvokeOnTransportEvent(NetworkEvent.Disconnect, (ulong)connectionEvent.Id.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + } + + private void OnServerClientDisconnected(Connection connection, NetCodeConnectionEvent connectionEvent) + { + InvokeOnTransportEvent(NetworkEvent.Disconnect, (ulong)connectionEvent.Id.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + } + + private void OnClientConnectionEvent(Connection connection, NetCodeConnectionEvent connectionEvent) + { + switch (connectionEvent.State) + { + case ConnectionState.State.Connected: + OnClientConnectedToServer(connection, connectionEvent); + break; + case ConnectionState.State.Disconnected: + OnClientDisconnectFromServer(connection, connectionEvent); + break; + } + } + + private void OnServerConnectionEvent(Connection connection, NetCodeConnectionEvent connectionEvent) + { + switch (connectionEvent.State) + { + case ConnectionState.State.Connected: + OnServerNewClientConnection(connection, connectionEvent); + break; + case ConnectionState.State.Disconnected: + OnServerClientDisconnected(connection, connectionEvent); + break; + } + } + + public override bool StartClient() + { + m_NetworkManager.NetcodeWorld.OnConnectionEvent += OnClientConnectionEvent; + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.Transport = this; + updateSystem.NetworkManager = m_NetworkManager; + return true; + } + + public override bool StartServer() + { + foreach (var connection in m_NetworkManager.NetcodeWorld.AllConnections) + { + OnServerNewClientConnection(connection, default); + } + + m_NetworkManager.NetcodeWorld.OnConnectionEvent += OnServerConnectionEvent; + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.Transport = this; + updateSystem.NetworkManager = m_NetworkManager; + return true; + } + + public override void DisconnectRemoteClient(ulong clientId) + { + m_NetworkManager.NetcodeWorld.DisconnectAClient(m_Connections[(int)clientId].Connection); + m_Connections.Remove((int)clientId); + } + + public override void DisconnectLocalClient() + { + // Remove the connection 1st (the world might not be available) + m_Connections.Remove((int)ServerClientId); + + // TODO-FIX-REVIEW-ME: + // This was causing errors to occur upon shutdown during an integration test. + // The cases being trapped for below yield no errors, but there might be some + // form of other underlying issue here: + + if (m_NetworkManager.NetcodeWorld == null || !m_NetworkManager.NetcodeWorld.IsCreated) + { + return; + } + + if (m_NetworkManager.IsServer || m_NetworkManager.NetcodeWorld.IsHost()) + { + if (m_NetworkManager.LogLevel <= LogLevel.Developer) + { + Debug.LogWarning("Host is attempting to shutdown the local client which is not required with a single world host."); + } + return; + } + m_NetworkManager.NetcodeWorld.RequestDisconnectFromServer(); + + } + + public override ulong GetCurrentRtt(ulong clientId) + { + var (transportId, _) = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId); + return (ulong)m_Connections[(int)transportId].Connection.RTT; + } + + public override void Initialize(NetworkManager networkManager = null) + { + m_Connections = new Dictionary(); + m_RealTimeProvider = networkManager.RealTimeProvider; + m_NetworkManager = networkManager; + } + + public override void Shutdown() + { + + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs.meta b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs.meta new file mode 100644 index 0000000000..ace36beccc --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9980b4e2027240ceb731e395dc270359 +timeCreated: 1772129541 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef index 3d0e9a58bf..98a2cd6a29 100644 --- a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef +++ b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef @@ -13,7 +13,9 @@ "Unity.Networking.Transport", "Unity.Collections", "Unity.Burst", - "Unity.Mathematics" + "Unity.Mathematics", + "Unity.NetCode", + "Unity.Entities" ], "includePlatforms": [], "excludePlatforms": [], @@ -88,6 +90,16 @@ "expression": "6000.5.0a1", "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" + }, + { + "name": "com.unity.multiplayer.playmode", + "expression": "0.1.0", + "define": "UNITY_MULTIPLAYER_PLAYMODE" + }, { "name": "Unity", "expression": "[6000.4.0b5,6000.5.0a1)", diff --git a/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs index 2791e4ac84..f3f60669ed 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class ArithmeticTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs index 7ee23412bf..2715f33d93 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs @@ -5,7 +5,7 @@ using UnityEditor.Build.Reporting; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BuildTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs index e30ceccb1f..d7724be9c4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class DisconnectMessageTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples.meta b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples.meta new file mode 100644 index 0000000000..bc0cce73b4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0f959eedfa8244038f30beb1f00b8cf0 +timeCreated: 1780100790 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/README.md b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/README.md new file mode 100644 index 0000000000..33ca58ffcc --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/README.md @@ -0,0 +1,31 @@ +# Code documentation + +Use this folder for any code snippets that only need to compile. + +Any code snippets that you want to run tests on should be put in [Runtime tests](../../Runtime/Documentation/README.md). + +To embed code in documentation, use the following tag + +```md +[!code-cs[](../../Tests/Editor/DocumentationCodeSamples/.cs#SomeRegionName)] +``` + +With the code formatted like this + +```cs +namespace DocumentationCodeSamples +{ + internal MyTestClass + { + #region SomeRegionName + // All the code in this region block will be embedded without indentation in the docs. + #endregion + + [Test] + public void TestOfDocumentationCode() + { + ... + } + } +} +``` diff --git a/testproject/Legacy/MultiprocessRuntime/readme.md.meta b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/README.md.meta similarity index 75% rename from testproject/Legacy/MultiprocessRuntime/readme.md.meta rename to com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/README.md.meta index 5ef9bc8c31..d43f352993 100644 --- a/testproject/Legacy/MultiprocessRuntime/readme.md.meta +++ b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/README.md.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 39b2fcb99dff6414e8f41b93f4c92b88 +guid: a6afff0520856465a9f94b9a5bcebf24 TextScriptImporter: externalObjects: {} userData: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization.meta b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization.meta new file mode 100644 index 0000000000..677d24fb22 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9371809eac694274ad78a6e7faf27e69 +timeCreated: 1780674489 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs new file mode 100644 index 0000000000..3f70a15cb7 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs @@ -0,0 +1,114 @@ +using NUnit.Framework; +using Unity.Collections; +using Unity.Netcode; + +namespace DocumentationCodeSamples +{ + #region HealthStruct + /// Container for storing health data for a player or item. + public struct Health + { + /// + /// The maximum health that this player or item can have. + /// This is unlikely to change often. + /// + public uint MaxHealth; + + /// + /// The current level of health that this player or item has. + /// This is likely to change regularly. + /// + public int CurrentHealth; + } + #endregion + + #region FastBuffer + /// Tells the Netcode how to serialize and deserialize our custom type. + // The class name doesn't matter here. + public static class FastBufferExtensions + { + /// + /// Extension method to override the serialization for a custom type. + /// + /// Buffer to write values into. + /// The type to customize or override. + public static void WriteValueSafe(this FastBufferWriter writer, in Health health) + { + writer.WriteValueSafe(health.MaxHealth); + writer.WriteValueSafe(health.CurrentHealth); + } + + /// + /// Extension method to override the de-serialization for a custom type. + /// + /// Buffer to read values from. + /// The type to customize or override. + public static void ReadValueSafe(this FastBufferReader reader, out Health health) + { + reader.ReadValueSafe(out uint max); + reader.ReadValueSafe(out int current); + health = new Health { MaxHealth = max, CurrentHealth = current }; + } + } + #endregion + + #region BufferSerializer + /// Tells the how to serialize and deserialize our custom type. + // The class name doesn't matter here. + public static class BufferSerializerExtensions + { + /// + /// Extension method to override bi-directional serialization for a custom type. + /// + /// Bi-directional serial + /// The type to customize or override. + /// Boilerplate syntax to enable the bi-directional serialization. + public static void SerializeValue(this BufferSerializer serializer, ref Health health) where TReaderWriter : IReaderWriter + { + // Because the BufferSerializer already knows how to read and write the primitive types + // We can use the existing BufferSerializer serialization. + serializer.SerializeValue(ref health.MaxHealth); + serializer.SerializeValue(ref health.CurrentHealth); + } + } + #endregion + + internal class TestSerializationDocs + { + [Test] + public void TestFastBufferSerialization() + { + var healthToTest = new Health { MaxHealth = 123, CurrentHealth = 89 }; + var expected = healthToTest; + + using var writer = new FastBufferWriter(256, Allocator.Temp, int.MaxValue); + writer.WriteValueSafe(healthToTest); + + using var reader = new FastBufferReader(writer, Allocator.None); + reader.ReadValueSafe(out Health readHealth); + + Assert.AreEqual(expected.MaxHealth, readHealth.MaxHealth); + Assert.AreEqual(expected.CurrentHealth, readHealth.CurrentHealth); + } + + [Test] + public void TestBufferSerializerSerialization() + { + var healthToTest = new Health { MaxHealth = 456, CurrentHealth = 78 }; + var expected = healthToTest; + + using var writer = new FastBufferWriter(256, Allocator.Temp, int.MaxValue); + var bufferWriter = new BufferSerializer(new BufferSerializerWriter(writer)); + bufferWriter.SerializeValue(ref healthToTest); + + using var tempReader = new FastBufferReader(bufferWriter.GetFastBufferWriter(), Allocator.None); + var bufferReader = new BufferSerializer(new BufferSerializerReader(tempReader)); + var readHealth = new Health(); + bufferReader.SerializeValue(ref readHealth); + + Assert.AreEqual(expected.MaxHealth, readHealth.MaxHealth); + Assert.AreEqual(expected.CurrentHealth, readHealth.CurrentHealth); + + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs.meta new file mode 100644 index 0000000000..a39141e43d --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/DocumentationCodeSamples/Serialization/SerializationCustomization.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 08949d7abc1249418192e44999279f89 +timeCreated: 1780101010 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs index 666183abe6..520633a961 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class InterpolatorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs index a33e7ac6f4..37a711a29c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class DisconnectOnSendTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs index a584ea61e6..aee1f2096d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs @@ -8,7 +8,7 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageCorruptionTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs index d77bf4ea72..5a879f1c20 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs @@ -5,7 +5,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageReceivingTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs index 66898947e6..025b3d54e4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageRegistrationTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs index ae2a1e955e..85049eec56 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs @@ -8,7 +8,7 @@ using UnityEngine.TestTools; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageSendingTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs index 222c08b24b..623fff9227 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using NUnit.Framework.Internal; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageVersioningTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs index 3f5a35b3a3..a22d4c7375 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NopMessageSender : INetworkMessageSender { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkBehaviourEditorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkBehaviourEditorTests.cs index ca7d121ea5..ebf3c43b20 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkBehaviourEditorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkBehaviourEditorTests.cs @@ -4,7 +4,7 @@ using UnityEngine.TestTools; using Object = UnityEngine.Object; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkBehaviourEditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs index ea4a4b155b..52cdcdbf96 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using NUnit.Framework; -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using Unity.Netcode.Transports.UTP; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkManagerConfigurationTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs index e4f127564a..fd3bae3cb8 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs @@ -1,10 +1,10 @@ using System.Text.RegularExpressions; using NUnit.Framework; -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEngine; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkObjectTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs index f50e502633..0cd1ae134f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs @@ -1,9 +1,9 @@ using NUnit.Framework; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.GameObjects.Editor.Configuration; using UnityEditor; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkPrefabProcessorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs index 77ad69dd62..a411b994b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using UnityEngine; -namespace Unity.Netcode.EditorTests.NetworkVar +namespace Unity.Netcode.GameObjects.EditorTests.NetworkVar { internal class NetworkVarTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index b6234001c8..f89c6ab3d3 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -4,7 +4,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal abstract class BaseFastBufferReaderWriterTest { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 9200d6b987..10a32a25b5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BitCounterTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index de83183880..5dbf8f7f39 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -2,7 +2,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BitReaderTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index b4a84ff710..dfc3f5caf4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -2,7 +2,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BitWriterTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index b6684ff0e0..7ee8cf5e8f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BufferSerializerTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 1bd197997f..30881efff0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -6,7 +6,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BytePackerTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 85aabbb52e..b27eda622b 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -5,7 +5,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class FastBufferReaderTests : BaseFastBufferReaderWriterTest { @@ -924,6 +924,50 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW } } + /// + /// This validates that catches a potential + /// scenario where the string's character count value has already been read + /// due to an error within user script and the resultant character count + /// multiplied times 2 (when using 2 bytes vs 1) causes the length to roll over + /// to a negative value which, in turn, causes the reader to attempt to read + /// into restricted memory and causes the editor to crash. + /// + [Test] + public void ReadingStringAfterStringLengthHasAlreadyBeenRead([Values] bool isSafeRead) + { + // This was an issue uncovered in UUM-145752 that resulted + // in the below text to result in a length that when using + // 2 bytes per character would cause the skewed size to roll + // over into a negative value causing the editor to crash + // when it attempted to read a large negative offset value. + string valueToTest = "true"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + using var writer = new FastBufferWriter(serializedValueSize, Allocator.Temp); + writer.WriteValueSafe(valueToTest); + + using var reader = new FastBufferReader(writer, Allocator.Temp); + + // Read the value of the character count before trying to read the string + // This mocks user code having read too far into a stream causing the position to be skewed such that + // the string reader reads the some of the bytes for the actual text as the length. + reader.ReadByteSafe(out byte count); + Assert.True(count == valueToTest.Length, $"Count ({count}) is not the expected size of {valueToTest.Length}!"); + if (isSafeRead) + { + // This should throw an overflow exception but should not crash the editor. + Assert.Throws(() => reader.ReadValueSafe(out string valueRead)); + } + else + { + // Assume user does a pre-calculation of the size to be read: + Assert.IsTrue(reader.TryBeginRead(count), "Reader denied read permission"); + + // This should throw an overflow exception but should not crash the editor. + Assert.Throws(() => reader.ReadValue(out string valueRead)); + } + } [TestCase(1, 0)] [TestCase(2, 0)] diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 32e580c66f..f52307d462 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -6,7 +6,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class FastBufferWriterTests : BaseFastBufferReaderWriterTest { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs index 8be50e5530..cdda8aa5e0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkSceneHandleTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs index f279f4de05..5565a166f6 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class UserBitReaderAndBitWriterTests_NCCBUG175 { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs index fc7cb01d69..7b435523ea 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs @@ -2,7 +2,7 @@ using NUnit.Framework; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { /// /// Tests for running a as a client. diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs index 2a137bde2b..93403ef555 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs @@ -5,7 +5,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkTimeTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs index 8d5edba0a4..4c6560aea5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class ServerNetworkTimeSystemTests { @@ -31,5 +31,3 @@ public void LocalTimeEqualServerTimeTest() } } - - diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs index e1dee9b3a7..ffa62cc265 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs @@ -2,7 +2,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { /// /// Helper functions for timing related tests. Allows to get a set of time steps and simulate time advancing without the need of a full playmode test. @@ -60,4 +60,3 @@ public static void ApplySteps(NetworkTimeSystem timeSystem, NetworkTickSystem ti } } } - diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs index 97671f906f..26a9ca94cb 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Netcode.Transports.UTP; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BatchedReceiveQueueTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs index f27bef95d3..81d65b92f9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Netcode.Transports.UTP; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BatchedSendQueueTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index 34f785074a..22e075eec1 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -4,7 +4,7 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class UnityTransportTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef index 06002bf7d2..a9a05da02b 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef @@ -1,10 +1,10 @@ { - "name": "Unity.Netcode.Editor.Tests", - "rootNamespace": "Unity.Netcode.EditorTests", + "name": "Unity.Netcode.GameObjects.Editor.Tests", + "rootNamespace": "Unity.Netcode.GameObjects.EditorTests", "references": [ "Unity.Collections", "Unity.Netcode.Runtime", - "Unity.Netcode.Editor", + "Unity.Netcode.GameObjects.Editor", "Unity.Multiplayer.MetricTypes", "Unity.Multiplayer.NetStats", "Unity.Multiplayer.Tools.MetricTypes", @@ -45,10 +45,10 @@ "define": "SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION" }, { - "name": "Unity", - "expression": "6000.5.0a1", - "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" + "name": "Unity", + "expression": "6000.5.0a1", + "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs index 30fbb52aff..076f2e2325 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class XXHashTests { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs index b6157324bc..fb88b9ada8 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DistributedAuthorityCodecTests.cs @@ -76,11 +76,6 @@ internal class TestNetworkComponent : NetworkBehaviour { public NetworkList MyNetworkList = new NetworkList(new List { 1, 2, 3 }); public NetworkVariable MyNetworkVar = new NetworkVariable(3); - - [Rpc(SendTo.Authority)] - public void TestAuthorityRpc(byte[] _) - { - } } protected override void OnOneTimeSetup() @@ -157,62 +152,6 @@ protected override IEnumerator OnStartedServerAndClients() m_Client.MessageManager.Hook(m_ClientCodecHook); } - [UnityTest] - public IEnumerator AuthorityRpc() - { - var player = m_Client.LocalClient.PlayerObject; - player.OwnerClientId = m_Client.LocalClientId + 1; - - var networkComponent = player.GetComponent(); - networkComponent.UpdateNetworkProperties(); - networkComponent.TestAuthorityRpc(new byte[] { 1, 2, 3, 4 }); - - // Universal Rpcs are sent as a ProxyMessage (which contains an RpcMessage) - yield return m_ClientCodecHook.WaitForMessageReceived(); - } - - [UnityTest] - public IEnumerator ChangeOwnership() - { - var message = new ChangeOwnershipMessage - { - DistributedAuthorityMode = true, - NetworkObjectId = 100, - OwnerClientId = 2, - }; - - yield return SendMessage(ref message); - } - - [UnityTest] - public IEnumerator ClientConnected() - { - var message = new ClientConnectedMessage() - { - ClientId = 2, - }; - - yield return SendMessage(ref message); - } - - [UnityTest] - public IEnumerator ClientDisconnected() - { - var message = new ClientDisconnectedMessage() - { - ClientId = 2, - }; - - yield return SendMessage(ref message); - } - - [UnityTest] - public IEnumerator CreateObject() - { - SpawnObject(m_SpawnObject, m_Client); - yield return m_ClientCodecHook.WaitForMessageReceived(); - } - [UnityTest] public IEnumerator DestroyObject() { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/NetworkClientAndPlayerObjectTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/NetworkClientAndPlayerObjectTests.cs index 5697833bbc..929592a9b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/NetworkClientAndPlayerObjectTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/NetworkClientAndPlayerObjectTests.cs @@ -246,8 +246,7 @@ private bool ValidateAllPlayerObjects() m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}] Local {nameof(NetworkClient.PlayerObject)} is null!"); success = false; } - else - if (!client.SpawnManager.PlayerObjects.Contains(client.LocalClient.PlayerObject)) + else if (!client.SpawnManager.PlayerObjects.Contains(client.LocalClient.PlayerObject)) { m_ErrorLogLevel1.AppendLine($"[Client-{client.LocalClientId}] Local {nameof(NetworkSpawnManager.PlayerObjects)} does not contain {client.LocalClient.PlayerObject.name}!"); success = false; diff --git a/testproject/Assets/Tests/Runtime/Physics.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples.meta similarity index 77% rename from testproject/Assets/Tests/Runtime/Physics.meta rename to com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples.meta index de0808f1a7..aea9d686c3 100644 --- a/testproject/Assets/Tests/Runtime/Physics.meta +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: efc90b4d4d76f7a48ad1cf726a3a193e +guid: e017ce40eb7744e03bc0a83fa18ddcc3 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration.meta similarity index 77% rename from testproject/Legacy/MultiprocessRuntime/readme-ressources.meta rename to com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration.meta index 00a2ce978f..baf6e6f199 100644 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources.meta +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5d13190bb106b4188934d5576df7e777 +guid: ac415df2337f69349996d9da97f07242 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs new file mode 100644 index 0000000000..945807a06c --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs @@ -0,0 +1,220 @@ +using System.Collections; +using System.Text; +using NUnit.Framework; +using Unity.Netcode; +using Unity.Netcode.TestHelpers.Runtime; +using Unity.Netcode.Transports.SinglePlayer; +using Unity.Netcode.Transports.UTP; +using UnityEngine; +using UnityEngine.TestTools; + +namespace DocumentationCodeSamples +{ + #region SinglePlayerTransportExample + /// + /// Example of how to start a single player or multiplayer session. + /// Place this on your NetworkManager's GameObject. + /// + internal class SwitchingTransportTypesExample : MonoBehaviour + { + public enum StartType + { + SinglePlayer, + Client, + Host, + Server + } + + private UnityTransport m_UnityTransport; + private SinglePlayerTransport m_SinglePlayerTransport; + private NetworkManager m_NetworkManager; + + private void Awake() + { + m_UnityTransport = GetComponent(); + m_SinglePlayerTransport = GetComponent(); + m_NetworkManager = GetComponent(); + } + + public bool StartSession(StartType startType) + { + var startStatus = false; + // Set the transport to use before starting + m_NetworkManager.NetworkConfig.NetworkTransport = startType == StartType.SinglePlayer ? m_SinglePlayerTransport : m_UnityTransport; + switch (startType) + { + case StartType.Host: + case StartType.SinglePlayer: + { + // Starting a host or single player is the same + startStatus = m_NetworkManager.StartHost(); + break; + } + case StartType.Server: + { + startStatus = m_NetworkManager.StartServer(); + break; + } + case StartType.Client: + { + startStatus = m_NetworkManager.StartClient(); + break; + } + } + return startStatus; + } + } + #endregion + + internal class VerifyNetcodeSessionActive : NetworkBehaviour + { + public bool RpcReceived { get; private set; } + + public bool TestNetworkVariableEvent { get; private set; } + public NetworkVariable TestNetworkVariable = new NetworkVariable(false, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner); + + [Rpc(SendTo.Everyone, InvokePermission = RpcInvokePermission.Owner)] + private void VerifyRpc(RpcParams rpcParams = default) + { + RpcReceived = true; + } + + public void TestConnectivity() + { + if (IsOwner) + { + VerifyRpc(); + TestNetworkVariable.Value = true; + } + } + + protected override void OnNetworkPostSpawn() + { + TestNetworkVariable.OnValueChanged += TestNetworkVariableChanged; + base.OnNetworkPostSpawn(); + } + + private void TestNetworkVariableChanged(bool previous, bool current) + { + TestNetworkVariableEvent = true; + } + } + + [TestFixture(SessionType.SinglePlayer)] + [TestFixture(SessionType.MultiPlayer)] + internal class SwitchingTransportTypesTests : NetcodeIntegrationTest + { + public enum SessionType + { + SinglePlayer, + MultiPlayer + } + + protected override int NumberOfClients => 0; + + // We do not need to test this against CMB. + protected override bool UseCMBService() + { + return false; + } + + private SessionType m_SessionType; + public SwitchingTransportTypesTests(SessionType sessionType) + { + m_SessionType = sessionType; + } + + protected override IEnumerator OnSetup() + { + m_CanStart = false; + return base.OnSetup(); + } + + protected override void OnCreatePlayerPrefab() + { + m_PlayerPrefab.AddComponent(); + base.OnCreatePlayerPrefab(); + } + + private bool m_CanStart = false; + protected override bool CanStartServerAndClients() + { + return m_CanStart; + } + + private NetworkManager m_Client; + protected override void OnNewClientCreated(NetworkManager networkManager) + { + m_Client = networkManager; + networkManager.NetworkConfig.EnableSceneManagement = false; + base.OnNewClientCreated(networkManager); + } + + [UnityTest] + public IEnumerator SwitchTransportTest() + { + var authority = GetAuthorityNetworkManager(); + authority.NetworkConfig.EnableSceneManagement = false; + var startType = SwitchingTransportTypesExample.StartType.Host; + if (m_SessionType == SessionType.SinglePlayer) + { + startType = SwitchingTransportTypesExample.StartType.SinglePlayer; + authority.gameObject.AddComponent(); + } + m_CanStart = true; + var example = authority.gameObject.AddComponent(); + Assert.IsTrue(example.StartSession(startType), "Failed to start single player session!"); + + if (m_SessionType != SessionType.SinglePlayer) + { + yield return CreateAndStartNewClient(); + AssertOnTimeout("Timed out waiting for client to start and connect!"); + } + + var verifyNetcode = authority.LocalClient.PlayerObject.GetComponent(); + verifyNetcode.TestConnectivity(); + if (m_SessionType != SessionType.SinglePlayer) + { + m_Client.LocalClient.PlayerObject.GetComponent().TestConnectivity(); + } + + yield return WaitForConditionOrTimeOut(VerifyNetcodeSession); + AssertOnTimeout($"Single player session had netcode related errors:"); + } + + /// + /// Verifies that all spawned players received the RPC and NetworkVariable + /// changed event. + /// + private bool VerifyNetcodeSession(StringBuilder errorLog) + { + foreach (var networkManager in m_NetworkManagers) + { + foreach (var networkObject in networkManager.SpawnManager.SpawnedObjectsList) + { + if (!networkObject.IsPlayerObject) + { + continue; + } + var verify = networkObject.gameObject.GetComponent(); + + if (!verify.RpcReceived) + { + errorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{verify.name}] Rpc was not recieved!"); + } + + if (!verify.TestNetworkVariableEvent) + { + errorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{verify.name}] NetworkVariable.OnValueChanged did not get invoked!"); + } + + if (!verify.TestNetworkVariable.Value) + { + errorLog.AppendLine($"[Client-{networkManager.LocalClientId}][{verify.name}] {nameof(VerifyNetcodeSessionActive.TestNetworkVariable)} did not get set!"); + } + } + } + return errorLog.Length == 0; + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs.meta new file mode 100644 index 0000000000..bd9ea061b2 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/Configuration/SinglePlayerSessions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cd8ec5118fc47e449ae915c51a3806b4 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable.meta new file mode 100644 index 0000000000..6fc7d1074d --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 446ef307887e3446f896c899f43af442 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs new file mode 100644 index 0000000000..74fb7fe157 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Netcode; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + +namespace DocumentationCodeSamples +{ + #region TestMyCustomNetworkVariable + // Using MyCustomNetworkVariable within a NetworkBehaviour + internal class TestMyCustomNetworkVariable : NetworkBehaviour + { + public MyCustomNetworkVariable CustomNetworkVariable = new MyCustomNetworkVariable(); + public MyCustomGenericNetworkVariable CustomGenericNetworkVariable = new MyCustomGenericNetworkVariable(); + + public override void OnNetworkSpawn() + { + if (IsServer) + { + for (int i = 0; i < 4; i++) + { + var someData = new SomeData + { + SomeFloatData = i, + SomeIntData = i + }; + someData.SomeListOfValues.Add((ulong)i + 1000000); + someData.SomeListOfValues.Add((ulong)i + 2000000); + someData.SomeListOfValues.Add((ulong)i + 3000000); + CustomNetworkVariable.SomeDataToSynchronize.Add(someData); + CustomNetworkVariable.SetDirty(true); + + CustomGenericNetworkVariable.SomeDataToSynchronize.Add(i); + CustomGenericNetworkVariable.SetDirty(true); + } + } + } + } + + /// + /// Bare minimum example of NetworkVariableBase derived class + /// + [Serializable] + public class MyCustomNetworkVariable : NetworkVariableBase + { + /// + /// Managed list of class instances + /// + internal List SomeDataToSynchronize = new List(); + + /// + /// Writes the complete state of the variable to the writer + /// + /// The stream to write the state to + public override void WriteField(FastBufferWriter writer) + { + // Serialize the data we need to synchronize + writer.WriteValueSafe(SomeDataToSynchronize.Count); + foreach (var dataEntry in SomeDataToSynchronize) + { + writer.WriteValueSafe(dataEntry.SomeIntData); + writer.WriteValueSafe(dataEntry.SomeFloatData); + writer.WriteValueSafe(dataEntry.SomeListOfValues.Count); + foreach (var valueItem in dataEntry.SomeListOfValues) + { + writer.WriteValueSafe(valueItem); + } + } + } + + /// + /// Reads the complete state from the reader and applies it + /// + /// The stream to read the state from + public override void ReadField(FastBufferReader reader) + { + // De-Serialize the data being synchronized + var itemsToUpdate = 0; + reader.ReadValueSafe(out itemsToUpdate); + SomeDataToSynchronize.Clear(); + for (int i = 0; i < itemsToUpdate; i++) + { + var newEntry = new SomeData(); + reader.ReadValueSafe(out newEntry.SomeIntData); + reader.ReadValueSafe(out newEntry.SomeFloatData); + var itemsCount = 0; + var tempValue = (ulong)0; + reader.ReadValueSafe(out itemsCount); + newEntry.SomeListOfValues.Clear(); + for (int j = 0; j < itemsCount; j++) + { + reader.ReadValueSafe(out tempValue); + newEntry.SomeListOfValues.Add(tempValue); + } + + SomeDataToSynchronize.Add(newEntry); + } + } + + /// + /// Used to write partial updates rather than synchronizing the full state on every change. + /// + /// The stream to write the state to + public override void WriteDelta(FastBufferWriter writer) + { + // Not implemented for this example, instead we can write the field + WriteField(writer); + } + + /// + /// Used to read partial updates rather than synchronizing the full state on every change. + /// + /// + public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta) + { + // Not implemented for this example, instead we can read the field + ReadField(reader); + } + + } + + /// + /// Bare minimum example of generic NetworkVariableBase derived class + /// + /// Generic type marker + [Serializable] + [GenerateSerializationForGenericParameter(0)] + public class MyCustomGenericNetworkVariable : NetworkVariableBase + { + /// Managed list of class instances + public List SomeDataToSynchronize = new List(); + + /// + /// Writes the complete state of the variable to the writer + /// + /// The stream to write the state to + public override void WriteField(FastBufferWriter writer) + { + // Serialize the data we need to synchronize + writer.WriteValueSafe(SomeDataToSynchronize.Count); + for (var i = 0; i < SomeDataToSynchronize.Count; ++i) + { + var dataEntry = SomeDataToSynchronize[i]; + // NetworkVariableSerialization is used for serializing generic types + NetworkVariableSerialization.Write(writer, ref dataEntry); + } + } + + /// + /// Reads the complete state from the reader and applies it + /// + /// The stream to read the state from + public override void ReadField(FastBufferReader reader) + { + // De-Serialize the data being synchronized + var itemsToUpdate = 0; + reader.ReadValueSafe(out itemsToUpdate); + SomeDataToSynchronize.Clear(); + for (int i = 0; i < itemsToUpdate; i++) + { + T newEntry = default; + // NetworkVariableSerialization is used for serializing generic types + NetworkVariableSerialization.Read(reader, ref newEntry); + SomeDataToSynchronize.Add(newEntry); + } + } + + /// + /// Used to write partial updates rather than synchronizing the full state on every change. + /// + /// The stream to write the state to + public override void WriteDelta(FastBufferWriter writer) + { + // Not implemented for this example, instead we can write the field + WriteField(writer); + } + + /// + /// Used to read partial updates rather than synchronizing the full state on every change. + /// + /// + public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta) + { + // Not implemented for this example, instead we can read the field + ReadField(reader); + } + } + + [Serializable] + internal class SomeData + { + public int SomeIntData = default; + public float SomeFloatData = default; + public List SomeListOfValues = new List(); + } + #endregion + + + internal class CustomNetworkVariableTest : NetcodeIntegrationTest + { + protected override int NumberOfClients => 2; + + private GameObject m_PrefabToSpawn; + + protected override void OnServerAndClientsCreated() + { + m_PrefabToSpawn = CreateNetworkObjectPrefab(nameof(TestMyCustomNetworkVariable)); + m_PrefabToSpawn.AddComponent(); + } + + /// + /// Validates when the authority applies a value during spawn or + /// post spawn of a newly instantiated and spawned object the value is set by the time non-authority + /// instances invoke . + /// + [UnityTest] + public IEnumerator CustomNetworkVariableCodeWorks() + { + var authority = GetAuthorityNetworkManager(); + var authorityObject = SpawnObject(m_PrefabToSpawn, authority).GetComponent(); + var authorityBehaviour = authorityObject.GetComponent(); + + yield return WaitForSpawnedOnAllOrTimeOut(authorityObject.NetworkObjectId); + AssertOnTimeout("Failed to spawn network object"); + + foreach (var networkManager in m_NetworkManagers) + { + Assert.True(networkManager.SpawnManager.SpawnedObjects.TryGetValue(authorityObject.NetworkObjectId, out var localObject)); + var testBehaviour = localObject.GetComponent(); + Assert.NotNull(testBehaviour); + Assert.AreEqual(authorityBehaviour.CustomNetworkVariable.SomeDataToSynchronize.Count, testBehaviour.CustomNetworkVariable.SomeDataToSynchronize.Count, $"[Client-{networkManager.LocalClientId}] Incorrect length found for {nameof(MyCustomNetworkVariable)}"); + Assert.AreEqual(authorityBehaviour.CustomGenericNetworkVariable.SomeDataToSynchronize, testBehaviour.CustomGenericNetworkVariable.SomeDataToSynchronize, $"[Client-{networkManager.LocalClientId}] Incorrect length found for {nameof(MyCustomNetworkVariable)}"); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs.meta new file mode 100644 index 0000000000..5f186a3e56 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomNetworkVariable.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a4617887a8b0d46be9babcc146fcfd20 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomSerializationDocsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomSerializationDocsTests.cs new file mode 100644 index 0000000000..1e6096b187 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomSerializationDocsTests.cs @@ -0,0 +1,213 @@ +using System.Collections; +using System.Text; +using Unity.Netcode; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + +namespace DocumentationCodeSamples +{ + internal static class FastBufferExtensions + { + internal static void WriteValueSafe(this FastBufferWriter writer, in CustomSerializationDocsTests.Health health) + { + writer.WriteValueSafe(health.MaxHealth); + writer.WriteValueSafe(health.CurrentHealth); + } + + internal static void ReadValueSafe(this FastBufferReader reader, out CustomSerializationDocsTests.Health health) + { + reader.ReadValueSafe(out uint max); + reader.ReadValueSafe(out int current); + health = new CustomSerializationDocsTests.Health { MaxHealth = max, CurrentHealth = current }; + } + } + + + internal class CustomSerializationDocsTests : NetcodeIntegrationTest + { + #region HealthExample + public struct Health + { + public uint MaxHealth; + public int CurrentHealth; + + // Register our custom serialization on load + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + public static void RegisterHealthSerialization() + { + // You can reuse the FastBufferWriter and FastBufferReader extension methods we wrote above + UserNetworkVariableSerialization.WriteValue = FastBufferExtensions.WriteValueSafe; + UserNetworkVariableSerialization.ReadValue = FastBufferExtensions.ReadValueSafe; + + // Here is where you register your custom delta handling. + UserNetworkVariableSerialization.WriteDelta = WriteDelta; + UserNetworkVariableSerialization.ReadDelta = ReadDelta; + + // You can also use lambda expressions to register functions + UserNetworkVariableSerialization.DuplicateValue = (in Health value, ref Health duplicatedValue) => { duplicatedValue = value; }; + } + + // We can use an enum to indicate which field has changed for the delta change. + // This lets us save bandwidth when only one value has changed. + // In the case of Health, we expect CurrentHealth to change much more often than MaxHealth + // Implementing WriteDelta saves us bandwidth on not sending the MaxHealth every time CurrentHealth changes. + private enum ChangeType : byte + { + MaxHealth, + CurrentHealth, + All, + } + + public static void WriteDelta(FastBufferWriter writer, in Health value, in Health previousValue) + { + if (value.MaxHealth == previousValue.MaxHealth && value.CurrentHealth != previousValue.CurrentHealth) + { + // If only our CurrentHealth has changed, we can send the CurrentHealth enum with only the updated CurrentHealth value + writer.WriteValueSafe(ChangeType.CurrentHealth); + writer.WriteValueSafe(value.CurrentHealth); + } + else if (value.CurrentHealth == previousValue.CurrentHealth && value.MaxHealth != previousValue.MaxHealth) + { + // If only our MaxHealth has changed, we can send the MaxHealth enum with only the updated MaxHealth value + writer.WriteValueSafe(ChangeType.MaxHealth); + writer.WriteValueSafe(value.MaxHealth); + } + else + { + // If both values have changed, we need to serialize both values. + writer.WriteValueSafe(ChangeType.All); + writer.WriteValueSafe(value.MaxHealth); + writer.WriteValueSafe(value.CurrentHealth); + } + } + + public static void ReadDelta(FastBufferReader reader, ref Health value) + { + // First we read what type of change we've received + reader.ReadValueSafe(out ChangeType changeType); + + // Then we read the data in our delta message, based on what type of change we've received. + switch (changeType) + { + case ChangeType.CurrentHealth: + { + reader.ReadValueSafe(out value.CurrentHealth); + break; + } + case ChangeType.MaxHealth: + { + reader.ReadValueSafe(out value.MaxHealth); + break; + } + case ChangeType.All: + { + reader.ReadValueSafe(out value.MaxHealth); + reader.ReadValueSafe(out value.CurrentHealth); + break; + } + } + } + } + #endregion + + internal class TestHealthBehaviour : NetworkBehaviour + { + internal readonly NetworkVariable HealthVar = new(); + + internal Health ReceivedFromRpc; + + [Rpc(SendTo.Everyone)] + public void SendHealthRpc(Health health) + { + ReceivedFromRpc = health; + } + } + + protected override int NumberOfClients => 1; + private GameObject m_PrefabToSpawn; + + protected override void OnServerAndClientsCreated() + { + m_PrefabToSpawn = CreateNetworkObjectPrefab(nameof(TestHealthBehaviour)); + m_PrefabToSpawn.AddComponent(); + } + + private Health m_ExpectedHealth; + private ulong m_NetworkObjectIdToTest; + + private bool m_TestingRpc; + + private bool ValidateAllAreEqual(StringBuilder errorLog) + { + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(m_NetworkObjectIdToTest, out NetworkObject localInstance)) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] SpawnedObject not found!"); + return false; + } + + var healthInstance = localInstance.GetComponent(); + if (healthInstance == null) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] Health instance is null!"); + return false; + } + + var received = m_TestingRpc ? healthInstance.ReceivedFromRpc : healthInstance.HealthVar.Value; + if (m_ExpectedHealth.MaxHealth != received.MaxHealth) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] MaxHealth values don't match! Expected {m_ExpectedHealth.MaxHealth}, Received {received.MaxHealth}"); + return false; + } + + if (m_ExpectedHealth.CurrentHealth != received.CurrentHealth) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] CurrentHealth values don't match! Expected {m_ExpectedHealth.CurrentHealth}, Received {received.CurrentHealth}"); + return false; + } + } + + return true; + } + + [UnityTest] + public IEnumerator TestHealthCode() + { + var authority = GetAuthorityNetworkManager(); + var authorityInstance = SpawnObject(m_PrefabToSpawn, authority).GetComponent(); + m_NetworkObjectIdToTest = authorityInstance.NetworkObjectId; + + yield return WaitForSpawnedOnAllOrTimeOut(authorityInstance.NetworkObjectId); + AssertOnTimeout("Failed to spawn network object"); + + var healthToTest = new Health { MaxHealth = 456, CurrentHealth = 23 }; + m_ExpectedHealth = healthToTest; + m_TestingRpc = true; + + authorityInstance.SendHealthRpc(healthToTest); + + yield return WaitForConditionOrTimeOut(ValidateAllAreEqual); + AssertOnTimeout("RPC send failed"); + + m_TestingRpc = false; + healthToTest = new Health { MaxHealth = 123, CurrentHealth = 45 }; + m_ExpectedHealth = healthToTest; + + authorityInstance.HealthVar.Value = healthToTest; + + yield return WaitForConditionOrTimeOut(ValidateAllAreEqual); + AssertOnTimeout("NetworkVariable assignment failed"); + + var current = authorityInstance.HealthVar.Value; + current.CurrentHealth -= 10; + authorityInstance.HealthVar.Value = current; + + m_ExpectedHealth = current; + + yield return WaitForConditionOrTimeOut(ValidateAllAreEqual); + AssertOnTimeout("NetworkVariable update failed"); + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomSerializationDocsTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomSerializationDocsTests.cs.meta new file mode 100644 index 0000000000..1588a4d111 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/NetworkVariable/CustomSerializationDocsTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 22820b0916e341ddbcadc50030dc5f60 +timeCreated: 1780100433 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/README.md b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/README.md new file mode 100644 index 0000000000..92cd488e8e --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/README.md @@ -0,0 +1,33 @@ +# Code documentation + +Use this folder for any code examples that we want to test to ensure the runtime functionality isn't broken + +Any code snippets that are small enough that you only want to check that they compile should be in [Editor tests](../../Editor/Documentation/README.md). + +To embed code in documentation, use the following tag + +```md +[!code-cs[](../../Tests/Runtime/DocumentationCodeSamples/.cs#SomeRegionName)] +``` + +With the code formatted like this + +```cs +namespace DocumentationCodeSamples +{ + internal MyTestClass : NetcodeIntegrationTest + { + #region SomeRegionName + // All the code in this region block will be embedded without indentation in the docs. + #endregion + + protected override int NumberOfClients => 1; + + [UnityTest] + public IEnumerator TestOfDocumentationCode() + { + ... + } + } +} +``` diff --git a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef.meta b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/README.md.meta similarity index 59% rename from testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef.meta rename to com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/README.md.meta index a06b37d08b..4f45c6f105 100644 --- a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef.meta +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DocumentationCodeSamples/README.md.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 66273ab9e01074f7da305fe84e13da47 -AssemblyDefinitionImporter: +guid: 8a8cfc9d633474efb95052169e9fcb50 +TextScriptImporter: externalObjects: {} userData: assetBundleName: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDestroyTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDestroyTests.cs index 0e226ec115..f27f28d53f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDestroyTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectDestroyTests.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Text.RegularExpressions; using NUnit.Framework; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; @@ -193,7 +194,7 @@ private bool HaveLogsBeenReceived() return false; } - if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, $"[Netcode] [SenderId:{m_ClientNetworkManagers[0].LocalClientId}] [Invalid Destroy][{m_ClientPlayerName}][NetworkObjectId:{m_ClientNetworkObjectId}] Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call Destroy or Despawn on the server/host instead.")) + if (!NetcodeLogAssert.HasLogBeenReceived(LogType.Error, new Regex($"SenderId:{m_ClientNetworkManagers[0].LocalClientId}]"))) { return false; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs index 34f2362982..5b5583f31d 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs @@ -341,7 +341,17 @@ bool HasConditionBeenMet() Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out while waiting for client side despawns!"); //----------- step 2 check spawn and destroy again - authorityInstance.GetComponent().Spawn(); + var authorityObject = authorityInstance.GetComponent(); + authorityObject.Spawn(); + + // This validates invoking GetSceneOriginHandle will not throw an exception for a dynamically spawned NetworkObject + // when the scene of origin hasn't been set. + var sceneOriginHandle = authorityObject.GetSceneOriginHandle(); + + // This validates that GetSceneOriginHandle will return the GameObject's scene handle that should be the currently active scene + var activeSceneHandle = SceneManager.GetActiveScene().handle; + Assert.IsTrue(sceneOriginHandle == activeSceneHandle, $"{nameof(NetworkObject)} should have returned the active scene handle of {activeSceneHandle} but returned {sceneOriginHandle}"); + // wait a tick yield return s_DefaultWaitForTick; // check spawned again on server this is 2 because we are reusing the object which was already spawned once. @@ -366,23 +376,6 @@ bool HasConditionBeenMet() Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out while waiting for client side despawns! (2nd pass)"); } - [Test] - public void DynamicallySpawnedNoSceneOriginException() - { - var gameObject = new GameObject(); - var networkObject = gameObject.AddComponent(); - networkObject.IsSpawned = true; - networkObject.SceneOriginHandle = default; - networkObject.IsSceneObject = false; - // This validates invoking GetSceneOriginHandle will not throw an exception for a dynamically spawned NetworkObject - // when the scene of origin hasn't been set. - var sceneOriginHandle = networkObject.GetSceneOriginHandle(); - - // This validates that GetSceneOriginHandle will return the GameObject's scene handle that should be the currently active scene - var activeSceneHandle = SceneManager.GetActiveScene().handle; - Assert.IsTrue(sceneOriginHandle == activeSceneHandle, $"{nameof(NetworkObject)} should have returned the active scene handle of {activeSceneHandle} but returned {sceneOriginHandle}"); - } - private class TrackOnSpawnFunctions : NetworkBehaviour { public int OnNetworkSpawnCalledCount { get; private set; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSpawnManyObjectsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSpawnManyObjectsTests.cs index cf786bd0c4..ad643d7ca7 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSpawnManyObjectsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSpawnManyObjectsTests.cs @@ -37,7 +37,6 @@ protected override void OnServerAndClientsCreated() var gameObject = new GameObject("TestObject"); var networkObject = gameObject.AddComponent(); NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject); - networkObject.IsSceneObject = false; gameObject.AddComponent(); m_PrefabToSpawn = new NetworkPrefab() { Prefab = gameObject }; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs index 6224c6c627..7535e4ffce 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs @@ -8,7 +8,6 @@ namespace Unity.Netcode.RuntimeTests { - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] // Ignored test tracked in MTT-14172 [TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.DAHost)] [TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Host)] @@ -28,12 +27,6 @@ internal class NetworkObjectSynchronizationTests : NetcodeIntegrationTest private LogLevel m_CurrentLogLevel; - // TODO: [CmbServiceTests] Adapt to run with the service - protected override bool UseCMBService() - { - return false; - } - public enum VariableLengthSafety { DisableNetVarSafety, @@ -57,15 +50,15 @@ protected override void OnCreatePlayerPrefab() protected override void OnServerAndClientsCreated() { - + var authority = GetAuthorityNetworkManager(); // Set the NetworkVariable Safety Check setting - m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety; + authority.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety; // Ignore the errors generated during this test (they are expected) - m_ServerNetworkManager.LogLevel = LogLevel.Nothing; + authority.LogLevel = LogLevel.Nothing; // Disable forcing the same prefabs to avoid failed connections - m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false; + authority.NetworkConfig.ForceSamePrefabs = false; // Create the valid network prefab m_NetworkPrefab = CreateNetworkObjectPrefab("ValidObject"); @@ -104,8 +97,9 @@ protected override void OnNewClientCreated(NetworkManager networkManager) public IEnumerator NetworkObjectDeserializationFailure() { m_CurrentLogLevel = LogLevel.Nothing; - var validSpawnedNetworkObjects = new List(); + var authoritySpawnedNetworkObjects = new List(); NetworkBehaviourWithNetworkVariables.ResetSpawnCount(); + var authority = GetAuthorityNetworkManager(); // Spawn NetworkObjects on the server side with half of them being the // invalid network prefabs to simulate NetworkObject synchronization failure @@ -113,47 +107,29 @@ public IEnumerator NetworkObjectDeserializationFailure() { if (i % 2 == 0) { - SpawnObject(m_InValidNetworkPrefab, m_ServerNetworkManager); + SpawnObject(m_InValidNetworkPrefab, authority); } else { // Keep track of the prefabs that should successfully spawn on the client side - validSpawnedNetworkObjects.Add(SpawnObject(m_NetworkPrefab, m_ServerNetworkManager)); + var instance = SpawnObject(m_NetworkPrefab, authority); + authoritySpawnedNetworkObjects.Add(instance.GetComponent()); } } // Assure the server-side spawned all NetworkObjects - yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ServerSpawnCount == k_NumberToSpawn); + yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.AuthoritySpawnCount == k_NumberToSpawn); // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned - yield return CreateAndStartNewClient(); + var newClient = CreateNewClient(); + yield return StartClient(newClient); if (m_UseHost) { - var delayCounter = 0; - while (m_ClientNetworkManagers.Length == 0) - { - delayCounter++; - Assert.True(delayCounter < 30, "TimeOut waiting for client to spawn!"); - yield return s_DefaultWaitForTick; - } - delayCounter = 0; - while (!m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId].ContainsKey(m_ClientNetworkManagers[0].LocalClientId)) - { - delayCounter++; - if (delayCounter >= 30) - { - VerboseDebug("Trap!"); - } - Assert.True(delayCounter < 30, "TimeOut waiting for client to spawn!"); - yield return s_DefaultWaitForTick; - } - - - var serverSideClientPlayerComponent = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); - var serverSideHostPlayerComponent = m_ServerNetworkManager.LocalClient.PlayerObject.GetComponent(); - var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent(); - var clientSideHostPlayerComponent = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ServerNetworkManager.LocalClientId].GetComponent(); + var serverSideClientPlayerComponent = m_PlayerNetworkObjects[authority.LocalClientId][newClient.LocalClientId].GetComponent(); + var serverSideHostPlayerComponent = authority.LocalClient.PlayerObject.GetComponent(); + var clientSidePlayerComponent = newClient.LocalClient.PlayerObject.GetComponent(); + var clientSideHostPlayerComponent = m_PlayerNetworkObjects[newClient.LocalClientId][authority.LocalClientId].GetComponent(); var modeText = m_DistributedAuthority ? "owner" : "server"; // Validate that the client side player values match the server side value of the client's player Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value, @@ -192,16 +168,15 @@ public IEnumerator NetworkObjectDeserializationFailure() else { // Spawn and connect another client when running as a server - yield return CreateAndStartNewClient(); - yield return WaitForConditionOrTimeOut(() => m_PlayerNetworkObjects[2].Count > 1); - AssertOnTimeout($"Timed out waiting for second client to have access to the first client's cloned player object!"); + var secondClient = CreateNewClient(); + yield return StartClient(secondClient); - var clientSide1PlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent(); - var clientSide2Player1Clone = m_PlayerNetworkObjects[2][clientSide1PlayerComponent.OwnerClientId].GetComponent(); + var clientSide1PlayerComponent = newClient.LocalClient.PlayerObject.GetComponent(); + var clientSide2Player1Clone = m_PlayerNetworkObjects[secondClient.LocalClientId][clientSide1PlayerComponent.OwnerClientId].GetComponent(); var clientOneId = clientSide1PlayerComponent.OwnerClientId; - var clientSide2PlayerComponent = m_ClientNetworkManagers[1].LocalClient.PlayerObject.GetComponent(); - var clientSide1Player2Clone = m_PlayerNetworkObjects[1][clientSide2PlayerComponent.OwnerClientId].GetComponent(); + var clientSide2PlayerComponent = secondClient.LocalClient.PlayerObject.GetComponent(); + var clientSide1Player2Clone = m_PlayerNetworkObjects[newClient.LocalClientId][clientSide2PlayerComponent.OwnerClientId].GetComponent(); var clientTwoId = clientSide2PlayerComponent.OwnerClientId; // Validate that client one's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not @@ -244,75 +219,57 @@ public IEnumerator NetworkObjectDeserializationFailure() } } - // DANGO-TODO: This scenario is only possible to do if we add a DA-Server to mock the CMB Service or we integrate the CMB Service AND we have updated NetworkVariable permissions - // to only allow the service to write. For now, we will skip this validation for distributed authority - if (!m_DistributedAuthority) + // Now validate all of the NetworkVariable values match to assure everything synchronized properly + foreach (var spawnedObject in authoritySpawnedNetworkObjects) { - // Now validate all of the NetworkVariable values match to assure everything synchronized properly - foreach (var spawnedObject in validSpawnedNetworkObjects) + foreach (var networkManager in m_NetworkManagers) { - foreach (var clientNetworkManager in m_ClientNetworkManagers) + if (networkManager == authority) { - //Validate that the connected client has spawned all of the instances that shouldn't have failed. - var clientSideNetworkObjects = s_GlobalNetworkObjects[clientNetworkManager.LocalClientId]; + continue; + } + //Validate that the connected client has spawned all of the instances that shouldn't have failed. + var clientSideNetworkObjects = s_GlobalNetworkObjects[networkManager.LocalClientId]; - Assert.IsTrue(NetworkBehaviourWithNetworkVariables.ClientSpawnCount[clientNetworkManager.LocalClientId] == validSpawnedNetworkObjects.Count, $"Client-{clientNetworkManager.LocalClientId} spawned " + - $"({NetworkBehaviourWithNetworkVariables.ClientSpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({validSpawnedNetworkObjects.Count})!"); + Assert.IsTrue(NetworkBehaviourWithNetworkVariables.NonAuthoritySpawnCount[networkManager.LocalClientId] == authoritySpawnedNetworkObjects.Count, $"Client-{networkManager.LocalClientId} spawned " + + $"({NetworkBehaviourWithNetworkVariables.NonAuthoritySpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({authoritySpawnedNetworkObjects.Count})!"); - var spawnedNetworkObject = spawnedObject.GetComponent(); - Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " + - $"{nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}"); + Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " + + $"{nameof(NetworkObject.NetworkObjectId)} of {spawnedObject.NetworkObjectId}"); - var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId]; - Assert.IsTrue(clientSideObject.NetworkManager == clientNetworkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!"); + var clientSideObject = clientSideNetworkObjects[spawnedObject.NetworkObjectId]; + Assert.IsTrue(clientSideObject.NetworkManager == networkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!"); - ValidateNetworkBehaviourWithNetworkVariables(spawnedNetworkObject, clientSideObject); - } + ValidateNetworkBehaviourWithNetworkVariables(spawnedObject, clientSideObject); } } } - private void ValidateNetworkBehaviourWithNetworkVariables(NetworkObject serverSideNetworkObject, NetworkObject clientSideNetworkObject) + private void ValidateNetworkBehaviourWithNetworkVariables(NetworkObject authorityNetworkObject, NetworkObject nonAuthorityNetworkObject) { - var serverSideComponent = serverSideNetworkObject.GetComponent(); - var clientSideComponent = clientSideNetworkObject.GetComponent(); + var authorityComponent = authorityNetworkObject.GetComponent(); + var nonAuthorityComponent = nonAuthorityNetworkObject.GetComponent(); string netVarName1 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); string netVarName2 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); string netVarName3 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); string netVarName4 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); - Assert.IsTrue(serverSideComponent.NetworkVariableData1.Count == clientSideComponent.NetworkVariableData1.Count, $"[{serverSideComponent.name}:{netVarName1}] Server side {nameof(NetworkList)} " + - $"count ({serverSideComponent.NetworkVariableData1.Count}) does not match the client side {nameof(NetworkList)} count ({clientSideComponent.NetworkVariableData1.Count})!"); + Assert.IsTrue(authorityComponent.NetworkVariableData1.Count == nonAuthorityComponent.NetworkVariableData1.Count, $"[{authorityComponent.name}:{netVarName1}] Server side {nameof(NetworkList)} " + + $"count ({authorityComponent.NetworkVariableData1.Count}) does not match the client side {nameof(NetworkList)} count ({nonAuthorityComponent.NetworkVariableData1.Count})!"); - for (int i = 0; i < serverSideComponent.NetworkVariableData1.Count; i++) + for (int i = 0; i < authorityComponent.NetworkVariableData1.Count; i++) { - Assert.IsTrue(serverSideComponent.NetworkVariableData1[i] == clientSideComponent.NetworkVariableData1[i], $"[{serverSideComponent.name}:{netVarName1}][Index:{i}] Server side instance value " + - $"({serverSideComponent.NetworkVariableData1[i]}) does not match the client side instance value ({clientSideComponent.NetworkVariableData1[i]})!"); + Assert.IsTrue(authorityComponent.NetworkVariableData1[i] == nonAuthorityComponent.NetworkVariableData1[i], $"[{authorityComponent.name}:{netVarName1}][Index:{i}] Server side instance value " + + $"({authorityComponent.NetworkVariableData1[i]}) does not match the client side instance value ({nonAuthorityComponent.NetworkVariableData1[i]})!"); } - Assert.IsTrue(serverSideComponent.NetworkVariableData2.Value == clientSideComponent.NetworkVariableData2.Value, $"[{serverSideComponent.name}:{netVarName2}] Server side instance value ({serverSideComponent.NetworkVariableData2.Value}) " + - $"does not match the client side instance value ({clientSideComponent.NetworkVariableData2.Value})!"); - Assert.IsTrue(serverSideComponent.NetworkVariableData3.Value == clientSideComponent.NetworkVariableData3.Value, $"[{serverSideComponent.name}:{netVarName3}] Server side instance value ({serverSideComponent.NetworkVariableData3.Value}) " + - $"does not match the client side instance value ({clientSideComponent.NetworkVariableData3.Value})!"); - Assert.IsTrue(serverSideComponent.NetworkVariableData4.Value == clientSideComponent.NetworkVariableData4.Value, $"[{serverSideComponent.name}:{netVarName4}] Server side instance value ({serverSideComponent.NetworkVariableData4.Value}) " + - $"does not match the client side instance value ({clientSideComponent.NetworkVariableData4.Value})!"); - } - - - private bool ClientSpawnedNetworkObjects(List spawnedObjectList) - { - var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId]; - - foreach (var spawnedObject in spawnedObjectList) - { - var serverSideSpawnedNetworkObject = spawnedObject.GetComponent(); - if (!clientSideNetworkObjects.ContainsKey(serverSideSpawnedNetworkObject.NetworkObjectId)) - { - return false; - } - } - return true; + Assert.IsTrue(authorityComponent.NetworkVariableData2.Value == nonAuthorityComponent.NetworkVariableData2.Value, $"[{authorityComponent.name}:{netVarName2}] Server side instance value ({authorityComponent.NetworkVariableData2.Value}) " + + $"does not match the client side instance value ({nonAuthorityComponent.NetworkVariableData2.Value})!"); + Assert.IsTrue(authorityComponent.NetworkVariableData3.Value == nonAuthorityComponent.NetworkVariableData3.Value, $"[{authorityComponent.name}:{netVarName3}] Server side instance value ({authorityComponent.NetworkVariableData3.Value}) " + + $"does not match the client side instance value ({nonAuthorityComponent.NetworkVariableData3.Value})!"); + Assert.IsTrue(authorityComponent.NetworkVariableData4.Value == nonAuthorityComponent.NetworkVariableData4.Value, $"[{authorityComponent.name}:{netVarName4}] Server side instance value ({authorityComponent.NetworkVariableData4.Value}) " + + $"does not match the client side instance value ({nonAuthorityComponent.NetworkVariableData4.Value})!"); } /// @@ -322,7 +279,8 @@ private bool ClientSpawnedNetworkObjects(List spawnedObjectList) [UnityTest] public IEnumerator NetworkBehaviourSynchronization() { - m_ServerNetworkManager.LogLevel = LogLevel.Normal; + var authority = GetAuthorityNetworkManager(); + authority.LogLevel = LogLevel.Normal; m_CurrentLogLevel = LogLevel.Normal; NetworkBehaviourSynchronizeFailureComponent.ResetBehaviour(); @@ -331,28 +289,29 @@ public IEnumerator NetworkBehaviourSynchronization() // Spawn 11 more NetworkObjects where there should be 4 of each failure type for (int i = 0; i < numberOfObjectsToSpawn; i++) { - var synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager); + var synchronizationObject = SpawnObject(m_SynchronizationPrefab, authority); var synchronizationBehaviour = synchronizationObject.GetComponent(); synchronizationBehaviour.AssignNextFailureType(); spawnedObjectList.Add(synchronizationObject); } // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned - yield return CreateAndStartNewClient(); + var newClient = CreateNewClient(); + yield return StartClient(newClient); // Validate that when a NetworkBehaviour fails to synchronize and is skipped over it does not // impact the rest of the NetworkBehaviours. - var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId]; - yield return WaitForConditionOrTimeOut(() => ClientSpawnedNetworkObjects(spawnedObjectList)); + var clientSideNetworkObjects = s_GlobalNetworkObjects[newClient.LocalClientId]; + yield return WaitForSpawnedOnAllOrTimeOut(clientSideNetworkObjects.Values); AssertOnTimeout($"Timed out waiting for newly joined client to spawn all NetworkObjects!"); foreach (var spawnedObject in spawnedObjectList) { - var serverSideSpawnedNetworkObject = spawnedObject.GetComponent(); - var clientSideObject = clientSideNetworkObjects[serverSideSpawnedNetworkObject.NetworkObjectId]; - var clientSideSpawnedNetworkObject = clientSideObject.GetComponent(); + var authorityObject = spawnedObject.GetComponent(); + var nonAuthorityObject = clientSideNetworkObjects[authorityObject.NetworkObjectId]; + var clientSideSpawnedNetworkObject = nonAuthorityObject.GetComponent(); - ValidateNetworkBehaviourWithNetworkVariables(serverSideSpawnedNetworkObject, clientSideSpawnedNetworkObject); + ValidateNetworkBehaviourWithNetworkVariables(authorityObject, clientSideSpawnedNetworkObject); } } @@ -362,12 +321,14 @@ public IEnumerator NetworkBehaviourSynchronization() [UnityTest] public IEnumerator NetworkBehaviourOnSynchronize() { - var serverSideInstance = SpawnObject(m_OnSynchronizePrefab, m_ServerNetworkManager).GetComponent(); + var authority = GetAuthorityNetworkManager(); + var serverSideInstance = SpawnObject(m_OnSynchronizePrefab, authority).GetComponent(); // Now spawn and connect a client that will have custom serialized data applied during the client synchronization process. - yield return CreateAndStartNewClient(); + var newClient = CreateNewClient(); + yield return StartClient(newClient); - var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId]; + var clientSideNetworkObjects = s_GlobalNetworkObjects[newClient.LocalClientId]; var clientSideInstance = clientSideNetworkObjects[serverSideInstance.NetworkObjectId].GetComponent(); // Validate the values match @@ -386,13 +347,13 @@ public IEnumerator NetworkBehaviourOnSynchronize() /// internal class NetworkBehaviourWithNetworkVariables : NetworkBehaviour { - public static int ServerSpawnCount { get; internal set; } - public static readonly Dictionary ClientSpawnCount = new Dictionary(); + public static int AuthoritySpawnCount { get; internal set; } + public static readonly Dictionary NonAuthoritySpawnCount = new Dictionary(); public static void ResetSpawnCount() { - ServerSpawnCount = 0; - ClientSpawnCount.Clear(); + AuthoritySpawnCount = 0; + NonAuthoritySpawnCount.Clear(); } private const uint k_MinDataBlocks = 1; @@ -424,15 +385,15 @@ public override void OnNetworkSpawn() { if (IsServer) { - ServerSpawnCount++; + AuthoritySpawnCount++; } else { - if (!ClientSpawnCount.ContainsKey(NetworkManager.LocalClientId)) + if (!NonAuthoritySpawnCount.ContainsKey(NetworkManager.LocalClientId)) { - ClientSpawnCount.Add(NetworkManager.LocalClientId, 0); + NonAuthoritySpawnCount.Add(NetworkManager.LocalClientId, 0); } - ClientSpawnCount[NetworkManager.LocalClientId]++; + NonAuthoritySpawnCount[NetworkManager.LocalClientId]++; } base.OnNetworkSpawn(); @@ -496,8 +457,8 @@ public override void OnNetworkSpawn() internal class NetworkBehaviourSynchronizeFailureComponent : NetworkBehaviour { public static int NumberOfFailureTypes { get; internal set; } - public static int ServerSpawnCount { get; internal set; } - public static int ClientSpawnCount { get; internal set; } + public static int AuthoritySpawnCount { get; internal set; } + public static int NonAuthoritySpawnCount { get; internal set; } private static FailureTypes s_FailureType = FailureTypes.None; @@ -513,8 +474,8 @@ public enum FailureTypes public static void ResetBehaviour() { - ServerSpawnCount = 0; - ClientSpawnCount = 0; + AuthoritySpawnCount = 0; + NonAuthoritySpawnCount = 0; s_FailureType = FailureTypes.None; NumberOfFailureTypes = System.Enum.GetValues(typeof(FailureTypes)).Length; } @@ -595,6 +556,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade } case FailureTypes.DontReadAnything: { + Debug.Log("Don't read anything is being run"); // Don't read anything break; } @@ -641,14 +603,14 @@ private void Awake() public override void OnNetworkSpawn() { - if (IsServer) + if (HasAuthority) { - ServerSpawnCount++; + AuthoritySpawnCount++; m_MyCustomData.GenerateData((ushort)Random.Range(1, 512)); } else { - ClientSpawnCount++; + NonAuthoritySpawnCount++; } base.OnNetworkSpawn(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs index 6c09212a18..1ef0a13eb4 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs @@ -488,8 +488,7 @@ private bool Object1IsNotVisibileToClient() m_ErrorLog.AppendLine($"{m_NetSpawnedObject1.name} is still visible to Client-{m_ClientWithoutVisibility}!"); } } - else - if (client.SpawnManager.SpawnedObjects[m_NetSpawnedObject1.NetworkObjectId].IsNetworkVisibleTo(m_ClientWithoutVisibility)) + else if (client.SpawnManager.SpawnedObjects[m_NetSpawnedObject1.NetworkObjectId].IsNetworkVisibleTo(m_ClientWithoutVisibility)) { m_ErrorLog.AppendLine($"Local instance of {m_NetSpawnedObject1.name} on Client-{client.LocalClientId} thinks Client-{m_ClientWithoutVisibility} still has visibility!"); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/InterpolationStopAndStartMotionTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/InterpolationStopAndStartMotionTest.cs index d064d7b3b2..3690d6fb6e 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/InterpolationStopAndStartMotionTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/InterpolationStopAndStartMotionTest.cs @@ -101,7 +101,7 @@ private bool WaitForInstancesToFinishInterpolation() return true; } - [UnityTest] + [UnityTest, Ignore("Disabled due to instability. Tracked by MTT-15432.")] public IEnumerator StopAndStartMotion() { m_IsSecondPass = false; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformNonAuthorityTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformNonAuthorityTests.cs index e5fb5a4ba9..350f312973 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformNonAuthorityTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformNonAuthorityTests.cs @@ -418,8 +418,7 @@ private bool ShouldSyncAxis(bool first, bool second, bool lastValue) // make the last one disabled. return false; } - else - if (!first && !second) + else if (!first && !second) { // If both are disabled, then make the // last one enabled. diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs new file mode 100644 index 0000000000..b5fd31ae71 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs @@ -0,0 +1,91 @@ +#if UNIFIED_NETCODE +using System.Collections; +using NUnit.Framework; +using Unity.Netcode.Components; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + + +namespace Unity.Netcode.RuntimeTests +{ + /// + /// Test class that deliberately removes some functionality from NetworkTransform that is conditionally disabled + /// by the presence of ghost objects in the base class. This is to help be certain that the network transform + /// is not doing the work, but that the work is being done by N4E's snapshots. + /// + internal class DoNothingNetworkTransform : NetworkTransform + { + public override void OnNetworkSpawn() + { + // Deliberately left empty + } + + internal override void InternalInitialization(bool isOwnershipChange = false) + { + // Deliberately left empty + } + } + + [TestFixture(HostOrServer.UnifiedHost)] + internal class UnifiedNetworkTransformTest : IntegrationTestWithApproximation + { + protected override int NumberOfClients => 2; + + private GameObject m_Prefab; + private NetworkObject m_Instance; + + public UnifiedNetworkTransformTest(HostOrServer hostOrServer) : base(hostOrServer) + { + } + + protected override bool OnSetVerboseDebug() + { + return false; + } + + protected override IEnumerator OnSetup() + { + // Creates the hybrid prefab + m_Prefab = CreateNetworkObjectPrefab("HybridPrefab"); + m_Prefab.AddComponent(); + return base.OnSetup(); + } + + [UnityTest] + public IEnumerator BasicMovementTest() + { + var authority = GetAuthorityNetworkManager(); + m_Instance = SpawnObject(m_Prefab, m_ServerNetworkManager).GetComponent(); + + // Wait 5 seconds so we will dump any deferred messages if it failed on clients + // when checking to see if it spawned or not on the clients next. + // Enable this to debug deferred + //yield return new WaitForSeconds(5); + + yield return WaitForSpawnedOnAllOrTimeOut(m_Instance); + AssertOnTimeout($"Failed to spawn {m_Instance.name} on all clients!"); + + VerboseDebug("All clients spawned instance!"); + + var originalPos = authority.LocalClient.PlayerObject.transform.position; + var newPos = originalPos + new Vector3(1, 1, 1); + + m_Instance.transform.position = newPos; + + foreach (var client in m_ClientNetworkManagers) + { + Assert.IsTrue(Approximately(originalPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position)); + } + + yield return new WaitForSeconds(1); + + foreach (var client in m_ClientNetworkManagers) + { + Assert.IsTrue(Approximately(newPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position)); + } + VerboseDebug("Test Passed!"); + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs.meta new file mode 100644 index 0000000000..10d990cfa3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0f26fa7bd5474b3f9947e0813374b50f +timeCreated: 1775078549 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransformAnticipationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransformAnticipationTests.cs index 61024d46da..f1ead09032 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransformAnticipationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransformAnticipationTests.cs @@ -6,6 +6,8 @@ using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; using UnityEngine.TestTools.Utils; +using Quaternion = UnityEngine.Quaternion; +using Vector3 = UnityEngine.Vector3; namespace Unity.Netcode.RuntimeTests { @@ -17,6 +19,12 @@ public void MoveRpc(Vector3 newPosition) transform.position = newPosition; } + [Rpc(SendTo.Server)] + public void LocalMoveRpc(Vector3 newPosition) + { + transform.localPosition = newPosition; + } + [Rpc(SendTo.Server)] public void ScaleRpc(Vector3 newScale) { @@ -29,6 +37,12 @@ public void RotateRpc(Quaternion newRotation) transform.rotation = newRotation; } + [Rpc(SendTo.Server)] + public void LocalRotateRpc(Quaternion newRotation) + { + transform.localRotation = newRotation; + } + public bool ShouldSmooth = false; public bool ShouldMove = false; @@ -59,12 +73,22 @@ internal class NetworkTransformAnticipationTests : NetcodeIntegrationTest protected override bool m_SetupIsACoroutine => false; protected override bool m_TearDownIsACoroutine => false; + private GameObject m_TestPrefab; + protected override void OnPlayerPrefabGameObjectCreated() { m_PlayerPrefab.AddComponent(); m_PlayerPrefab.AddComponent(); } + protected override void OnServerAndClientsCreated() + { + m_TestPrefab = CreateNetworkObjectPrefab("child object"); + var transform = m_TestPrefab.AddComponent(); + transform.InLocalSpace = true; + m_TestPrefab.AddComponent(); + } + protected override void OnTimeTravelServerAndClientsConnected() { var serverComponent = GetServerComponent(); @@ -80,6 +104,13 @@ protected override void OnTimeTravelServerAndClientsConnected() otherClientComponent.transform.position = Vector3.zero; otherClientComponent.transform.localScale = Vector3.one; otherClientComponent.transform.rotation = Quaternion.LookRotation(Vector3.forward); + + var childObject = SpawnObject(m_TestPrefab, m_ServerNetworkManager); + childObject.transform.parent = serverComponent.transform; + childObject.transform.localPosition = Vector3.zero; + childObject.transform.localScale = Vector3.one; + childObject.transform.localRotation = Quaternion.LookRotation(Vector3.forward); + WaitForSpawnedOnAllOrTimeOutWithTimeTravel(childObject.GetComponent()); } public AnticipatedNetworkTransform GetTestComponent() @@ -115,16 +146,36 @@ public AnticipatedNetworkTransform GetOtherClientComponent() return null; } + public AnticipatedNetworkTransform GetChildComponent(AnticipatedNetworkTransform parent) + { + foreach (var tr in parent.GetComponentsInChildren()) + { + if (tr == parent) + { + continue; + } + + return tr; + } + + return null; + } + [Test] public void WhenAnticipating_ValueChangesImmediately() { var testComponent = GetTestComponent(); + var childComponent = GetChildComponent(testComponent); var quaternionComparer = new QuaternionEqualityComparer(0.000001f); testComponent.AnticipateMove(new Vector3(0, 1, 2)); testComponent.AnticipateScale(new Vector3(1, 2, 3)); testComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + childComponent.AnticipateMove(new Vector3(0, 1, 2)); + childComponent.AnticipateScale(new Vector3(1, 2, 3)); + childComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + Assert.AreEqual(new Vector3(0, 1, 2), testComponent.transform.position); Assert.AreEqual(new Vector3(1, 2, 3), testComponent.transform.localScale); Assert.That(testComponent.transform.rotation, Is.EqualTo(Quaternion.LookRotation(new Vector3(2, 3, 4))).Using(quaternionComparer)); // Quaternion comparer added due to FP precision problems on Android devices. @@ -132,40 +183,71 @@ public void WhenAnticipating_ValueChangesImmediately() Assert.AreEqual(new Vector3(0, 1, 2), testComponent.AnticipatedState.Position); Assert.AreEqual(new Vector3(1, 2, 3), testComponent.AnticipatedState.Scale); Assert.That(testComponent.AnticipatedState.Rotation, Is.EqualTo(Quaternion.LookRotation(new Vector3(2, 3, 4))).Using(quaternionComparer)); // Quaternion comparer added due to FP precision problems on Android devices. + + Assert.AreEqual(new Vector3(0, 1, 2), childComponent.transform.localPosition); + Assert.AreEqual(new Vector3(1, 2, 3), childComponent.transform.localScale); + Assert.That(childComponent.transform.localRotation, Is.EqualTo(Quaternion.LookRotation(new Vector3(2, 3, 4))).Using(quaternionComparer)); // Quaternion comparer added due to FP precision problems on Android devices. + + Assert.AreEqual(new Vector3(0, 1, 2), childComponent.AnticipatedState.Position); + Assert.AreEqual(new Vector3(1, 2, 3), childComponent.AnticipatedState.Scale); + Assert.That(childComponent.AnticipatedState.Rotation, Is.EqualTo(Quaternion.LookRotation(new Vector3(2, 3, 4))).Using(quaternionComparer)); // Quaternion comparer added due to FP precision problems on Android devices. } [Test] public void WhenAnticipating_AuthoritativeValueDoesNotChange() { var testComponent = GetTestComponent(); + var childComponent = GetChildComponent(testComponent); var startPosition = testComponent.transform.position; var startScale = testComponent.transform.localScale; var startRotation = testComponent.transform.rotation; + var childStartPosition = childComponent.transform.localPosition; + var childStartScale = childComponent.transform.localScale; + var childStartRotation = childComponent.transform.localRotation; + testComponent.AnticipateMove(new Vector3(0, 1, 2)); testComponent.AnticipateScale(new Vector3(1, 2, 3)); testComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + childComponent.AnticipateMove(new Vector3(0, 1, 2)); + childComponent.AnticipateScale(new Vector3(1, 2, 3)); + childComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + Assert.AreEqual(startPosition, testComponent.AuthoritativeState.Position); Assert.AreEqual(startScale, testComponent.AuthoritativeState.Scale); Assert.AreEqual(startRotation, testComponent.AuthoritativeState.Rotation); + + Assert.AreEqual(childStartPosition, childComponent.AuthoritativeState.Position); + Assert.AreEqual(childStartScale, childComponent.AuthoritativeState.Scale); + Assert.AreEqual(childStartRotation, childComponent.AuthoritativeState.Rotation); } [Test] public void WhenAnticipating_ServerDoesNotChange() { var testComponent = GetTestComponent(); + var childComponent = GetChildComponent(testComponent); var startPosition = testComponent.transform.position; var startScale = testComponent.transform.localScale; var startRotation = testComponent.transform.rotation; + var childStartPosition = childComponent.transform.localPosition; + var childStartScale = childComponent.transform.localScale; + var childStartRotation = childComponent.transform.localRotation; + testComponent.AnticipateMove(new Vector3(0, 1, 2)); testComponent.AnticipateScale(new Vector3(1, 2, 3)); testComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + childComponent.AnticipateMove(new Vector3(0, 1, 2)); + childComponent.AnticipateScale(new Vector3(1, 2, 3)); + childComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + var serverComponent = GetServerComponent(); + var serverChild = GetChildComponent(serverComponent); Assert.AreEqual(startPosition, serverComponent.AuthoritativeState.Position); Assert.AreEqual(startScale, serverComponent.AuthoritativeState.Scale); @@ -174,6 +256,13 @@ public void WhenAnticipating_ServerDoesNotChange() Assert.AreEqual(startScale, serverComponent.AnticipatedState.Scale); Assert.AreEqual(startRotation, serverComponent.AnticipatedState.Rotation); + Assert.AreEqual(childStartPosition, serverChild.AuthoritativeState.Position); + Assert.AreEqual(childStartScale, serverChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(childStartRotation, serverChild.AuthoritativeState.Rotation); + Assert.AreEqual(childStartPosition, serverChild.AnticipatedState.Position); + Assert.AreEqual(childStartScale, serverChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(childStartRotation, serverChild.AnticipatedState.Rotation); + TimeTravel(2, 120); Assert.AreEqual(startPosition, serverComponent.AuthoritativeState.Position); @@ -182,22 +271,35 @@ public void WhenAnticipating_ServerDoesNotChange() Assert.AreEqual(startPosition, serverComponent.AnticipatedState.Position); Assert.AreEqual(startScale, serverComponent.AnticipatedState.Scale); Assert.AreEqual(startRotation, serverComponent.AnticipatedState.Rotation); + + Assert.AreEqual(childStartPosition, serverChild.AuthoritativeState.Position); + Assert.AreEqual(childStartScale, serverChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(childStartRotation, serverChild.AuthoritativeState.Rotation); + Assert.AreEqual(childStartPosition, serverChild.AnticipatedState.Position); + Assert.AreEqual(childStartScale, serverChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(childStartRotation, serverChild.AnticipatedState.Rotation); } [Test] public void WhenAnticipating_OtherClientDoesNotChange() { var testComponent = GetTestComponent(); + var childComponent = GetChildComponent(testComponent); var startPosition = testComponent.transform.position; var startScale = testComponent.transform.localScale; var startRotation = testComponent.transform.rotation; + var childStartPosition = childComponent.transform.localPosition; + var childStartScale = childComponent.transform.localScale; + var childStartRotation = childComponent.transform.localRotation; + testComponent.AnticipateMove(new Vector3(0, 1, 2)); testComponent.AnticipateScale(new Vector3(1, 2, 3)); testComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); var otherClientComponent = GetOtherClientComponent(); + var otherClientChild = GetChildComponent(otherClientComponent); Assert.AreEqual(startPosition, otherClientComponent.AuthoritativeState.Position); Assert.AreEqual(startScale, otherClientComponent.AuthoritativeState.Scale); @@ -206,6 +308,13 @@ public void WhenAnticipating_OtherClientDoesNotChange() Assert.AreEqual(startScale, otherClientComponent.AnticipatedState.Scale); Assert.AreEqual(startRotation, otherClientComponent.AnticipatedState.Rotation); + Assert.AreEqual(childStartPosition, otherClientChild.AuthoritativeState.Position); + Assert.AreEqual(childStartScale, otherClientChild.AuthoritativeState.Scale); + Assert.AreEqual(childStartRotation, otherClientChild.AuthoritativeState.Rotation); + Assert.AreEqual(childStartPosition, otherClientChild.AnticipatedState.Position); + Assert.AreEqual(childStartScale, otherClientChild.AnticipatedState.Scale); + Assert.AreEqual(childStartRotation, otherClientChild.AnticipatedState.Rotation); + TimeTravel(2, 120); Assert.AreEqual(startPosition, otherClientComponent.AuthoritativeState.Position); @@ -214,24 +323,45 @@ public void WhenAnticipating_OtherClientDoesNotChange() Assert.AreEqual(startPosition, otherClientComponent.AnticipatedState.Position); Assert.AreEqual(startScale, otherClientComponent.AnticipatedState.Scale); Assert.AreEqual(startRotation, otherClientComponent.AnticipatedState.Rotation); + + Assert.AreEqual(childStartPosition, otherClientChild.AuthoritativeState.Position); + Assert.AreEqual(childStartScale, otherClientChild.AuthoritativeState.Scale); + Assert.AreEqual(childStartRotation, otherClientChild.AuthoritativeState.Rotation); + Assert.AreEqual(childStartPosition, otherClientChild.AnticipatedState.Position); + Assert.AreEqual(childStartScale, otherClientChild.AnticipatedState.Scale); + Assert.AreEqual(childStartRotation, otherClientChild.AnticipatedState.Rotation); } [Test] public void WhenServerChangesSnapValue_ValuesAreUpdated() { var testComponent = GetTestComponent(); + var testChild = GetChildComponent(testComponent); var serverComponent = GetServerComponent(); + var serverChild = GetChildComponent(serverComponent); serverComponent.Interpolate = false; + serverChild.Interpolate = false; testComponent.AnticipateMove(new Vector3(0, 1, 2)); testComponent.AnticipateScale(new Vector3(1, 2, 3)); testComponent.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + testChild.AnticipateMove(new Vector3(0, 1, 2)); + testChild.AnticipateScale(new Vector3(1, 2, 3)); + testChild.AnticipateRotate(Quaternion.LookRotation(new Vector3(2, 3, 4))); + var rpcComponent = testComponent.GetComponent(); rpcComponent.MoveRpc(new Vector3(2, 3, 4)); + var childRpcComponent = testChild.GetComponent(); + childRpcComponent.LocalMoveRpc(new Vector3(2, 3, 4)); - WaitForMessageReceivedWithTimeTravel(new List { m_ServerNetworkManager }); + WaitForMessagesReceivedWithTimeTravel(new List + { + typeof(RpcMessage), + typeof(RpcMessage), + }, new List { m_ServerNetworkManager }); var otherClientComponent = GetOtherClientComponent(); + var otherClientChild = GetChildComponent(otherClientComponent); WaitForConditionOrTimeOutWithTimeTravel(() => testComponent.AuthoritativeState.Position == serverComponent.transform.position && otherClientComponent.AuthoritativeState.Position == serverComponent.transform.position); @@ -242,6 +372,16 @@ public void WhenServerChangesSnapValue_ValuesAreUpdated() Assert.AreEqual(serverComponent.transform.position, otherClientComponent.transform.position); Assert.AreEqual(serverComponent.transform.position, otherClientComponent.AnticipatedState.Position); Assert.AreEqual(serverComponent.transform.position, otherClientComponent.AuthoritativeState.Position); + + Assert.AreEqual(serverChild.transform.localPosition, testChild.transform.localPosition); + Assert.AreNotEqual(serverChild.transform.localPosition, testChild.transform.position); + Assert.AreEqual(serverChild.transform.localPosition, testChild.AnticipatedState.Position); + Assert.AreEqual(serverChild.transform.localPosition, testChild.AuthoritativeState.Position); + + Assert.AreEqual(serverChild.transform.localPosition, otherClientChild.transform.localPosition); + Assert.AreNotEqual(serverChild.transform.localPosition, otherClientChild.transform.position); + Assert.AreEqual(serverChild.transform.localPosition, otherClientChild.AnticipatedState.Position); + Assert.AreEqual(serverChild.transform.localPosition, otherClientChild.AuthoritativeState.Position); } public void AssertQuaternionsAreEquivalent(Quaternion a, Quaternion b) @@ -264,15 +404,23 @@ public void WhenServerChangesSmoothValue_ValuesAreLerped() { var testComponent = GetTestComponent(); var otherClientComponent = GetOtherClientComponent(); + var testChild = GetChildComponent(testComponent); + var otherChild = GetChildComponent(otherClientComponent); testComponent.StaleDataHandling = StaleDataHandling.Ignore; otherClientComponent.StaleDataHandling = StaleDataHandling.Ignore; + testChild.StaleDataHandling = StaleDataHandling.Ignore; + otherChild.StaleDataHandling = StaleDataHandling.Ignore; var serverComponent = GetServerComponent(); + var serverChild = GetChildComponent(serverComponent); serverComponent.Interpolate = false; + serverChild.Interpolate = false; testComponent.GetComponent().ShouldSmooth = true; otherClientComponent.GetComponent().ShouldSmooth = true; + testChild.GetComponent().ShouldSmooth = true; + otherChild.GetComponent().ShouldSmooth = true; var startPosition = testComponent.transform.position; var startScale = testComponent.transform.localScale; @@ -287,17 +435,27 @@ public void WhenServerChangesSmoothValue_ValuesAreLerped() testComponent.AnticipateMove(anticipePosition); testComponent.AnticipateScale(anticipeScale); testComponent.AnticipateRotate(anticipeRotation); + testChild.AnticipateMove(anticipePosition); + testChild.AnticipateScale(anticipeScale); + testChild.AnticipateRotate(anticipeRotation); var rpcComponent = testComponent.GetComponent(); rpcComponent.MoveRpc(serverSetPosition); rpcComponent.RotateRpc(serverSetRotation); rpcComponent.ScaleRpc(serverSetScale); + var childRpcComponent = testChild.GetComponent(); + childRpcComponent.LocalMoveRpc(serverSetPosition); + childRpcComponent.LocalRotateRpc(serverSetRotation); + childRpcComponent.ScaleRpc(serverSetScale); WaitForMessagesReceivedWithTimeTravel(new List { typeof(RpcMessage), typeof(RpcMessage), typeof(RpcMessage), + typeof(RpcMessage), + typeof(RpcMessage), + typeof(RpcMessage), }, new List { m_ServerNetworkManager }); WaitForMessageReceivedWithTimeTravel(m_ClientNetworkManagers.ToList()); @@ -327,6 +485,30 @@ public void WhenServerChangesSmoothValue_ValuesAreLerped() AssertVectorsAreEquivalent(serverSetScale, otherClientComponent.AuthoritativeState.Scale); AssertQuaternionsAreEquivalent(serverSetRotation, otherClientComponent.AuthoritativeState.Rotation); + AssertVectorsAreEquivalent(Vector3.Lerp(anticipePosition, serverSetPosition, percentChanged), testChild.transform.localPosition); + AssertVectorsAreEquivalent(Vector3.Lerp(anticipeScale, serverSetScale, percentChanged), testChild.transform.localScale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(anticipeRotation, serverSetRotation, percentChanged), testChild.transform.localRotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(anticipePosition, serverSetPosition, percentChanged), testChild.AnticipatedState.Position); + AssertVectorsAreEquivalent(Vector3.Lerp(anticipeScale, serverSetScale, percentChanged), testChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(anticipeRotation, serverSetRotation, percentChanged), testChild.AnticipatedState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, testChild.AuthoritativeState.Position); + AssertVectorsAreEquivalent(serverSetScale, testChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, testChild.AuthoritativeState.Rotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(startPosition, serverSetPosition, percentChanged), otherChild.transform.localPosition); + AssertVectorsAreEquivalent(Vector3.Lerp(startScale, serverSetScale, percentChanged), otherChild.transform.localScale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(startRotation, serverSetRotation, percentChanged), otherChild.transform.localRotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(startPosition, serverSetPosition, percentChanged), otherChild.AnticipatedState.Position); + AssertVectorsAreEquivalent(Vector3.Lerp(startScale, serverSetScale, percentChanged), otherChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(startRotation, serverSetRotation, percentChanged), otherChild.AnticipatedState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, otherChild.AuthoritativeState.Position); + AssertVectorsAreEquivalent(serverSetScale, otherChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, otherChild.AuthoritativeState.Rotation); + for (var i = 1; i < 60; ++i) { TimeTravel(1f / 60f, 1); @@ -355,6 +537,30 @@ public void WhenServerChangesSmoothValue_ValuesAreLerped() AssertVectorsAreEquivalent(serverSetPosition, otherClientComponent.AuthoritativeState.Position); AssertVectorsAreEquivalent(serverSetScale, otherClientComponent.AuthoritativeState.Scale); AssertQuaternionsAreEquivalent(serverSetRotation, otherClientComponent.AuthoritativeState.Rotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(anticipePosition, serverSetPosition, percentChanged), testChild.transform.localPosition); + AssertVectorsAreEquivalent(Vector3.Lerp(anticipeScale, serverSetScale, percentChanged), testChild.transform.localScale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(anticipeRotation, serverSetRotation, percentChanged), testChild.transform.localRotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(anticipePosition, serverSetPosition, percentChanged), testChild.AnticipatedState.Position); + AssertVectorsAreEquivalent(Vector3.Lerp(anticipeScale, serverSetScale, percentChanged), testChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(anticipeRotation, serverSetRotation, percentChanged), testChild.AnticipatedState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, testChild.AuthoritativeState.Position); + AssertVectorsAreEquivalent(serverSetScale, testChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, testChild.AuthoritativeState.Rotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(startPosition, serverSetPosition, percentChanged), otherChild.transform.localPosition); + AssertVectorsAreEquivalent(Vector3.Lerp(startScale, serverSetScale, percentChanged), otherChild.transform.localScale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(startRotation, serverSetRotation, percentChanged), otherChild.transform.localRotation); + + AssertVectorsAreEquivalent(Vector3.Lerp(startPosition, serverSetPosition, percentChanged), otherChild.AnticipatedState.Position); + AssertVectorsAreEquivalent(Vector3.Lerp(startScale, serverSetScale, percentChanged), otherChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(Quaternion.Lerp(startRotation, serverSetRotation, percentChanged), otherChild.AnticipatedState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, otherChild.AuthoritativeState.Position); + AssertVectorsAreEquivalent(serverSetScale, otherChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, otherChild.AuthoritativeState.Rotation); } TimeTravel(1f / 60f, 1); @@ -381,6 +587,30 @@ public void WhenServerChangesSmoothValue_ValuesAreLerped() AssertVectorsAreEquivalent(serverSetPosition, otherClientComponent.AuthoritativeState.Position); AssertVectorsAreEquivalent(serverSetScale, otherClientComponent.AuthoritativeState.Scale); AssertQuaternionsAreEquivalent(serverSetRotation, otherClientComponent.AuthoritativeState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, testChild.transform.localPosition); + AssertVectorsAreEquivalent(serverSetScale, testChild.transform.localScale); + AssertQuaternionsAreEquivalent(serverSetRotation, testChild.transform.localRotation); + + AssertVectorsAreEquivalent(serverSetPosition, testChild.AnticipatedState.Position); + AssertVectorsAreEquivalent(serverSetScale, testChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, testChild.AnticipatedState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, testChild.AuthoritativeState.Position); + AssertVectorsAreEquivalent(serverSetScale, testChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, testChild.AuthoritativeState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, otherChild.transform.localPosition); + AssertVectorsAreEquivalent(serverSetScale, otherChild.transform.localScale); + AssertQuaternionsAreEquivalent(serverSetRotation, otherChild.transform.localRotation); + + AssertVectorsAreEquivalent(serverSetPosition, otherChild.AnticipatedState.Position); + AssertVectorsAreEquivalent(serverSetScale, otherChild.AnticipatedState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, otherChild.AnticipatedState.Rotation); + + AssertVectorsAreEquivalent(serverSetPosition, otherChild.AuthoritativeState.Position); + AssertVectorsAreEquivalent(serverSetScale, otherChild.AuthoritativeState.Scale); + AssertQuaternionsAreEquivalent(serverSetRotation, otherChild.AuthoritativeState.Rotation); } [Test] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs index ecaf3ec792..7f4dba5f16 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -1,6 +1,6 @@ using System; using System.Collections; -using System.Linq; +using System.Collections.Generic; using NUnit.Framework; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; @@ -41,9 +41,10 @@ public void RegisterCustomLoopInTheMiddle() PlayerLoop.SetPlayerLoop(curPlayerLoop); NetworkUpdateLoop.UnregisterLoopSystems(); - + var subSystemArray = PlayerLoop.GetCurrentPlayerLoop().subSystemList[0].subSystemList; + var lastType = subSystemArray[subSystemArray.Length - 1].type; // our custom `PlayerLoopSystem` with the type of `NetworkUpdateLoopTests` should still exist - Assert.AreEqual(typeof(NetworkUpdateLoopTests), PlayerLoop.GetCurrentPlayerLoop().subSystemList[0].subSystemList.Last().type); + Assert.AreEqual(typeof(NetworkUpdateLoopTests), lastType); } // replace the current PlayerLoop with the cached PlayerLoop after the test PlayerLoop.SetPlayerLoop(cachedPlayerLoop); @@ -94,7 +95,14 @@ public void UpdateStageSystems() for (int i = 0; i < currentPlayerLoop.subSystemList.Length; i++) { var playerLoopSystem = currentPlayerLoop.subSystemList[i]; - var subsystems = playerLoopSystem.subSystemList.ToList(); + // New behaviour (6000.6.x) + // Some PlayerLoopSystems can evidently now have no sub-system lists. + if (playerLoopSystem.subSystemList == null) + { + // Ignore any PlayerLoopSystem with no sub-system lists. + continue; + } + var subsystems = new List(playerLoopSystem.subSystemList); if (playerLoopSystem.type == typeof(Initialization)) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs index 5f3be93f57..daceeee8b9 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs @@ -15,6 +15,10 @@ namespace Unity.Netcode.RuntimeTests [TestFixture(HostOrServer.Host)] [TestFixture(HostOrServer.DAHost)] [TestFixture(HostOrServer.Server)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] + [TestFixture(HostOrServer.UnifiedServer)] +#endif internal class NetworkListTests : NetcodeIntegrationTest { protected override int NumberOfClients => 3; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableBaseInitializesWhenPersisted.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableBaseInitializesWhenPersisted.cs index 0245602fcd..6e8dc8cde9 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableBaseInitializesWhenPersisted.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableBaseInitializesWhenPersisted.cs @@ -26,7 +26,6 @@ protected override void OnOneTimeSetup() s_NetworkPrefab = new GameObject("PresistPrefab"); var networkObject = s_NetworkPrefab.AddComponent(); networkObject.GlobalObjectIdHash = 8888888; - networkObject.SetSceneObjectStatus(false); s_NetworkPrefab.AddComponent(); s_NetworkPrefab.AddComponent(); // Create enough prefab instance handlers to be re-used for all tests. @@ -374,7 +373,6 @@ public NetworkObject GetInstance() if (PrefabInstances.Count == 0) { instanceToReturn = Object.Instantiate(m_NetworkPrefab).GetComponent(); - instanceToReturn.SetSceneObjectStatus(false); instanceToReturn.gameObject.SetActive(true); } else diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.cs deleted file mode 100644 index 089eeee542..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbody2DTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -#if COM_UNITY_MODULES_PHYSICS2D -using System.Collections; -using NUnit.Framework; -using Unity.Netcode.Components; -using UnityEngine; -using UnityEngine.TestTools; -using Unity.Netcode.TestHelpers.Runtime; - -namespace Unity.Netcode.RuntimeTests -{ - internal class NetworkRigidbody2DDynamicTest : NetworkRigidbody2DTestBase - { - public override bool Kinematic => false; - } - - internal class NetworkRigidbody2DKinematicTest : NetworkRigidbody2DTestBase - { - public override bool Kinematic => true; - } - - public abstract class NetworkRigidbody2DTestBase : NetcodeIntegrationTest - { - protected override int NumberOfClients => 1; - - public abstract bool Kinematic { get; } - - protected override void OnCreatePlayerPrefab() - { - m_PlayerPrefab.AddComponent(); - m_PlayerPrefab.AddComponent(); - m_PlayerPrefab.AddComponent(); - m_PlayerPrefab.GetComponent().interpolation = RigidbodyInterpolation2D.Interpolate; - m_PlayerPrefab.GetComponent().isKinematic = Kinematic; - } - - /// - /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. - /// - /// - [UnityTest] - public IEnumerator TestRigidbodyKinematicEnableDisable() - { - // This is the *SERVER VERSION* of the *CLIENT PLAYER* - var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); - yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult); - var serverPlayer = serverClientPlayerResult.Result.gameObject; - - // This is the *CLIENT VERSION* of the *CLIENT PLAYER* - var clientClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); - yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult); - var clientPlayer = clientClientPlayerResult.Result.gameObject; - - Assert.IsNotNull(serverPlayer); - Assert.IsNotNull(clientPlayer); - - yield return WaitForTicks(m_ServerNetworkManager, 5); - - // server rigidbody has authority and should have a kinematic mode of false - Assert.True(serverPlayer.GetComponent().isKinematic == Kinematic); - Assert.AreEqual(RigidbodyInterpolation2D.Interpolate, serverPlayer.GetComponent().interpolation); - - // client rigidbody has no authority and should have a kinematic mode of true - Assert.True(clientPlayer.GetComponent().isKinematic); - Assert.AreEqual(RigidbodyInterpolation2D.None, clientPlayer.GetComponent().interpolation); - - // despawn the server player, (but keep it around on the server) - serverPlayer.GetComponent().Despawn(false); - - yield return WaitForTicks(m_ServerNetworkManager, 5); - - // This should equal Kinematic - Assert.IsTrue(serverPlayer.GetComponent().isKinematic == Kinematic); - - yield return WaitForTicks(m_ServerNetworkManager, 5); - - Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned. - } - } -} -#endif // COM_UNITY_MODULES_PHYSICS2D diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbodyTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbodyTest.cs index 4509797369..bf5b77b42f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbodyTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/NetworkRigidbodyTest.cs @@ -1,7 +1,6 @@ -#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D +#if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D using System.Collections; using System.Collections.Generic; -using System.Text; using NUnit.Framework; using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; @@ -10,491 +9,231 @@ namespace Unity.Netcode.RuntimeTests { - [TestFixture(RigidbodyInterpolation.Interpolate, true, true)] // This should be allowed under all condistions when using Rigidbody motion - [TestFixture(RigidbodyInterpolation.Extrapolate, true, true)] // This should not allow extrapolation on non-auth instances when using Rigidbody motion & NT interpolation - [TestFixture(RigidbodyInterpolation.Extrapolate, false, true)] // This should allow extrapolation on non-auth instances when using Rigidbody & NT has no interpolation - [TestFixture(RigidbodyInterpolation.Interpolate, true, false)] // This should not allow kinematic instances to have Rigidbody interpolation enabled - [TestFixture(RigidbodyInterpolation.Interpolate, false, false)] // Testing that rigid body interpolation remains the same if NT interpolate is disabled + [TestFixture(HostOrServer.Server)] + [TestFixture(HostOrServer.Host)] + [TestFixture(HostOrServer.DAHost)] internal class NetworkRigidbodyTest : NetcodeIntegrationTest { protected override int NumberOfClients => 1; - private bool m_NetworkTransformInterpolate; - private bool m_UseRigidBodyForMotion; - private RigidbodyInterpolation m_RigidbodyInterpolation; - public NetworkRigidbodyTest(RigidbodyInterpolation rigidbodyInterpolation, bool networkTransformInterpolate, bool useRigidbodyForMotion) - { - m_RigidbodyInterpolation = rigidbodyInterpolation; - m_NetworkTransformInterpolate = networkTransformInterpolate; - m_UseRigidBodyForMotion = useRigidbodyForMotion; - } - - protected override void OnCreatePlayerPrefab() - { - var networkTransform = m_PlayerPrefab.AddComponent(); - networkTransform.Interpolate = m_NetworkTransformInterpolate; - var rigidbody = m_PlayerPrefab.AddComponent(); - rigidbody.interpolation = m_RigidbodyInterpolation; - var networkRigidbody = m_PlayerPrefab.AddComponent(); - networkRigidbody.UseRigidBodyForMotion = m_UseRigidBodyForMotion; - } - - /// - /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. - /// - /// - [UnityTest] - public IEnumerator TestRigidbodyKinematicEnableDisable() - { - // This is the *SERVER VERSION* of the *CLIENT PLAYER* - var serverClientPlayerInstance = m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].PlayerObject; - - // This is the *CLIENT VERSION* of the *CLIENT PLAYER* - var clientPlayerInstance = m_ClientNetworkManagers[0].LocalClient.PlayerObject; - - Assert.IsNotNull(serverClientPlayerInstance, $"{nameof(serverClientPlayerInstance)} is null!"); - Assert.IsNotNull(clientPlayerInstance, $"{nameof(clientPlayerInstance)} is null!"); - - var serverClientInstanceRigidBody = serverClientPlayerInstance.GetComponent(); - var clientRigidBody = clientPlayerInstance.GetComponent(); - - if (m_UseRigidBodyForMotion) - { - var interpolateCompareNonAuthoritative = m_NetworkTransformInterpolate ? RigidbodyInterpolation.Interpolate : m_RigidbodyInterpolation; - - // Server authoritative NT should yield non-kinematic mode for the server-side player instance - Assert.False(serverClientInstanceRigidBody.isKinematic, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is kinematic!"); - - // The authoritative instance can be None, Interpolate, or Extrapolate for the Rigidbody interpolation settings. - Assert.AreEqual(m_RigidbodyInterpolation, serverClientInstanceRigidBody.interpolation, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + - $"player's {nameof(Rigidbody)}'s interpolation is {serverClientInstanceRigidBody.interpolation} and not {m_RigidbodyInterpolation}!"); - - // Server authoritative NT should yield kinematic mode for the client-side player instance - Assert.True(clientRigidBody.isKinematic, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic!"); - - // When using Rigidbody motion, authoritative and non-authoritative Rigidbody interpolation settings should be preserved (except when extrapolation is used - Assert.AreEqual(interpolateCompareNonAuthoritative, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + - $"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {interpolateCompareNonAuthoritative}!"); - } - else + private List<(RigidbodyInterpolation interpolationType, bool enableInterpolation, bool useRigidbodyForMotion)> m_TestConfigurations = + new List<(RigidbodyInterpolation interpolationType, bool enableInterpolation, bool useRigidbodyForMotion)>() { - // server rigidbody has authority and should not be kinematic - Assert.False(serverClientInstanceRigidBody.isKinematic, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is kinematic!"); - Assert.AreEqual(RigidbodyInterpolation.Interpolate, serverClientInstanceRigidBody.interpolation, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + - $"player's {nameof(Rigidbody)}'s interpolation is {serverClientInstanceRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.Interpolate)}!"); - - // Server authoritative NT should yield kinematic mode for the client-side player instance - Assert.True(clientRigidBody.isKinematic, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic!"); - - // client rigidbody has no authority with NT interpolation disabled should allow Rigidbody interpolation - if (!m_NetworkTransformInterpolate) - { - Assert.AreEqual(RigidbodyInterpolation.Interpolate, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + - $"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.Interpolate)}!"); - } - else - { - Assert.AreEqual(RigidbodyInterpolation.None, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + - $"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.None)}!"); - } - } - - // despawn the server player (but keep it around on the server) - serverClientPlayerInstance.Despawn(false); - - yield return WaitForConditionOrTimeOut(() => !serverClientPlayerInstance.IsSpawned && !clientPlayerInstance.IsSpawned); - AssertOnTimeout("Timed out waiting for client player to despawn on both server and client!"); - - // When despawned, we should always be kinematic (i.e. don't apply physics when despawned) - Assert.True(serverClientInstanceRigidBody.isKinematic, $"[Server-Side][Despawned] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic when despawned!"); - Assert.IsTrue(clientPlayerInstance == null, $"[Client-Side] Player {nameof(NetworkObject)} is not null!"); - } - } - - internal class ContactEventTransformHelperWithInfo : ContactEventTransformHelper, IContactEventHandlerWithInfo - { - public ContactEventHandlerInfo GetContactEventHandlerInfo() - { - var contactEventHandlerInfo = new ContactEventHandlerInfo() - { - HasContactEventPriority = IsOwner, - ProvideNonRigidBodyContactEvents = m_EnableNonRigidbodyContacts.Value, + (RigidbodyInterpolation.Interpolate, true, true), // This should be allowed under all condistions when using Rigidbody motion + (RigidbodyInterpolation.Extrapolate, true, true), // This should not allow extrapolation on non-auth instances when using Rigidbody motion & NT interpolation + (RigidbodyInterpolation.Extrapolate, false, true), // This should allow extrapolation on non-auth instances when using Rigidbody & NT has no interpolation + (RigidbodyInterpolation.Interpolate, true, false), // This should not allow kinematic instances to have Rigidbody interpolation enabled + (RigidbodyInterpolation.Interpolate, false, false) // Testing that rigidbody interpolation remains the same if NT interpolate is disabled }; - return contactEventHandlerInfo; - } - - protected override void OnRegisterForContactEvents(bool isRegistering) - { - RigidbodyContactEventManager.Instance.RegisterHandler(this, isRegistering); - } - } + /// + /// The current test configuration applied to the current test running. + /// + private (RigidbodyInterpolation interpolationType, bool enableInterpolation, bool useRigidbodyForMotion) m_CurrentConfiguration; - internal class ContactEventTransformHelper : NetworkTransform, IContactEventHandler - { - public static Vector3 SessionOwnerSpawnPoint; - public static Vector3 ClientSpawnPoint; - public static bool VerboseDebug; - public enum HelperStates + public NetworkRigidbodyTest(HostOrServer hostOrServer) : base(hostOrServer) { - None, - MoveForward, } - private HelperStates m_HelperState; - - public void SetHelperState(HelperStates state) - { - m_HelperState = state; - if (!m_NetworkRigidbody.IsKinematic()) - { - m_NetworkRigidbody.Rigidbody.angularVelocity = Vector3.zero; - m_NetworkRigidbody.Rigidbody.linearVelocity = Vector3.zero; - } - m_NetworkRigidbody.Rigidbody.isKinematic = m_HelperState == HelperStates.None; - if (!m_NetworkRigidbody.IsKinematic()) - { - m_NetworkRigidbody.Rigidbody.angularVelocity = Vector3.zero; - m_NetworkRigidbody.Rigidbody.linearVelocity = Vector3.zero; - } + /// + /// Base prefab for and + /// + private GameObject m_RigidbodyPrefab; + private NetworkTransform m_3DNetworkTransform; + private Rigidbody m_PrefabRigidbody; + private NetworkRigidbody m_PrefabNetworkRigidbody; + private NetworkObject m_3DAuthorityInstance; - } + /// + /// Base prefab for and + /// + private GameObject m_Rigidbody2DPrefab; + private NetworkTransform m_2DNetworkTransform; + private Rigidbody2D m_PrefabRigidbody2D; + private NetworkRigidbody2D m_PrefabNetworkRigidbody2D; + private NetworkObject m_2DAuthorityInstance; - protected struct ContactEventInfo + protected override void OnServerAndClientsCreated() { - public ulong EventId; - public Vector3 AveragedCollisionNormal; - public Rigidbody CollidingBody; - public Vector3 ContactPoint; - } + m_RigidbodyPrefab = CreateNetworkObjectPrefab("RBTest"); + m_3DNetworkTransform = m_RigidbodyPrefab.AddComponent(); + m_PrefabRigidbody = m_RigidbodyPrefab.AddComponent(); + m_PrefabNetworkRigidbody = m_RigidbodyPrefab.AddComponent(); - protected List m_ContactEvents = new List(); + m_Rigidbody2DPrefab = CreateNetworkObjectPrefab("RB2DTest"); + m_2DNetworkTransform = m_Rigidbody2DPrefab.AddComponent(); + m_PrefabRigidbody2D = m_Rigidbody2DPrefab.AddComponent(); + m_PrefabNetworkRigidbody2D = m_Rigidbody2DPrefab.AddComponent(); - protected NetworkVariable m_EnableNonRigidbodyContacts = new NetworkVariable(false, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner); - - protected NetworkRigidbody m_NetworkRigidbody; - public ContactEventTransformHelper Target; - - public bool HasContactEvents() - { - return m_ContactEvents.Count > 0; + base.OnServerAndClientsCreated(); } - public Rigidbody GetRigidbody() + private string m_ConfigHeader; + private void ApplyCurrentTestConfiguration() { - return m_NetworkRigidbody.Rigidbody; - } - - public bool HadContactWith(ContactEventTransformHelper otherObject) - { - if (otherObject == null) - { - return false; - } - foreach (var contactEvent in m_ContactEvents) - { - if (contactEvent.CollidingBody == otherObject.m_NetworkRigidbody.Rigidbody) - { - return true; - } - } - return false; - } + // Configure both 3D and 2D versions based on the current test configuration + m_3DNetworkTransform.Interpolate = m_CurrentConfiguration.enableInterpolation; + m_PrefabRigidbody.interpolation = m_CurrentConfiguration.interpolationType; + m_PrefabNetworkRigidbody.UseRigidBodyForMotion = m_CurrentConfiguration.useRigidbodyForMotion; + m_2DNetworkTransform.Interpolate = m_CurrentConfiguration.enableInterpolation; + m_PrefabRigidbody2D.interpolation = m_CurrentConfiguration.interpolationType == RigidbodyInterpolation.Interpolate ? RigidbodyInterpolation2D.Interpolate : RigidbodyInterpolation2D.Extrapolate; + m_PrefabNetworkRigidbody2D.UseRigidBodyForMotion = m_CurrentConfiguration.useRigidbodyForMotion; - protected virtual void CheckToStopMoving() - { - SetHelperState(HadContactWith(Target) ? HelperStates.None : HelperStates.MoveForward); + // Build a header used in assert messages + m_ConfigHeader = $"[{m_CurrentConfiguration.interpolationType}][Interpolate: {m_CurrentConfiguration.enableInterpolation}][RB-Motion: {m_CurrentConfiguration.useRigidbodyForMotion}]"; } - public void ContactEvent(ulong eventId, Vector3 averagedCollisionNormal, Rigidbody collidingBody, Vector3 contactPoint, bool hasCollisionStay = false, Vector3 averagedCollisionStayNormal = default) + /// + /// Iterates through the to validate various + /// Rigidbody interpolation settings and kinematic states for authority and non-authority + /// instances. + /// + [UnityTest] + public IEnumerator TestRigidbodyKinematicEnableDisable() { - if (Target == null) + foreach (var configuration in m_TestConfigurations) { - return; - } + m_CurrentConfiguration = configuration; + ApplyCurrentTestConfiguration(); - if (collidingBody != null) - { - Log($">>>>>>> contact event with {collidingBody.name}!"); - } - else - { - Log($">>>>>>> contact event with non-rigidbody!"); - } + // Host, Server, DAHost/Session-owner are spawn authority + yield return RunTestConfiguration(); - m_ContactEvents.Add(new ContactEventInfo() - { - EventId = eventId, - AveragedCollisionNormal = averagedCollisionNormal, - CollidingBody = collidingBody, - ContactPoint = contactPoint, - }); - CheckToStopMoving(); - } - - private void SetInitialPositionClientServer() - { - if (IsServer) - { - if (!NetworkManager.DistributedAuthorityMode && !IsLocalPlayer) - { - transform.position = ClientSpawnPoint; - m_NetworkRigidbody.Rigidbody.position = ClientSpawnPoint; - } - else + // When using distributed authority, swap the session owner with + // the non-session owner client as being the spawn authority. + if (m_DistributedAuthority) { - transform.position = SessionOwnerSpawnPoint; - m_NetworkRigidbody.Rigidbody.position = SessionOwnerSpawnPoint; + yield return RunTestConfiguration(true); } } - else - { - transform.position = ClientSpawnPoint; - m_NetworkRigidbody.Rigidbody.position = ClientSpawnPoint; - } } - private void SetInitialPositionDistributedAuthority() - { - if (HasAuthority) + /// + /// Validates the current applied test configuration. + /// + private IEnumerator RunTestConfiguration(bool swapAuthority = false) + { + // The authority is the "spawn authority". + // Distributed authority runs this a second time with a non-session owner client being the + // spawn authority to validate that scenario works correctly. + var authority = !swapAuthority ? GetAuthorityNetworkManager() : GetNonAuthorityNetworkManager(); + var nonAuthority = !swapAuthority ? GetNonAuthorityNetworkManager() : GetAuthorityNetworkManager(); + + // Spawn instances of both the 3D and 2D prefabs configured for the current test. + m_3DAuthorityInstance = SpawnObject(m_RigidbodyPrefab, authority).GetComponent(); + yield return WaitForSpawnedOnAllOrTimeOut(m_3DAuthorityInstance); + AssertOnTimeout($"Failed to spawn {m_3DAuthorityInstance.name} on all clients!"); + + m_2DAuthorityInstance = SpawnObject(m_Rigidbody2DPrefab, authority).GetComponent(); + yield return WaitForSpawnedOnAllOrTimeOut(m_2DAuthorityInstance); + AssertOnTimeout($"Failed to spawn {m_2DAuthorityInstance.name} on all clients!"); + + // Test 3D Rigidbody + #region 3D Rigidbody validation + var authorityRigidbody = m_3DAuthorityInstance.GetComponent(); + var nonAuthorityInstance = nonAuthority.SpawnManager.SpawnedObjects[m_3DAuthorityInstance.NetworkObjectId]; + var nonAuthorityRigidbody = nonAuthorityInstance.GetComponent(); + var authorityHeader = $"{m_ConfigHeader}[Authority] Client-{authority.LocalClientId}'s instance of {m_3DAuthorityInstance.name}"; + // The authority instance should always be non-kinematic + Assert.False(authorityRigidbody.isKinematic, $"{authorityHeader} is kinematic!"); + + var nonAuthorityHeader = $"{m_ConfigHeader}[Non-Authority] Client-{nonAuthority.LocalClientId}'s instance of {nonAuthorityInstance.name}"; + // Non-authority instances should always be kinematic + Assert.True(nonAuthorityRigidbody.isKinematic, $"{nonAuthorityHeader} is not kinematic!"); + var interpolateCompareNonAuthoritative = RigidbodyInterpolation.None; + + if (m_CurrentConfiguration.useRigidbodyForMotion) { - if (IsSessionOwner) - { - transform.position = SessionOwnerSpawnPoint; - m_NetworkRigidbody.Rigidbody.position = SessionOwnerSpawnPoint; - } - else - { - transform.position = ClientSpawnPoint; - m_NetworkRigidbody.Rigidbody.position = ClientSpawnPoint; - } - } - } - - public override void OnNetworkSpawn() - { - m_NetworkRigidbody = GetComponent(); + // The authoritative instance can be None, Interpolate, or Extrapolate for the Rigidbody interpolation settings. + Assert.AreEqual(m_CurrentConfiguration.interpolationType, authorityRigidbody.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody.interpolation} " + + $"and not {m_CurrentConfiguration.interpolationType}!"); - m_NetworkRigidbody.Rigidbody.maxLinearVelocity = 15; - m_NetworkRigidbody.Rigidbody.maxAngularVelocity = 10; + // When using Rigidbody motion, authoritative and non-authoritative Rigidbody interpolation settings should be preserved (except when extrapolation is used + interpolateCompareNonAuthoritative = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation.Interpolate : m_CurrentConfiguration.interpolationType; - if (NetworkManager.DistributedAuthorityMode) - { - SetInitialPositionDistributedAuthority(); } else { - SetInitialPositionClientServer(); - } - if (IsLocalPlayer) - { - RegisterForContactEvents(true); - } - else - { - m_NetworkRigidbody.Rigidbody.detectCollisions = false; - } - base.OnNetworkSpawn(); - } - - protected virtual void OnRegisterForContactEvents(bool isRegistering) - { - RigidbodyContactEventManager.Instance.RegisterHandler(this, isRegistering); - } - - public void RegisterForContactEvents(bool isRegistering) - { - OnRegisterForContactEvents(isRegistering); - } - - private void FixedUpdate() - { - if (!IsSpawned || !IsOwner || m_HelperState != HelperStates.MoveForward) - { - return; - } - var distance = Vector3.Distance(Target.transform.position, transform.position); - var moveAmount = Mathf.Max(1.2f, distance); - // Head towards our target - var dir = (Target.transform.position - transform.position).normalized; - var deltaMove = dir * moveAmount * Time.fixedDeltaTime; - m_NetworkRigidbody.Rigidbody.MovePosition(m_NetworkRigidbody.Rigidbody.position + deltaMove); - + Assert.AreEqual(RigidbodyInterpolation.Interpolate, authorityRigidbody.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody.interpolation} " + + $"and not {RigidbodyInterpolation.Interpolate}!"); - Log($" Loc: {transform.position} | Dest: {Target.transform.position} | Dist: {distance} | MoveDelta: {deltaMove}"); - } - - protected void Log(string msg) - { - if (VerboseDebug) - { - Debug.Log($"Client-{OwnerClientId} {msg}"); + // client rigidbody has no authority with NT interpolation disabled should allow Rigidbody interpolation + interpolateCompareNonAuthoritative = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation.None : RigidbodyInterpolation.Interpolate; } - } - } - - [TestFixture(HostOrServer.Host, ContactEventTypes.Default)] - [TestFixture(HostOrServer.DAHost, ContactEventTypes.Default)] - [TestFixture(HostOrServer.Host, ContactEventTypes.WithInfo)] - [TestFixture(HostOrServer.DAHost, ContactEventTypes.WithInfo)] - internal class RigidbodyContactEventManagerTests : IntegrationTestWithApproximation - { - protected override int NumberOfClients => 1; - - private GameObject m_RigidbodyContactEventManager; - public enum ContactEventTypes - { - Default, - WithInfo - } + Assert.AreEqual(interpolateCompareNonAuthoritative, nonAuthorityRigidbody.interpolation, $"{nonAuthorityHeader} interpolation is {nonAuthorityRigidbody.interpolation} " + + $"and not {interpolateCompareNonAuthoritative}!"); + #endregion - private ContactEventTypes m_ContactEventType; - private StringBuilder m_ErrorLogger = new StringBuilder(); + // Test 2D Rigidbody + #region 2D Rigidbody validation + var authorityRigidbody2D = m_2DAuthorityInstance.GetComponent(); + var nonAuthorityInstance2D = nonAuthority.SpawnManager.SpawnedObjects[m_2DAuthorityInstance.NetworkObjectId]; + var nonAuthorityRigidbody2D = nonAuthorityInstance2D.GetComponent(); - public RigidbodyContactEventManagerTests(HostOrServer hostOrServer, ContactEventTypes contactEventType) : base(hostOrServer) - { - m_ContactEventType = contactEventType; - } + authorityHeader = $"{m_ConfigHeader}[Authority] Client-{authority.LocalClientId}'s instance of {m_2DAuthorityInstance.name}"; + // The authority instance should always be non-kinematic + Assert.False(authorityRigidbody2D.bodyType == RigidbodyType2D.Kinematic, $"{authorityHeader} is kinematic!"); - protected override void OnCreatePlayerPrefab() - { - ContactEventTransformHelper.SessionOwnerSpawnPoint = GetRandomVector3(-4, -3); - ContactEventTransformHelper.ClientSpawnPoint = GetRandomVector3(3, 4); - if (m_ContactEventType == ContactEventTypes.Default) + nonAuthorityHeader = $"{m_ConfigHeader}[Non-Authority] Client-{nonAuthority.LocalClientId}'s instance of {nonAuthorityInstance.name}"; + // Non-authority instances should always be kinematic + Assert.True(nonAuthorityRigidbody2D.bodyType == RigidbodyType2D.Kinematic, $"{nonAuthorityHeader} is not kinematic!"); + var interpolateCompareNonAuthoritative2D = RigidbodyInterpolation2D.None; + var configInterpolation2D = m_CurrentConfiguration.interpolationType == RigidbodyInterpolation.Interpolate ? RigidbodyInterpolation2D.Interpolate : RigidbodyInterpolation2D.Extrapolate; + if (m_CurrentConfiguration.useRigidbodyForMotion) { - var helper = m_PlayerPrefab.AddComponent(); - helper.AuthorityMode = NetworkTransform.AuthorityModes.Owner; + // The authoritative instance can be None, Interpolate, or Extrapolate for the Rigidbody interpolation settings. + Assert.AreEqual(configInterpolation2D, authorityRigidbody2D.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody2D.interpolation} " + + $"and not {m_CurrentConfiguration.interpolationType}!"); + + // When using Rigidbody motion, authoritative and non-authoritative Rigidbody interpolation settings should be preserved (except when extrapolation is used + interpolateCompareNonAuthoritative2D = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation2D.Interpolate : configInterpolation2D; } else { - var helperWithInfo = m_PlayerPrefab.AddComponent(); - helperWithInfo.AuthorityMode = NetworkTransform.AuthorityModes.Owner; - } - - var rigidbody = m_PlayerPrefab.AddComponent(); - rigidbody.useGravity = false; - rigidbody.isKinematic = true; - rigidbody.mass = 5.0f; - rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous; - var sphereCollider = m_PlayerPrefab.AddComponent(); - sphereCollider.radius = 0.5f; - sphereCollider.providesContacts = true; - - var networkRigidbody = m_PlayerPrefab.AddComponent(); - networkRigidbody.UseRigidBodyForMotion = true; - networkRigidbody.AutoUpdateKinematicState = false; - - m_RigidbodyContactEventManager = new GameObject(); - m_RigidbodyContactEventManager.AddComponent(); - } - + Assert.AreEqual(RigidbodyInterpolation2D.Interpolate, authorityRigidbody2D.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody2D.interpolation} " + + $"and not {RigidbodyInterpolation2D.Interpolate}!"); - - private bool PlayersSpawnedInRightLocation() - { - var authority = GetAuthorityNetworkManager(); - var nonAuthority = GetNonAuthorityNetworkManager(); - - var position = authority.LocalClient.PlayerObject.transform.position; - if (!Approximately(ContactEventTransformHelper.SessionOwnerSpawnPoint, position)) - { - m_ErrorLogger.AppendLine($"Client-{authority.LocalClientId} player position {position} does not match the assigned player position {ContactEventTransformHelper.SessionOwnerSpawnPoint}!"); - return false; - } - - position = nonAuthority.LocalClient.PlayerObject.transform.position; - if (!Approximately(ContactEventTransformHelper.ClientSpawnPoint, position)) - { - m_ErrorLogger.AppendLine($"Client-{nonAuthority.LocalClientId} player position {position} does not match the assigned player position {ContactEventTransformHelper.ClientSpawnPoint}!"); - return false; - } - var playerObject = (NetworkObject)null; - if (!authority.SpawnManager.SpawnedObjects.ContainsKey(nonAuthority.LocalClient.PlayerObject.NetworkObjectId)) - { - m_ErrorLogger.AppendLine($"Client-{authority.LocalClientId} cannot find a local spawned instance of Client-{nonAuthority.LocalClientId}'s player object!"); - return false; + // client rigidbody has no authority with NT interpolation disabled should allow Rigidbody interpolation + interpolateCompareNonAuthoritative2D = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation2D.None : RigidbodyInterpolation2D.Interpolate; } - playerObject = authority.SpawnManager.SpawnedObjects[nonAuthority.LocalClient.PlayerObject.NetworkObjectId]; - position = playerObject.transform.position; - if (!Approximately(ContactEventTransformHelper.ClientSpawnPoint, position)) - { - m_ErrorLogger.AppendLine($"Client-{authority.LocalClientId} player position {position} for Client-{playerObject.OwnerClientId} does not match the assigned player position {ContactEventTransformHelper.ClientSpawnPoint}!"); - return false; - } + Assert.AreEqual(interpolateCompareNonAuthoritative2D, nonAuthorityRigidbody2D.interpolation, $"{nonAuthorityHeader} interpolation is {nonAuthorityRigidbody2D.interpolation} " + + $"and not {interpolateCompareNonAuthoritative}!"); + #endregion - if (!nonAuthority.SpawnManager.SpawnedObjects.ContainsKey(authority.LocalClient.PlayerObject.NetworkObjectId)) - { - m_ErrorLogger.AppendLine($"Client-{nonAuthority.LocalClientId} cannot find a local spawned instance of Client-{authority.LocalClientId}'s player object!"); - return false; - } - playerObject = nonAuthority.SpawnManager.SpawnedObjects[authority.LocalClient.PlayerObject.NetworkObjectId]; - position = playerObject.transform.position; - if (!Approximately(ContactEventTransformHelper.SessionOwnerSpawnPoint, playerObject.transform.position)) - { - m_ErrorLogger.AppendLine($"Client-{nonAuthority.LocalClientId} player position {position} for Client-{playerObject.OwnerClientId} does not match the assigned player position {ContactEventTransformHelper.SessionOwnerSpawnPoint}!"); - return false; - } - return true; + var spawnedInstances = new List() { m_3DAuthorityInstance, m_2DAuthorityInstance }; + m_3DAuthorityInstance.Despawn(); + m_2DAuthorityInstance.Despawn(); + yield return WaitForDespawnedOnAllOrTimeOut(spawnedInstances); + AssertOnTimeout($"Failed to de-spawn instances on all clients!"); + m_3DAuthorityInstance = null; + m_2DAuthorityInstance = null; } - - [UnityTest] - public IEnumerator TestContactEvents() + /// + /// Handle clean up in case of a failed test + /// + protected override IEnumerator OnTearDown() { - ContactEventTransformHelper.VerboseDebug = m_EnableVerboseDebug; - - m_PlayerPrefab.SetActive(false); - m_ErrorLogger.Clear(); - // Validate all instances are spawned in the right location - yield return WaitForConditionOrTimeOut(PlayersSpawnedInRightLocation); - AssertOnTimeout($"Timed out waiting for all player instances to spawn in the corect location:\n {m_ErrorLogger}"); - m_ErrorLogger.Clear(); - - var authority = GetAuthorityNetworkManager(); - var nonAuthority = GetNonAuthorityNetworkManager(); + // If either of these are not null then we most likely failed and didn't cleanup. - var sessionOwnerPlayer = m_ContactEventType == ContactEventTypes.Default ? authority.LocalClient.PlayerObject.GetComponent() : - authority.LocalClient.PlayerObject.GetComponent(); - var clientPlayer = m_ContactEventType == ContactEventTypes.Default ? nonAuthority.LocalClient.PlayerObject.GetComponent() : - nonAuthority.LocalClient.PlayerObject.GetComponent(); - - // Get both players to point towards each other - sessionOwnerPlayer.Target = clientPlayer; - clientPlayer.Target = sessionOwnerPlayer; - - sessionOwnerPlayer.SetHelperState(ContactEventTransformHelper.HelperStates.MoveForward); - clientPlayer.SetHelperState(ContactEventTransformHelper.HelperStates.MoveForward); - - - yield return WaitForConditionOrTimeOut(() => sessionOwnerPlayer.HadContactWith(clientPlayer) || clientPlayer.HadContactWith(sessionOwnerPlayer)); - AssertOnTimeout("Timed out waiting for a player to collide with another player!"); - - clientPlayer.RegisterForContactEvents(false); - sessionOwnerPlayer.RegisterForContactEvents(false); - var otherPlayer = m_ContactEventType == ContactEventTypes.Default ? authority.SpawnManager.SpawnedObjects[clientPlayer.NetworkObjectId].GetComponent() : - authority.SpawnManager.SpawnedObjects[clientPlayer.NetworkObjectId].GetComponent(); - otherPlayer.RegisterForContactEvents(false); - otherPlayer = m_ContactEventType == ContactEventTypes.Default ? nonAuthority.SpawnManager.SpawnedObjects[sessionOwnerPlayer.NetworkObjectId].GetComponent() : - nonAuthority.SpawnManager.SpawnedObjects[sessionOwnerPlayer.NetworkObjectId].GetComponent(); - otherPlayer.RegisterForContactEvents(false); - - Object.Destroy(m_RigidbodyContactEventManager); - m_RigidbodyContactEventManager = null; - } + // Clean-up m_3DAuthorityInstance + if (m_3DAuthorityInstance) + { + Object.Destroy(m_3DAuthorityInstance); + m_3DAuthorityInstance = null; + } - protected override IEnumerator OnTearDown() - { - // In case of a test failure - if (m_RigidbodyContactEventManager) + // Clean-up m_2DAuthorityInstance + if (m_2DAuthorityInstance) { - Object.Destroy(m_RigidbodyContactEventManager); - m_RigidbodyContactEventManager = null; + Object.Destroy(m_2DAuthorityInstance); + m_2DAuthorityInstance = null; } return base.OnTearDown(); } } } -#endif // COM_UNITY_MODULES_PHYSICS +#endif // COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/RigidbodyContactEventManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/RigidbodyContactEventManagerTests.cs new file mode 100644 index 0000000000..51aaa5ba56 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/RigidbodyContactEventManagerTests.cs @@ -0,0 +1,403 @@ +#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D +using System.Collections; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Unity.Netcode.Components; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + + +namespace Unity.Netcode.RuntimeTests +{ + + + [TestFixture(HostOrServer.Host, ContactEventTypes.Default)] + [TestFixture(HostOrServer.DAHost, ContactEventTypes.Default)] + [TestFixture(HostOrServer.Host, ContactEventTypes.WithInfo)] + [TestFixture(HostOrServer.DAHost, ContactEventTypes.WithInfo)] + internal class RigidbodyContactEventManagerTests : IntegrationTestWithApproximation + { + protected override int NumberOfClients => 1; + + private GameObject m_RigidbodyContactEventManager; + + public enum ContactEventTypes + { + Default, + WithInfo + } + + private ContactEventTypes m_ContactEventType; + private StringBuilder m_ErrorLogger = new StringBuilder(); + + public RigidbodyContactEventManagerTests(HostOrServer hostOrServer, ContactEventTypes contactEventType) : base(hostOrServer) + { + m_ContactEventType = contactEventType; + } + + protected override void OnCreatePlayerPrefab() + { + ContactEventTransformHelper.SessionOwnerSpawnPoint = GetRandomVector3(-4, -3); + ContactEventTransformHelper.ClientSpawnPoint = GetRandomVector3(3, 4); + if (m_ContactEventType == ContactEventTypes.Default) + { + var helper = m_PlayerPrefab.AddComponent(); + helper.AuthorityMode = NetworkTransform.AuthorityModes.Owner; + } + else + { + var helperWithInfo = m_PlayerPrefab.AddComponent(); + helperWithInfo.AuthorityMode = NetworkTransform.AuthorityModes.Owner; + } + + var rigidbody = m_PlayerPrefab.AddComponent(); + rigidbody.useGravity = false; + rigidbody.isKinematic = true; + rigidbody.mass = 5.0f; + rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous; + var sphereCollider = m_PlayerPrefab.AddComponent(); + sphereCollider.radius = 0.5f; + sphereCollider.providesContacts = true; + + var networkRigidbody = m_PlayerPrefab.AddComponent(); + networkRigidbody.UseRigidBodyForMotion = true; + networkRigidbody.AutoUpdateKinematicState = false; + + m_RigidbodyContactEventManager = new GameObject(); + m_RigidbodyContactEventManager.AddComponent(); + } + + + + private bool PlayersSpawnedInRightLocation() + { + var authority = GetAuthorityNetworkManager(); + var nonAuthority = GetNonAuthorityNetworkManager(); + + var position = authority.LocalClient.PlayerObject.transform.position; + if (!Approximately(ContactEventTransformHelper.SessionOwnerSpawnPoint, position)) + { + m_ErrorLogger.AppendLine($"Client-{authority.LocalClientId} player position {position} does not match the assigned player position {ContactEventTransformHelper.SessionOwnerSpawnPoint}!"); + return false; + } + + position = nonAuthority.LocalClient.PlayerObject.transform.position; + if (!Approximately(ContactEventTransformHelper.ClientSpawnPoint, position)) + { + m_ErrorLogger.AppendLine($"Client-{nonAuthority.LocalClientId} player position {position} does not match the assigned player position {ContactEventTransformHelper.ClientSpawnPoint}!"); + return false; + } + var playerObject = (NetworkObject)null; + if (!authority.SpawnManager.SpawnedObjects.ContainsKey(nonAuthority.LocalClient.PlayerObject.NetworkObjectId)) + { + m_ErrorLogger.AppendLine($"Client-{authority.LocalClientId} cannot find a local spawned instance of Client-{nonAuthority.LocalClientId}'s player object!"); + return false; + } + playerObject = authority.SpawnManager.SpawnedObjects[nonAuthority.LocalClient.PlayerObject.NetworkObjectId]; + position = playerObject.transform.position; + + if (!Approximately(ContactEventTransformHelper.ClientSpawnPoint, position)) + { + m_ErrorLogger.AppendLine($"Client-{authority.LocalClientId} player position {position} for Client-{playerObject.OwnerClientId} does not match the assigned player position {ContactEventTransformHelper.ClientSpawnPoint}!"); + return false; + } + + if (!nonAuthority.SpawnManager.SpawnedObjects.ContainsKey(authority.LocalClient.PlayerObject.NetworkObjectId)) + { + m_ErrorLogger.AppendLine($"Client-{nonAuthority.LocalClientId} cannot find a local spawned instance of Client-{authority.LocalClientId}'s player object!"); + return false; + } + playerObject = nonAuthority.SpawnManager.SpawnedObjects[authority.LocalClient.PlayerObject.NetworkObjectId]; + position = playerObject.transform.position; + if (!Approximately(ContactEventTransformHelper.SessionOwnerSpawnPoint, playerObject.transform.position)) + { + m_ErrorLogger.AppendLine($"Client-{nonAuthority.LocalClientId} player position {position} for Client-{playerObject.OwnerClientId} does not match the assigned player position {ContactEventTransformHelper.SessionOwnerSpawnPoint}!"); + return false; + } + return true; + } + + + [UnityTest] + public IEnumerator TestContactEvents() + { + ContactEventTransformHelper.VerboseDebug = m_EnableVerboseDebug; + + m_PlayerPrefab.SetActive(false); + m_ErrorLogger.Clear(); + // Validate all instances are spawned in the right location + yield return WaitForConditionOrTimeOut(PlayersSpawnedInRightLocation); + AssertOnTimeout($"Timed out waiting for all player instances to spawn in the corect location:\n {m_ErrorLogger}"); + m_ErrorLogger.Clear(); + + var authority = GetAuthorityNetworkManager(); + var nonAuthority = GetNonAuthorityNetworkManager(); + + var sessionOwnerPlayer = m_ContactEventType == ContactEventTypes.Default ? authority.LocalClient.PlayerObject.GetComponent() : + authority.LocalClient.PlayerObject.GetComponent(); + var clientPlayer = m_ContactEventType == ContactEventTypes.Default ? nonAuthority.LocalClient.PlayerObject.GetComponent() : + nonAuthority.LocalClient.PlayerObject.GetComponent(); + + // Get both players to point towards each other + sessionOwnerPlayer.Target = clientPlayer; + clientPlayer.Target = sessionOwnerPlayer; + + sessionOwnerPlayer.SetHelperState(ContactEventTransformHelper.HelperStates.MoveForward); + clientPlayer.SetHelperState(ContactEventTransformHelper.HelperStates.MoveForward); + + + yield return WaitForConditionOrTimeOut(() => sessionOwnerPlayer.HadContactWith(clientPlayer) || clientPlayer.HadContactWith(sessionOwnerPlayer)); + AssertOnTimeout("Timed out waiting for a player to collide with another player!"); + + clientPlayer.RegisterForContactEvents(false); + sessionOwnerPlayer.RegisterForContactEvents(false); + var otherPlayer = m_ContactEventType == ContactEventTypes.Default ? authority.SpawnManager.SpawnedObjects[clientPlayer.NetworkObjectId].GetComponent() : + authority.SpawnManager.SpawnedObjects[clientPlayer.NetworkObjectId].GetComponent(); + otherPlayer.RegisterForContactEvents(false); + otherPlayer = m_ContactEventType == ContactEventTypes.Default ? nonAuthority.SpawnManager.SpawnedObjects[sessionOwnerPlayer.NetworkObjectId].GetComponent() : + nonAuthority.SpawnManager.SpawnedObjects[sessionOwnerPlayer.NetworkObjectId].GetComponent(); + otherPlayer.RegisterForContactEvents(false); + + Object.Destroy(m_RigidbodyContactEventManager); + m_RigidbodyContactEventManager = null; + } + + protected override IEnumerator OnTearDown() + { + // In case of a test failure + if (m_RigidbodyContactEventManager) + { + Object.Destroy(m_RigidbodyContactEventManager); + m_RigidbodyContactEventManager = null; + } + + return base.OnTearDown(); + } + } + + #region Test helper classes + internal class ContactEventTransformHelperWithInfo : ContactEventTransformHelper, IContactEventHandlerWithInfo + { + public ContactEventHandlerInfo GetContactEventHandlerInfo() + { + var contactEventHandlerInfo = new ContactEventHandlerInfo() + { + HasContactEventPriority = IsOwner, + ProvideNonRigidBodyContactEvents = m_EnableNonRigidbodyContacts.Value, + }; + return contactEventHandlerInfo; + } + + protected override void OnRegisterForContactEvents(bool isRegistering) + { + RigidbodyContactEventManager.Instance.RegisterHandler(this, isRegistering); + } + } + + internal class ContactEventTransformHelper : NetworkTransform, IContactEventHandler + { + public static Vector3 SessionOwnerSpawnPoint; + public static Vector3 ClientSpawnPoint; + public static bool VerboseDebug; + public enum HelperStates + { + None, + MoveForward, + } + + private HelperStates m_HelperState; + + public void SetHelperState(HelperStates state) + { + m_HelperState = state; + if (!m_NetworkRigidbody.IsKinematic()) + { + m_NetworkRigidbody.Rigidbody.angularVelocity = Vector3.zero; + m_NetworkRigidbody.Rigidbody.linearVelocity = Vector3.zero; + } + m_NetworkRigidbody.Rigidbody.isKinematic = m_HelperState == HelperStates.None; + if (!m_NetworkRigidbody.IsKinematic()) + { + m_NetworkRigidbody.Rigidbody.angularVelocity = Vector3.zero; + m_NetworkRigidbody.Rigidbody.linearVelocity = Vector3.zero; + } + + } + + protected struct ContactEventInfo + { + public ulong EventId; + public Vector3 AveragedCollisionNormal; + public Rigidbody CollidingBody; + public Vector3 ContactPoint; + } + + protected List m_ContactEvents = new List(); + + protected NetworkVariable m_EnableNonRigidbodyContacts = new NetworkVariable(false, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner); + + protected NetworkRigidbody m_NetworkRigidbody; + public ContactEventTransformHelper Target; + + public bool HasContactEvents() + { + return m_ContactEvents.Count > 0; + } + + public Rigidbody GetRigidbody() + { + return m_NetworkRigidbody.Rigidbody; + } + + public bool HadContactWith(ContactEventTransformHelper otherObject) + { + if (otherObject == null) + { + return false; + } + foreach (var contactEvent in m_ContactEvents) + { + if (contactEvent.CollidingBody == otherObject.m_NetworkRigidbody.Rigidbody) + { + return true; + } + } + return false; + } + + protected virtual void CheckToStopMoving() + { + SetHelperState(HadContactWith(Target) ? HelperStates.None : HelperStates.MoveForward); + } + + public void ContactEvent(ulong eventId, Vector3 averagedCollisionNormal, Rigidbody collidingBody, Vector3 contactPoint, bool hasCollisionStay = false, Vector3 averagedCollisionStayNormal = default) + { + if (Target == null) + { + return; + } + + if (collidingBody != null) + { + Log($">>>>>>> contact event with {collidingBody.name}!"); + } + else + { + Log($">>>>>>> contact event with non-rigidbody!"); + } + + m_ContactEvents.Add(new ContactEventInfo() + { + EventId = eventId, + AveragedCollisionNormal = averagedCollisionNormal, + CollidingBody = collidingBody, + ContactPoint = contactPoint, + }); + CheckToStopMoving(); + } + + private void SetInitialPositionClientServer() + { + if (IsServer) + { + if (!NetworkManager.DistributedAuthorityMode && !IsLocalPlayer) + { + transform.position = ClientSpawnPoint; + m_NetworkRigidbody.Rigidbody.position = ClientSpawnPoint; + } + else + { + transform.position = SessionOwnerSpawnPoint; + m_NetworkRigidbody.Rigidbody.position = SessionOwnerSpawnPoint; + } + } + else + { + transform.position = ClientSpawnPoint; + m_NetworkRigidbody.Rigidbody.position = ClientSpawnPoint; + } + } + + private void SetInitialPositionDistributedAuthority() + { + if (HasAuthority) + { + if (IsSessionOwner) + { + transform.position = SessionOwnerSpawnPoint; + m_NetworkRigidbody.Rigidbody.position = SessionOwnerSpawnPoint; + } + else + { + transform.position = ClientSpawnPoint; + m_NetworkRigidbody.Rigidbody.position = ClientSpawnPoint; + } + } + } + + public override void OnNetworkSpawn() + { + m_NetworkRigidbody = GetComponent(); + + m_NetworkRigidbody.Rigidbody.maxLinearVelocity = 15; + m_NetworkRigidbody.Rigidbody.maxAngularVelocity = 10; + + if (NetworkManager.DistributedAuthorityMode) + { + SetInitialPositionDistributedAuthority(); + } + else + { + SetInitialPositionClientServer(); + } + if (IsLocalPlayer) + { + RegisterForContactEvents(true); + } + else + { + m_NetworkRigidbody.Rigidbody.detectCollisions = false; + } + base.OnNetworkSpawn(); + } + + protected virtual void OnRegisterForContactEvents(bool isRegistering) + { + RigidbodyContactEventManager.Instance.RegisterHandler(this, isRegistering); + } + + public void RegisterForContactEvents(bool isRegistering) + { + OnRegisterForContactEvents(isRegistering); + } + + private void FixedUpdate() + { + if (!IsSpawned || !IsOwner || m_HelperState != HelperStates.MoveForward) + { + return; + } + var distance = Vector3.Distance(Target.transform.position, transform.position); + var moveAmount = Mathf.Max(1.2f, distance); + // Head towards our target + var dir = (Target.transform.position - transform.position).normalized; + var deltaMove = dir * moveAmount * Time.fixedDeltaTime; + m_NetworkRigidbody.Rigidbody.MovePosition(m_NetworkRigidbody.Rigidbody.position + deltaMove); + + + Log($" Loc: {transform.position} | Dest: {Target.transform.position} | Dist: {distance} | MoveDelta: {deltaMove}"); + } + + protected void Log(string msg) + { + if (VerboseDebug) + { + Debug.Log($"Client-{OwnerClientId} {msg}"); + } + } + } + #endregion +} +#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Physics/RigidbodyContactEventManagerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/RigidbodyContactEventManagerTests.cs.meta new file mode 100644 index 0000000000..687995a482 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Physics/RigidbodyContactEventManagerTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6d002e232abdb5049aa3c4f5cc36292d \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerSynchronizationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerSynchronizationTests.cs new file mode 100644 index 0000000000..0c5895b612 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerSynchronizationTests.cs @@ -0,0 +1,97 @@ +using System.Collections; +using System.Text.RegularExpressions; +using NUnit.Framework; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Unity.Netcode.RuntimeTests +{ + [TestFixture(HostOrServer.Host)] + [TestFixture(HostOrServer.DAHost)] + internal class NetworkPrefabHandlerSynchronizationTests : NetcodeIntegrationTest + { + protected override int NumberOfClients => 1; + + public NetworkPrefabHandlerSynchronizationTests(HostOrServer hostOrServer) : base(hostOrServer) { } + + private GameObject m_ValidPrefab; + private GameObject m_ClientSideValidPrefab; + private GameObject m_ClientSideExceptionPrefab; + + protected override void OnServerAndClientsCreated() + { + m_ValidPrefab = CreateNetworkObjectPrefab("ValidPrefab"); + m_ClientSideValidPrefab = CreateNetworkObjectPrefab("ClientSideValidPrefab"); + m_ClientSideExceptionPrefab = CreateNetworkObjectPrefab("ClientSideExceptionPrefab"); + base.OnServerAndClientsCreated(); + } + + [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer, RuntimePlatform.OSXPlayer, RuntimePlatform.OSXEditor })] // Ignored test tracked in MTT-15473 + public IEnumerator NetworkPrefabHandlerSpawnAndSynchronizeTests() + { + var nonAuthority = GetNonAuthorityNetworkManager(); + + var networkObjectToSpawnOnClient = m_ClientSideValidPrefab.GetComponent(); + nonAuthority.PrefabHandler.AddHandler(m_ClientSideExceptionPrefab, new NetworkPrefabExceptionThrower()); + nonAuthority.PrefabHandler.AddHandler(m_ValidPrefab, new NetworkPrefabInstanceHandler(networkObjectToSpawnOnClient)); + + var authority = GetAuthorityNetworkManager(); + + // Spawn the invalid object first. + var exceptionObject = SpawnObject(m_ClientSideExceptionPrefab, authority).GetComponent(); + + // Check the invalid object spawns on the authority, expect an error from non-authority. + LogAssert.Expect(LogType.Exception, "Exception: exception while instantiating"); + LogAssert.Expect(LogType.Error, new Regex("Failed to spawn NetworkObject!")); + // Authority should receive an error from non-authority and should use the globalObjectIdHash to find the failing object + LogAssert.Expect(LogType.Error, new Regex($@"SenderId:{nonAuthority.LocalClientId}\]\[{Regex.Escape(exceptionObject.name)}")); + + yield return WaitForConditionOrTimeOut(() => exceptionObject.IsSpawned); + AssertOnTimeout("Failed to spawn object on authority!"); + + // Now spawn a valid object + var validObject = SpawnObject(m_ValidPrefab, authority).GetComponent(); + + // The valid object should spawn as expected + yield return WaitForSpawnedOnAllOrTimeOut(validObject); + AssertOnTimeout("Failed to spawn valid prefab on all clients!"); + + // Create a new client and register the same PrefabHandlers on the client + var newClient = CreateNewClient(); + newClient.PrefabHandler.AddHandler(m_ClientSideExceptionPrefab, new NetworkPrefabExceptionThrower()); + newClient.PrefabHandler.AddHandler(m_ValidPrefab, new NetworkPrefabInstanceHandler(networkObjectToSpawnOnClient)); + + // Expect assertions from the new client + LogAssert.Expect(LogType.Exception, "Exception: exception while instantiating"); + LogAssert.Expect(LogType.Error, new Regex("Failed to spawn NetworkObject!")); + + // Authority will receive an error from new client and should use the globalObjectIdHash to find the failing object + var expectedNewClientId = nonAuthority.LocalClientId + 1; + LogAssert.Expect(LogType.Error, new Regex($@"SenderId:{expectedNewClientId}\]\[{Regex.Escape(exceptionObject.name)}")); + + // Start and synchronize the new client + yield return StartClient(newClient); + + // Validate the valid prefab spawned on all clients without issue + var expectedAuthorityHash = m_ValidPrefab.GetComponent().GlobalObjectIdHash; + var expectedNonAuthorityHash = m_ClientSideValidPrefab.GetComponent().GlobalObjectIdHash; + foreach (var networkManager in m_NetworkManagers) + { + Assert.True(networkManager.SpawnManager.SpawnedObjects.TryGetValue(validObject.NetworkObjectId, out NetworkObject spawnedObject), $"Client-{networkManager.LocalClientId} failed to spawn version of valid object!"); + + if (spawnedObject.HasAuthority) + { + Assert.That(spawnedObject.GlobalObjectIdHash, Is.EqualTo(expectedAuthorityHash), "NetworkObject spawned with unexpected GlobalObjectIdHash!"); + Assert.That(networkManager.SpawnManager.SpawnedObjects.ContainsKey(exceptionObject.NetworkObjectId), Is.True, "Authority missing spawned NetworkObject!"); + } + else + { + Assert.That(spawnedObject.GlobalObjectIdHash, Is.EqualTo(expectedNonAuthorityHash), "NetworkObject spawned with unexpected GlobalObjectIdHash!"); + Assert.That(networkManager.SpawnManager.SpawnedObjects.ContainsKey(exceptionObject.NetworkObjectId), Is.False, "Non authority should not have spawned exception object!"); + } + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerSynchronizationTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerSynchronizationTests.cs.meta new file mode 100644 index 0000000000..a0c417d419 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerSynchronizationTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 63f7e166459749bfbbf88e5b82ab917d +timeCreated: 1783026731 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs index 502146fa24..571ab30133 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.cs @@ -174,11 +174,6 @@ protected override void OnServerAndClientsCreated() networkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab); } - m_PrefabOverride.Prefab.GetComponent().IsSceneObject = false; - m_PrefabOverride.SourcePrefabToOverride.GetComponent().IsSceneObject = false; - m_PrefabOverride.OverridingTargetPrefab.GetComponent().IsSceneObject = false; - m_ClientSidePlayerPrefab.Prefab.GetComponent().IsSceneObject = false; - base.OnServerAndClientsCreated(); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs index 996021d425..52faa09867 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs @@ -1,4 +1,4 @@ -#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +#if MULTIPLAYER_TOOLS && (DEBUG || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -139,7 +139,16 @@ public IEnumerator RpcInvokePermissionSendingTests() [UnityTest] public IEnumerator RpcInvokePermissionReceivingTests() { - var firstClient = GetNonAuthorityNetworkManager(0); + NetworkManager firstClient = null; + foreach (var networkManager in m_NetworkManagers) + { + if (firstClient == null && !networkManager.IsServer && !networkManager.LocalClient.IsSessionOwner) + { + firstClient = networkManager; + } + + networkManager.LogLevel = LogLevel.Error; + } var spawnedObject = SpawnObject(m_Prefab, firstClient).GetComponent(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs index f943422b39..fb90660844 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs @@ -2,9 +2,9 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using NUnit.Framework; using Unity.Collections; using Unity.Netcode.TestHelpers.Runtime; @@ -29,22 +29,14 @@ internal class UniversalRpcNetworkBehaviour : NetworkBehaviour public ulong ReceivedFrom = ulong.MaxValue; public int ReceivedCount; - public void OnRpcReceived() + public void OnRpcReceived([CallerMemberName] string methodName = "") { - var st = new StackTrace(); - var sf = st.GetFrame(1); - - var currentMethod = sf.GetMethod(); - Received = currentMethod.Name; + Received = methodName; ReceivedCount++; } - public void OnRpcReceivedWithParams(int a, bool b, float f, string s) + public void OnRpcReceivedWithParams(int a, bool b, float f, string s, [CallerMemberName] string methodName = "") { - var st = new StackTrace(); - var sf = st.GetFrame(1); - - var currentMethod = sf.GetMethod(); - Received = currentMethod.Name; + Received = methodName; ReceivedCount++; ReceivedParams = new Tuple(a, b, f, s); } @@ -1031,7 +1023,8 @@ public void VerifySentToNotServerWithParams(ulong objectOwner, ulong sender, str public void VerifySentToClientsAndHostWithParams(ulong objectOwner, ulong sender, string methodName, int i, bool b, float f, string s) { - if (m_ServerNetworkManager.IsHost) + var authority = GetAuthorityNetworkManager(); + if (authority.IsHost) { VerifySentToEveryoneWithParams(objectOwner, sender, methodName, i, b, f, s); } @@ -1319,11 +1312,33 @@ private void SendingNoOverrideWithParams(SendTo sendTo, ulong objectOwner, ulong var sendMethodName = $"DefaultTo{sendTo}WithParamsRpc"; var verifyMethodName = $"VerifySentTo{sendTo}WithParams"; + VerboseDebug("Get player object..."); var senderObject = GetPlayerObject(objectOwner, sender); + if (senderObject == null) + { + Debug.LogError($"Sender object is NULL!"); + Assert.Fail(); + return; + } + + VerboseDebug($"Getting send method: {sendMethodName}"); var sendMethod = senderObject.GetType().GetMethod(sendMethodName); + if (sendMethod == null) + { + Debug.LogError($"Send method for {sendMethodName} is NULL!"); + Assert.Fail(); + return; + } sendMethod.Invoke(senderObject, new object[] { i, b, f, s }); + VerboseDebug($"Getting verify method: {verifyMethodName}"); var verifyMethod = GetType().GetMethod(verifyMethodName); + if (verifyMethod == null) + { + Debug.LogError($"Verify method for {verifyMethodName} is NULL!"); + Assert.Fail(); + return; + } verifyMethod.Invoke(this, new object[] { objectOwner, sender, sendMethodName, i, b, f, s }); } @@ -1344,11 +1359,33 @@ private void SendingNoOverrideWithParamsAndRpcParams(SendTo sendTo, ulong object var sendMethodName = $"DefaultTo{sendTo}WithParamsAndRpcParamsRpc"; var verifyMethodName = $"VerifySentTo{sendTo}WithParams"; + VerboseDebug("Get player object..."); var senderObject = GetPlayerObject(objectOwner, sender); + if (senderObject == null) + { + Debug.LogError($"Sender object is NULL!"); + Assert.Fail(); + return; + } + + VerboseDebug($"Getting send method: {sendMethodName}"); var sendMethod = senderObject.GetType().GetMethod(sendMethodName); + if (sendMethod == null) + { + Debug.LogError($"Send method for {sendMethodName} is NULL!"); + Assert.Fail(); + return; + } sendMethod.Invoke(senderObject, new object[] { i, b, f, s, new RpcParams() }); + VerboseDebug($"Getting verify method: {verifyMethodName}"); var verifyMethod = GetType().GetMethod(verifyMethodName); + if (verifyMethod == null) + { + Debug.LogError($"Verify method for {verifyMethodName} is NULL!"); + Assert.Fail(); + return; + } verifyMethod.Invoke(this, new object[] { objectOwner, sender, sendMethodName, i, b, f, s }); } @@ -1383,7 +1420,7 @@ private void RunTestTypeB(TestTypes testType) { foreach (var defaultSendTo in sendToValues) { - UnityEngine.Debug.Log($"[{testType}][{defaultSendTo}]"); + VerboseDebug($"[{testType}][{defaultSendTo}]"); foreach (var overrideSendTo in sendToValues) { for (ulong objectOwner = 0; objectOwner <= numberOfClientsULong; objectOwner++) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs index 2a5c2c35f9..209caf300b 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/IntegrationTestSceneHandler.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; @@ -141,47 +140,27 @@ private static void SceneManager_sceneLoaded(Scene scene, LoadSceneMode loadScen { SceneManager.sceneLoaded -= SceneManager_sceneLoaded; - ProcessInSceneObjects(scene, CurrentQueuedSceneJob.IntegrationTestSceneHandler.NetworkManager); + ProcessInSceneObjects(scene); CurrentQueuedSceneJob.JobType = QueuedSceneJob.JobTypes.Completed; } } /// - /// Handles some pre-spawn processing of in-scene placed NetworkObjects - /// to make sure the appropriate NetworkManagerOwner is assigned. It - /// also makes sure that each in-scene placed NetworkObject has an + /// Handles some pre-spawn processing of in-scene placed NetworkObjects. + /// Makes sure that each in-scene placed NetworkObject has an /// ObjectIdentifier component if one is not assigned to it or its /// children. /// /// the scenes that was just loaded - /// the relative NetworkManager - private static void ProcessInSceneObjects(Scene scene, NetworkManager networkManager) + private static void ProcessInSceneObjects(Scene scene) { - // Get all in-scene placed NeworkObjects that were instantiated when this scene loaded - var inSceneNetworkObjects = FindObjects.ByType(orderByIdentifier: true).Where((c) => c.IsSceneObject != false && c.GetSceneOriginHandle() == scene.handle); - foreach (var sobj in inSceneNetworkObjects) + // Get all in-scene placed NetworkObjects that were instantiated when this scene loaded + foreach (var sceneObject in FindObjects.FromSceneByType(scene, false)) { - ProcessInSceneObject(sobj, networkManager); - } - } - - /// - /// Assures to apply an ObjectNameIdentifier to all children - /// - private static void ProcessInSceneObject(NetworkObject networkObject, NetworkManager networkManager) - { - if (networkObject.GetComponent() == null) - { - networkObject.gameObject.AddComponent(); - var networkObjects = networkObject.gameObject.GetComponentsInChildren(); - foreach (var child in networkObjects) + if (sceneObject.InScenePlaced && sceneObject.GetComponent() == null) { - if (child == networkObject) - { - continue; - } - ProcessInSceneObject(child, networkManager); + sceneObject.gameObject.AddComponent(); } } } @@ -288,7 +267,7 @@ private void Sever_SceneLoaded(Scene scene, LoadSceneMode arg1) if (m_ServerSceneBeingLoaded == scene.name) { SceneManager.sceneLoaded -= Sever_SceneLoaded; - ProcessInSceneObjects(scene, NetworkManager); + ProcessInSceneObjects(scene); } } @@ -708,16 +687,11 @@ public void MoveObjectsFromSceneToDontDestroyOnLoad(ref NetworkManager networkMa { // Create a local copy of the spawned objects list since the spawn manager will adjust the list as objects // are despawned. - var networkObjects = FindObjects.ByType(orderByIdentifier: true).Where((c) => c.IsSpawned); var distributedAuthority = networkManager.DistributedAuthorityMode; - foreach (var networkObject in networkObjects) + foreach (var networkObject in FindObjects.FromSceneByType(scene, false)) { - if (networkObject == null || (networkObject != null && networkObject.gameObject.scene.handle != scene.handle)) + if (!networkObject.IsSpawned) { - if (networkObject != null) - { - VerboseDebug($"[MoveObjects from {scene.name} | {scene.handle}] Ignoring {networkObject.gameObject.name} because it isn't in scene {networkObject.gameObject.scene.name} "); - } continue; } @@ -750,7 +724,7 @@ public void MoveObjectsFromSceneToDontDestroyOnLoad(ref NetworkManager networkMa if (!networkObject.DestroyWithScene && networkObject.gameObject.scene != networkManager.SceneManager.DontDestroyOnLoadScene) { // Only move dynamically spawned NetworkObjects with no parent as the children will follow - if (networkObject.gameObject.transform.parent == null && networkObject.IsSceneObject != null && !networkObject.IsSceneObject.Value) + if (networkObject.gameObject.transform.parent == null && !networkObject.InScenePlaced) { VerboseDebug($"[MoveObjects from {scene.name} | {scene.handle}] Moving {networkObject.gameObject.name} because it is in scene {networkObject.gameObject.scene.name} with DWS = {networkObject.DestroyWithScene}."); Object.DontDestroyOnLoad(networkObject.gameObject); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index a6f4a83275..445055a964 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -6,6 +6,9 @@ using System.Runtime.CompilerServices; using System.Text; using NUnit.Framework; +#if UNIFIED_NETCODE +using Unity.NetCode; +#endif using Unity.Netcode.RuntimeTests; using Unity.Netcode.Transports.UTP; using UnityEngine; @@ -223,7 +226,17 @@ public enum HostOrServer /// /// Denotes that distributed authority is being used. /// - DAHost + DAHost, +#if UNIFIED_NETCODE + /// + /// Use N4E-backed hybrid spawning in server mode + /// + UnifiedServer, + /// + /// Use N4E-backed hybrid spawning in host mode + /// + UnifiedHost +#endif } /// @@ -609,7 +622,7 @@ private void InternalOnOneTimeSetup() VerboseDebug($"Exiting {nameof(OneTimeSetup)}"); -#if DEVELOPMENT_BUILD || UNITY_EDITOR +#if DEBUG // Default to not log the serialized type not optimized warning message when testing. NetworkManager.DisableNotOptimizedSerializedType = true; #endif @@ -626,6 +639,12 @@ private void InternalOnOneTimeSetup() /// protected virtual IEnumerator OnSetup() { +#if UNIFIED_NETCODE + if (m_AllPrefabsAsHybrid) + { + GhostSpawnManager.RegisterPendingGhost = RegisterPendingGhost; + } +#endif yield return null; } @@ -639,6 +658,12 @@ protected virtual IEnumerator OnSetup() /// protected virtual void OnInlineSetup() { +#if UNIFIED_NETCODE + if (m_AllPrefabsAsHybrid) + { + GhostSpawnManager.RegisterPendingGhost = RegisterPendingGhost; + } +#endif } /// @@ -705,6 +730,24 @@ public IEnumerator SetUp() VerboseDebug($"Exiting {nameof(SetUp)}"); } +#if UNIFIED_NETCODE + private void RegisterPendingGhost(NetworkObject networkObject, ulong networkObjectId) + { + var ghost = networkObject.GetComponent(); + Assert.IsNotNull(ghost, $"[RegisterPendingGhost][NetworkObject-{networkObjectId}] Has no {nameof(GhostObject)}!"); + foreach (var networkManager in m_NetworkManagers) + { + // If the world matches, then register the instance with this NetworkManager's spawn manager. + if (networkManager.NetcodeWorld == ghost.World) + { + networkManager.SpawnManager.GhostSpawnManager.RegisterGhostPendingSpawn(networkObject, networkObjectId); + return; + } + } + Debug.LogError($"Did not find a world for NetworkObject-{networkObjectId}!!"); + } +#endif + /// /// Override this to add components or adjustments to the default player prefab /// @@ -728,7 +771,6 @@ private void CreatePlayerPrefab() m_PlayerPrefab = new GameObject("Player"); OnPlayerPrefabGameObjectCreated(); NetworkObject networkObject = m_PlayerPrefab.AddComponent(); - networkObject.IsSceneObject = false; // Make it a prefab NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject); @@ -824,7 +866,20 @@ protected void CreateServerAndClients(int numberOfClients) { manager.NetworkConfig.PlayerPrefab = m_PlayerPrefab; SetDistributedAuthorityProperties(manager); +#if UNIFIED_NETCODE + foreach (var pendingPrefab in m_PendingPrefabs) + { + var prefab = new NetworkPrefab() + { + Prefab = pendingPrefab + }; + manager.NetworkConfig.Prefabs.Add(prefab); + } +#endif } +#if UNIFIED_NETCODE + m_PendingPrefabs.Clear(); +#endif // Provides opportunity to allow child derived classes to // modify the NetworkManager's configuration before starting. @@ -1613,6 +1668,21 @@ protected IEnumerator CoroutineShutdownAndCleanUp() DestroyNetworkManagers(); } + /// + /// When using hybrid spawning, this handles clean up. + /// + protected void UnifiedCleanup() + { +#if UNIFIED_NETCODE + if (m_AllPrefabsAsHybrid) + { + m_PendingPrefabs.Clear(); + GhostSpawnManager.RegisterPendingGhost = null; + CleanupPrefabReferences(); + } +#endif + } + /// /// Note: For mode /// this is called before ShutdownAndCleanUp. @@ -1620,6 +1690,7 @@ protected IEnumerator CoroutineShutdownAndCleanUp() /// protected virtual IEnumerator OnTearDown() { + UnifiedCleanup(); yield return null; } @@ -1628,6 +1699,7 @@ protected virtual IEnumerator OnTearDown() /// protected virtual void OnInlineTearDown() { + UnifiedCleanup(); } /// @@ -1763,6 +1835,24 @@ protected void DestroySceneNetworkObjects() if (CanDestroyNetworkObject(networkObject)) { +#if UNIFIED_NETCODE + // Handle removing the prefab reference and destroying it + // and then destroying the GhostObject prior to destroying + // a hybrid prefab. + var ghostAdapter = networkObject.GetComponent(); + if (ghostAdapter != null) + { + if (ghostAdapter.prefabReference != null) + { + var prefabReference = ghostAdapter.prefabReference; + prefabReference.Prefab = null; + ghostAdapter.prefabReference = null; + Object.Destroy(prefabReference); + } + Object.Destroy(networkObject.gameObject); + continue; + } +#endif // Destroy the GameObject that holds the NetworkObject component Object.DestroyImmediate(networkObject.gameObject); } @@ -1934,6 +2024,22 @@ protected IEnumerator WaitForConditionOrTimeOut(Func checkF }, timeOutHelper); } + /// + /// Waits until the specified condition returns true or a timeout occurs. + /// This overload allows the condition to provide additional error details via a . + /// + /// A delegate that takes a for error details and returns true when the desired condition is met. + /// the maximum times to check for the condition (default is 60). + protected void WaitForConditionOrTimeOutWithTimeTravel(Func checkForCondition, int maxTries = 60) + { + WaitForConditionOrTimeOutWithTimeTravel(() => + { + // Clear errorBuilder before each check to ensure the errorBuilder only contains information from the lastest run + m_InternalErrorLog.Clear(); + return checkForCondition(m_InternalErrorLog); + }, maxTries); + } + /// /// Waits until the given NetworkObject is spawned on all clients or a timeout occurs. /// @@ -1988,7 +2094,7 @@ protected IEnumerator WaitForSpawnedOnAllOrTimeOut(GameObject gameObject, Timeou /// The list of s to wait for. /// An optional to control the timeout period. If null, the default timeout is used. /// An for use in Unity coroutines. - protected IEnumerator WaitForSpawnedOnAllOrTimeOut(List networkObjects, TimeoutHelper timeOutHelper = null) + protected IEnumerator WaitForSpawnedOnAllOrTimeOut(ICollection networkObjects, TimeoutHelper timeOutHelper = null) { bool ValidateObjectsSpawnedOnAllClients(StringBuilder errorLog) { @@ -2009,6 +2115,77 @@ bool ValidateObjectsSpawnedOnAllClients(StringBuilder errorLog) yield return WaitForConditionOrTimeOut(ValidateObjectsSpawnedOnAllClients, timeOutHelper); } + /// + /// Waits until the given NetworkObject is spawned on all clients or a timeout occurs. + /// + /// The id of the to wait for. + /// the maximum times to check for the condition (default is 60). + protected void WaitForSpawnedOnAllOrTimeOutWithTimeTravel(ulong networkObjectId, int maxTries = 60) + { + bool ValidateObjectSpawnedOnAllClients(StringBuilder errorLog) + { + foreach (var client in m_NetworkManagers) + { + if (!client.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + { + errorLog.Append($"Client-{client.LocalClientId} has not spawned Object-{networkObjectId}!"); + return false; + } + } + return true; + } + + WaitForConditionOrTimeOutWithTimeTravel(ValidateObjectSpawnedOnAllClients, maxTries); + } + + /// + /// Waits until the given NetworkObject is spawned on all clients or a timeout occurs. + /// + /// The to wait for. + /// the maximum times to check for the condition (default is 60). + protected void WaitForSpawnedOnAllOrTimeOutWithTimeTravel(NetworkObject networkObject, int maxTries = 60) + { + var networkObjectId = networkObject.NetworkObjectId; + WaitForSpawnedOnAllOrTimeOutWithTimeTravel(networkObjectId, maxTries); + } + + /// + /// Waits until the given NetworkObject is spawned on all clients or a timeout occurs. + /// + /// The containing a to wait for. + /// the maximum times to check for the condition (default is 60). + protected void WaitForSpawnedOnAllOrTimeOutWithTimeTravel(GameObject gameObject, int maxTries = 60) + { + var networkObjectId = gameObject.GetComponent().NetworkObjectId; + WaitForSpawnedOnAllOrTimeOutWithTimeTravel(networkObjectId, maxTries); + } + + /// + /// Waits until all given NetworkObjects are spawned on all clients or a timeout occurs. + /// + /// The list of s to wait for. + /// the maximum times to check for the condition (default is 60). + protected void WaitForSpawnedOnAllOrTimeOutWithTimeTravel(List networkObjects, int maxTries = 60) + { + bool ValidateObjectsSpawnedOnAllClients(StringBuilder errorLog) + { + foreach (var client in m_NetworkManagers) + { + foreach (var networkObject in networkObjects) + { + if (!client.SpawnManager.SpawnedObjects.ContainsKey(networkObject.NetworkObjectId)) + { + errorLog.Append($"Client-{client.LocalClientId} has not spawned Object-{networkObject.NetworkObjectId}!"); + return false; + } + } + } + return true; + } + + WaitForConditionOrTimeOutWithTimeTravel(ValidateObjectsSpawnedOnAllClients, maxTries); + } + /// /// Waits until all given NetworkObjects are spawned on all clients or a timeout occurs. /// @@ -2189,6 +2366,10 @@ internal void WaitForMessagesReceivedWithTimeTravel(List messagesInOrder, Assert.True(WaitForConditionOrTimeOutWithTimeTravel(hooks), $"[Messages Not Recieved] {hooks.GetHooksStillWaiting()}"); } +#if UNIFIED_NETCODE + protected bool m_AllPrefabsAsHybrid = false; +#endif + /// /// Creates a basic NetworkObject test prefab, assigns it to a new /// NetworkPrefab entry, and then adds it to the server and client(s) @@ -2198,6 +2379,12 @@ internal void WaitForMessagesReceivedWithTimeTravel(List messagesInOrder, /// The assigned to the new NetworkPrefab entry protected GameObject CreateNetworkObjectPrefab(string baseName) { +#if UNIFIED_NETCODE + if (m_AllPrefabsAsHybrid) + { + return CreateHybridPrefab(baseName, true); + } +#endif var prefabCreateAssertError = $"You can only invoke this method during {nameof(OnServerAndClientsCreated)} " + $"but before {nameof(OnStartedServerAndClients)}!"; var authorityNetworkManager = GetAuthorityNetworkManager(); @@ -2210,6 +2397,100 @@ protected GameObject CreateNetworkObjectPrefab(string baseName) return prefabObject; } +#if UNIFIED_NETCODE + // Pending prefabs declared before NetworkManagers instantiated + private List m_PendingPrefabs = new List(); + protected void CleanupPrefabReferences() + { + foreach (var reference in Object.FindObjectsByType()) + { + Object.Destroy(reference); + } + } + protected GameObject CreateHybridPrefab(string baseName, bool moveToDDOL = true) + { + // Prevent from trying to register/spawn when creating this hybrid prefab + var gameObject = new GameObject + { + name = baseName + }; + + // Order of operations in how these execute is actually important. + // GhostObject should execute 1st. + // NetworkObjectBridge 2nd. + // NetworkObject 3rd. + // NetworkBehaviours will execute in the order they are arranged unless otherwise specified. + + // When adding a Hybrid/Ghost prefab: + // - We disabled the GameObject prior to adding the GhostPrefabReference (so IsPrefab() == true). + // - Add the GhostObject and GhostPrefabReference + // - Then set it back to active. + gameObject.SetActive(false); + var adapter = gameObject.AddComponent(); + // Mark the reference as post processing to avoid registering this instance automatically. + GhostPrefabReference.s_IsPostProcessing = true; + adapter.prefabReference = ScriptableObject.CreateInstance(); + adapter.prefabReference.name = "GhostPrefabReference"; + adapter.prefabReference.Prefab = gameObject; + + GhostPrefabReference.s_IsPostProcessing = false; + + // TODO: This might be part of the CreateHybridPrefab parameters + // For now, just use normal interpolation until we get integration + // tests running. + // Once we have validated prediction works and have a working manual + // test, we can circle back to this (possibly make that a sub-task + // with the dependency to prediction manual test). + adapter.SupportedGhostModes = GhostModeMask.Interpolated; + + // Once done with setting up the GhostObject, we can set it back to active in the hierarchy + gameObject.SetActive(true); + + // GhostBehaviours that are part of a prefab will not invoke Ghost.InternalAcquireEntityReference + // Add the bridge + var bridge = gameObject.AddComponent(); + + // Now add NGO components + var no = gameObject.AddComponent(); + + // NetworkObject Ghost specific settings + no.HasGhost = true; + no.GhostObject = adapter; + no.HadBridge = true; + no.NetworkObjectBridge = bridge; + + // Disable transform synchronization for NetworkObject serialization + // since that is handled by the GhostObject. + no.SynchronizeTransform = false; + + // Turn it into a test prefab + NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(no); + if (moveToDDOL) + { + Object.DontDestroyOnLoad(gameObject); + } + var authorityNetworkManager = GetAuthorityNetworkManager(); + if (authorityNetworkManager == null) + { + m_PendingPrefabs.Add(gameObject); + } + else + { + authorityNetworkManager.AddNetworkPrefab(gameObject); + foreach (var clientNetworkManager in m_ClientNetworkManagers) + { + if (clientNetworkManager == authorityNetworkManager) + { + continue; + } + clientNetworkManager.AddNetworkPrefab(gameObject); + } + } + return gameObject; + } +#endif + + /// /// Overloaded method /// @@ -2255,6 +2536,14 @@ internal void SpawnInstanceWithOwnership(NetworkObject networkObjectToSpawn, Net } else { +#if UNIFIED_NETCODE + // TODO-FixMe: NetCode.Netcode.Instance is a singleton and might cause issues + // assigning this. + if (networkObjectToSpawn.HasGhost) + { + NetCode.Netcode.Instance.m_ActiveWorld = m_ServerNetworkManager.NetcodeWorld; + } +#endif networkObjectToSpawn.NetworkManagerOwner = m_ServerNetworkManager; // Required to assure the server does the spawning if (spawnAuthority == m_ServerNetworkManager) { @@ -2406,8 +2695,6 @@ public NetcodeIntegrationTest(HostOrServer hostOrServer) private void InitializeTestConfiguration(NetworkTopologyTypes networkTopologyType, HostOrServer? hostOrServer) { - NetworkMessageManager.EnableMessageOrderConsoleLog = false; - // Set m_NetworkTopologyType first because m_DistributedAuthority is calculated from it. m_NetworkTopologyType = networkTopologyType; @@ -2417,8 +2704,12 @@ private void InitializeTestConfiguration(NetworkTopologyTypes networkTopologyTyp // Note: For m_DistributedAuthority to be true, the m_NetworkTopologyType must be set to NetworkTopologyTypes.DistributedAuthority hostOrServer = m_DistributedAuthority ? HostOrServer.DAHost : HostOrServer.Host; } +#if UNIFIED_NETCODE + m_UseHost = hostOrServer == HostOrServer.Host || hostOrServer == HostOrServer.DAHost || hostOrServer == HostOrServer.UnifiedHost; + m_AllPrefabsAsHybrid = (hostOrServer == HostOrServer.UnifiedServer || hostOrServer == HostOrServer.UnifiedHost); +#else m_UseHost = hostOrServer == HostOrServer.Host || hostOrServer == HostOrServer.DAHost; - +#endif // If we are using a distributed authority network topology and the environment variable // to use the CMBService is set, then perform the m_UseCmbService check. if (m_DistributedAuthority && GetServiceEnvironmentVariable()) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs index 32a0f734d7..8f32fc1916 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs @@ -690,9 +690,6 @@ public static void MakeNetworkObjectTestPrefab(NetworkObject networkObject, uint networkObject.GlobalObjectIdHash = ++s_AutoIncrementGlobalObjectIdHashCounter; } - // Prevent object from being snapped up as a scene object - networkObject.IsSceneObject = false; - // To avoid issues with integration tests that forget to clean up, // this feature only works with NetcodeIntegrationTest derived classes if (IsNetcodeIntegrationTestRunning) @@ -745,18 +742,17 @@ public static GameObject CreateNetworkObjectPrefab(string baseName, NetworkManag Assert.IsFalse(authorityNetworkManager.IsListening, prefabCreateAssertError); var gameObject = CreateNetworkObject(baseName); - var networkPrefab = new NetworkPrefab() { Prefab = gameObject }; // We could refactor this test framework to share a NetworkPrefabList instance, but at this point it's // probably more trouble than it's worth to verify these lists stay in sync across all tests... - authorityNetworkManager.NetworkConfig.Prefabs.Add(networkPrefab); + authorityNetworkManager.AddNetworkPrefab(gameObject); foreach (var clientNetworkManager in clients) { if (clientNetworkManager == authorityNetworkManager) { continue; } - clientNetworkManager.NetworkConfig.Prefabs.Add(new NetworkPrefab() { Prefab = gameObject }); + clientNetworkManager.AddNetworkPrefab(gameObject); } return gameObject; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeLogAssert.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeLogAssert.cs index 72f9753ee1..e2c95d7450 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeLogAssert.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeLogAssert.cs @@ -222,6 +222,30 @@ public bool HasLogBeenReceived(LogType type, string message) return found; } + /// + /// Determines if a log message was logged or not. + /// + /// to check for. + /// containing the message to search for. + /// or + public bool HasLogBeenReceived(LogType type, Regex message) + { + var found = false; + lock (m_Lock) + { + foreach (var logEvent in AllLogs) + { + if (logEvent.LogType == type && message.IsMatch(logEvent.Message)) + { + found = true; + break; + } + } + } + return found; + } + + /// /// Clears out the log history that is searched. /// diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs index abbfcad948..2bdce8f205 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs @@ -429,7 +429,10 @@ public IEnumerator ServerDisconnectWithDataInQueue() } // Check client disconnection with data in send queue. - [UnityTest] + // Excluded on iOS: sending Unreliable data then immediately disconnecting is a race on slow + // iOS CI devices (the unreliable packet can be dropped), so it flakes with a "Timed out while + // waiting for network event" even after raising the mobile timeout. Tracked by MTT-15433. + [UnityTest, UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] public IEnumerator ClientDisconnectWithDataInQueue() { InitializeTransport(out m_Server, out m_ServerEvents); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef b/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef index cde139b07f..86b6bae95f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef @@ -13,7 +13,9 @@ "Unity.Netcode.TestHelpers.Runtime", "Unity.Mathematics", "UnityEngine.TestRunner", - "UnityEditor.TestRunner" + "UnityEditor.TestRunner", + "Unity.NetCode", + "Unity.Entities" ], "includePlatforms": [], "excludePlatforms": [], @@ -47,6 +49,11 @@ "name": "Unity", "expression": "6000.1.0a1", "define": "HOSTNAME_RESOLUTION_AVAILABLE" + }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" } ], "noEngineReferences": false diff --git a/com.unity.netcode.gameobjects/package.json b/com.unity.netcode.gameobjects/package.json index 04492deaa0..7c27297c2d 100644 --- a/com.unity.netcode.gameobjects/package.json +++ b/com.unity.netcode.gameobjects/package.json @@ -2,8 +2,9 @@ "name": "com.unity.netcode.gameobjects", "displayName": "Netcode for GameObjects", "description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.", - "version": "2.11.3", - "unity": "6000.0", + "version": "3.0.0", + "unity": "6000.6", + "unityRelease": "0b5", "dependencies": { "com.unity.nuget.mono-cecil": "1.11.4", "com.unity.transport": "2.6.0" diff --git a/minimalproject/Packages/manifest.json b/minimalproject/Packages/manifest.json index 6556fd319a..0a7ffc6f4d 100644 --- a/minimalproject/Packages/manifest.json +++ b/minimalproject/Packages/manifest.json @@ -2,7 +2,12 @@ "disableProjectUpdate": false, "dependencies": { "com.unity.ide.rider": "3.0.38", - "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects" + "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", + "com.unity.modules.adaptiveperformance": "1.0.0", + "com.unity.modules.physicscore2d": "1.0.0", + "com.unity.modules.tetgen": "1.0.0", + "com.unity.modules.timelinefoundation": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0" }, "testables": [ "com.unity.netcode.gameobjects" diff --git a/minimalproject/Packages/packages-lock.json b/minimalproject/Packages/packages-lock.json deleted file mode 100644 index 5b95287ae1..0000000000 --- a/minimalproject/Packages/packages-lock.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "dependencies": { - "com.unity.burst": { - "version": "1.8.25", - "depth": 2, - "source": "registry", - "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.collections": { - "version": "2.6.2", - "depth": 2, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.23", - "com.unity.mathematics": "1.3.2", - "com.unity.test-framework": "1.4.6", - "com.unity.nuget.mono-cecil": "1.11.5", - "com.unity.test-framework.performance": "3.0.3" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ext.nunit": { - "version": "2.0.5", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.ide.rider": { - "version": "3.0.38", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ext.nunit": "1.0.6" - }, - "url": "https://packages.unity.com" - }, - "com.unity.mathematics": { - "version": "1.3.2", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.netcode.gameobjects": { - "version": "file:../../com.unity.netcode.gameobjects", - "depth": 0, - "source": "local", - "dependencies": { - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.6.0" - } - }, - "com.unity.nuget.mono-cecil": { - "version": "1.11.5", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.test-framework": { - "version": "1.6.0", - "depth": 3, - "source": "builtin", - "dependencies": { - "com.unity.ext.nunit": "2.0.3", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.test-framework.performance": { - "version": "3.2.0", - "depth": 3, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.33", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.transport": { - "version": "2.6.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.24", - "com.unity.collections": "2.2.1", - "com.unity.mathematics": "1.3.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.modules.imgui": { - "version": "1.0.0", - "depth": 4, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.jsonserialize": { - "version": "1.0.0", - "depth": 3, - "source": "builtin", - "dependencies": {} - } - } -} diff --git a/minimalproject/ProjectSettings/PackageManagerSettings.asset b/minimalproject/ProjectSettings/PackageManagerSettings.asset index be4a7974ec..71b4685896 100644 --- a/minimalproject/ProjectSettings/PackageManagerSettings.asset +++ b/minimalproject/ProjectSettings/PackageManagerSettings.asset @@ -12,32 +12,33 @@ MonoBehaviour: m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: - m_EnablePreviewPackages: 0 - m_EnablePackageDependencies: 0 + m_EnablePreReleasePackages: 0 m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 0 - m_Registries: - - m_Id: main + oneTimePackageErrorsPopUpShown: 0 + m_MainRegistry: + m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_IsUnityRegistry: 1 m_Capabilities: 7 + m_ConfigSource: 0 + m_Compliance: + m_Status: 0 + m_Violations: [] + m_ScopedRegistries: [] m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: - m_ErrorMessage: - m_Original: - m_Id: - m_Name: - m_Url: - m_Scopes: [] - m_IsDefault: 0 - m_Capabilities: 0 m_Modified: 0 - m_Name: - m_Url: - m_Scopes: - - - m_SelectedScopeIndex: 0 + m_ErrorMessage: + m_UserModificationsEntityId: + m_rawData: 1099511629076 + m_OriginalEntityId: + m_rawData: 1099511629077 + m_LoadAssets: 0 diff --git a/minimalproject/ProjectSettings/PhysicsCoreProjectSettings2D.asset b/minimalproject/ProjectSettings/PhysicsCoreProjectSettings2D.asset new file mode 100644 index 0000000000..049c7454b5 --- /dev/null +++ b/minimalproject/ProjectSettings/PhysicsCoreProjectSettings2D.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!176606843 &1 +PhysicsCoreProjectSettings2D: + m_ObjectHideFlags: 0 + m_PhysicsCoreSettings: {fileID: 0} diff --git a/minimalproject/ProjectSettings/ProjectSettings.asset b/minimalproject/ProjectSettings/ProjectSettings.asset index d110b4845a..4b22a835c4 100644 --- a/minimalproject/ProjectSettings/ProjectSettings.asset +++ b/minimalproject/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 22 + serializedVersion: 30 productGUID: 53f9fd541540241529c78965266c29d6 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -41,21 +41,23 @@ PlayerSettings: height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1024 defaultScreenHeight: 768 defaultScreenWidthWeb: 960 defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 + unsupportedMSAAFallback: 0 + m_SpriteBatchMaxVertexCount: 65535 + m_SpriteBatchVertexThreshold: 300 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 - m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + numberOfMipsStrippedPerMipmapLimitGroup: {} + m_StackTraceTypes: 010000000100000001000000010000000100000001000000i iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 - iosAllowHTTPDownload: 1 allowedAutorotateToPortrait: 1 allowedAutorotateToPortraitUpsideDown: 1 allowedAutorotateToLandscapeRight: 1 @@ -63,22 +65,39 @@ PlayerSettings: useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 + adjustIOSFPSUsingThermalState: 1 + thermalStateSeriousIOSFPS: 30 + thermalStateCriticalIOSFPS: 15 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 + androidRequestedVisibleInsets: 0 + androidSystemBarsBehavior: 2 + androidDisplayOptions: 1 androidBlitType: 0 + androidResizeableActivity: 1 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + androidAutoRotationBehavior: 1 + androidPredictiveBackSupport: 1 + androidApplicationEntry: 1 defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 - captureSingleScreen: 0 + callOnDisableOnAssetBundleUnload: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 usePlayerLog: 1 + dedicatedServerOptimizations: 1 bakeCollisionMeshes: 0 forceSingleInstance: 0 useFlipModelSwapchain: 1 @@ -86,6 +105,7 @@ PlayerSettings: useMacAppStoreValidation: 0 macAppStoreCategory: public.app-category.games gpuSkinning: 1 + meshDeformation: 2 xboxPIXTextureCapture: 0 xboxEnableAvatar: 0 xboxEnableKinect: 0 @@ -99,6 +119,7 @@ PlayerSettings: xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 @@ -113,48 +134,53 @@ PlayerSettings: switchNVNShaderPoolsGranularity: 33554432 switchNVNDefaultPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216 + switchGpuScratchPoolGranularity: 2097152 + switchAllowGpuScratchShrinking: 0 switchNVNMaxPublicTextureIDCount: 0 switchNVNMaxPublicSamplerIDCount: 0 - stadiaPresentMode: 0 - stadiaTargetFramerate: 0 + switchMaxWorkerMultiple: 8 + switchNVNGraphicsFirmwareMemory: 32 + switchGraphicsJobsSyncAfterKick: 1 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 vulkanEnablePreTransform: 0 vulkanEnableLateAcquireNextImage: 0 - m_SupportedAspectRatios: - 4:3: 1 - 5:4: 1 - 16:10: 1 - 16:9: 1 - Others: 1 + vulkanEnableCommandBufferRecycling: 1 + loadStoreDebugModeEnabled: 0 + visionOSBundleVersion: 1.0 + tvOSBundleVersion: 1.0 bundleVersion: 0.1 preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + allowHDRDisplaySupport: 0 useHDRDisplay: 0 - D3DHDRBitDepth: 0 - m_ColorGamuts: 00000000 + hdrBitDepth: 0 + m_ColorGamuts: 00000000i targetPixelDensity: 30 resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 + androidMinAspectRatio: 1 applicationIdentifier: {} buildNumber: Standalone: 0 + VisionOS: 0 iPhone: 0 tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 19 + AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 @@ -162,15 +188,21 @@ PlayerSettings: ForceInternetPermission: 0 ForceSDCardPermission: 0 CreateWallpaper: 0 - APKExpansionFiles: 0 + androidSplitApplicationBinary: 0 keepLoadedShadersAlive: 0 StripUnusedMeshComponents: 1 + strictShaderVariantMatching: 0 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 - iOSTargetOSVersionString: 11.0 + iOSSimulatorArchitecture: 0 + iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 + tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 11.0 + tvOSTargetOSVersionString: 15.0 + VisionOSSdkVersion: 0 + VisionOSTargetOSVersionString: 1.0 + xcodeProjectType: 0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -195,7 +227,6 @@ PlayerSettings: rgba: 0 iOSLaunchScreenFillPct: 100 iOSLaunchScreenSize: 100 - iOSLaunchScreenCustomXibPath: iOSLaunchScreeniPadType: 0 iOSLaunchScreeniPadImage: {fileID: 0} iOSLaunchScreeniPadBackgroundColor: @@ -203,22 +234,25 @@ PlayerSettings: rgba: 0 iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadSize: 100 - iOSLaunchScreeniPadCustomXibPath: iOSLaunchScreenCustomStoryboardPath: iOSLaunchScreeniPadCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] + macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 metalAPIValidation: 1 + metalCompileShaderBinary: 0 iOSRenderExtraFrameOnPause: 0 iosCopyPluginsCodeInsteadOfSymlink: 0 appleDeveloperTeamID: iOSManualSigningProvisioningProfileID: tvOSManualSigningProvisioningProfileID: + VisionOSManualSigningProvisioningProfileID: iOSManualSigningProvisioningProfileType: 0 tvOSManualSigningProvisioningProfileType: 0 + VisionOSManualSigningProvisioningProfileType: 0 appleEnableAutomaticSigning: 0 iOSRequireARKit: 0 iOSAutomaticallyDetectAndAddCapabilities: 1 @@ -233,15 +267,21 @@ PlayerSettings: useCustomLauncherGradleManifest: 0 useCustomBaseGradleTemplate: 0 useCustomGradlePropertiesTemplate: 0 + useCustomGradleSettingsTemplate: 0 useCustomProguardFile: 0 AndroidTargetArchitectures: 1 AndroidSplashScreenScale: 0 androidSplashScreen: {fileID: 0} AndroidKeystoreName: AndroidKeyaliasName: + AndroidEnableArmv9SecurityFeatures: 0 + AndroidEnableArm64MTE: 0 AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 + androidAppCategory: 3 + useAndroidAppCategory: 1 + androidAppCategoryOther: AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 @@ -251,11 +291,12 @@ PlayerSettings: height: 180 banner: {fileID: 0} androidGamepadSupportLevel: 0 - AndroidMinifyWithR8: 0 AndroidMinifyRelease: 0 AndroidMinifyDebug: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 150 + AndroidReportGooglePlayAppDependencies: 1 + androidSymbolsSizeThreshold: 800 m_BuildTargetIcons: [] m_BuildTargetPlatformIcons: [] m_BuildTargetBatching: @@ -274,6 +315,7 @@ PlayerSettings: - m_BuildTarget: WebGL m_StaticBatching: 0 m_DynamicBatching: 0 + m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: - m_BuildTarget: MacStandaloneSupport m_GraphicsJobs: 0 @@ -308,23 +350,22 @@ PlayerSettings: m_GraphicsJobMode: 0 m_BuildTargetGraphicsAPIs: - m_BuildTarget: AndroidPlayer - m_APIs: 150000000b000000 - m_Automatic: 0 + m_APIs: 150000000b000000i + m_Automatic: 1 - m_BuildTarget: iOSSupport - m_APIs: 10000000 + m_APIs: 10000000i m_Automatic: 1 - m_BuildTarget: AppleTVSupport - m_APIs: 10000000 + m_APIs: 10000000i m_Automatic: 1 - m_BuildTarget: WebGLSupport - m_APIs: 0b000000 + m_APIs: 0b000000i m_Automatic: 1 - m_BuildTargetVRSettings: - - m_BuildTarget: Standalone - m_Enabled: 0 - m_Devices: - - Oculus - - OpenVR + - m_BuildTarget: WindowsStandaloneSupport + m_APIs: 0200000012000000i + m_Automatic: 0 + m_DefaultShaderChunkSizeInMB: 16 + m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 @@ -334,17 +375,23 @@ PlayerSettings: iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupHDRCubemapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetGroupLoadStoreDebugModeSettings: [] m_BuildTargetNormalMapEncoding: [] + m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 + editorGfxJobOverride: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 cameraUsageDescription: locationUsageDescription: microphoneUsageDescription: + bluetoothUsageDescription: + macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 @@ -352,9 +399,11 @@ PlayerSettings: switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 - switchUseGOLDLinker: 0 + switchEnableFileSystemTrace: 0 + switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: + switchCompilerFlags: switchTitleNames_0: switchTitleNames_1: switchTitleNames_2: @@ -428,7 +477,6 @@ PlayerSettings: switchReleaseVersion: 0 switchDisplayVersion: 1.0.0 switchStartupUserAccount: 0 - switchTouchScreenUsage: 0 switchSupportedLanguagesMask: 0 switchLogoType: 0 switchApplicationErrorCodeCategory: @@ -470,6 +518,7 @@ PlayerSettings: switchNativeFsCacheSize: 32 switchIsHoldTypeHorizontal: 0 switchSupportedNpadCount: 8 + switchEnableTouchScreen: 1 switchSocketConfigEnabled: 0 switchTcpInitialSendBufferSize: 32 switchTcpInitialReceiveBufferSize: 64 @@ -480,10 +529,16 @@ PlayerSettings: switchSocketBufferEfficiency: 4 switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 - switchPlayerConnectionEnabled: 1 + switchDisableHTCSPlayerConnection: 0 switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 0 switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + switchUpgradedPlayerSettingsToNMETA: 0 + switchCaStoreSource: 0 + switchCaStoreFilePath: ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -554,6 +609,7 @@ PlayerSettings: ps4videoRecordingFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0 ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 ps4GPU800MHz: 1 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] @@ -566,6 +622,7 @@ PlayerSettings: webGLMemorySize: 16 webGLExceptionSupport: 1 webGLNameFilesAsHashes: 0 + webGLShowDiagnostics: 0 webGLDataCaching: 1 webGLDebugSymbols: 0 webGLEmscriptenArgs: @@ -578,25 +635,57 @@ PlayerSettings: webGLLinkerTarget: 1 webGLThreadsSupport: 0 webGLDecompressionFallback: 0 + webGLInitialMemorySize: 32 + webGLMaximumMemorySize: 2048 + webGLMemoryGrowthMode: 2 + webGLMemoryLinearGrowthStep: 16 + webGLMemoryGeometricGrowthStep: 0.2 + webGLMemoryGeometricGrowthCap: 96 + webGLPowerPreference: 2 + webGLWebAssemblyTable: 0 + webGLWebAssemblyBigInt: 0 + webGLCloseOnQuit: 0 + webWasm2023: 1 + webEnableSubmoduleStrippingCompatibility: 0 + webProgressiveAssetLoading: 0 scriptingDefineSymbols: - 1: NGO_MINIMALPROJECT + Standalone: NGO_MINIMALPROJECT additionalCompilerArguments: {} platformArchitecture: {} - scriptingBackend: {} + scriptingBackend: + Android: 0 il2cppCompilerConfiguration: {} - managedStrippingLevel: {} + il2cppCodeGeneration: {} + il2cppLTOMode: {} + il2cppStacktraceInformation: {} + managedStrippingLevel: + Android: 1 + EmbeddedLinux: 1 + GameCoreScarlett: 1 + GameCoreXboxOne: 1 + Kepler: 1 + Nintendo Switch: 1 + Nintendo Switch 2: 1 + PS4: 1 + PS5: 1 + QNX: 1 + VisionOS: 1 + WebGL: 1 + Windows Store Apps: 1 + XboxOne: 1 + iPhone: 1 + tvOS: 1 incrementalIl2cppBuild: {} suppressCommonWarnings: 1 allowUnsafeCode: 0 useDeterministicCompilation: 1 - useReferenceAssemblies: 1 - enableRoslynAnalyzers: 1 additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 1 - assemblyVersionValidation: 1 gcWBarrierValidation: 0 + managedCodeVariant: {} apiCompatibilityLevelPerPlatform: {} + editorAssembliesCompatibilityLevel: 1 m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: Template_3D @@ -620,11 +709,13 @@ PlayerSettings: metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 platformCapabilities: {} metroTargetDeviceFamilies: {} metroFTAName: metroFTAFileTypes: [] metroProtocolName: + vcxProjDefaultLanguage: XboxOneProductId: XboxOneUpdateKey: XboxOneSandboxId: @@ -652,7 +743,6 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} cloudServicesEnabled: UNet: 1 luminIcon: @@ -666,8 +756,17 @@ PlayerSettings: luminVersion: m_VersionCode: 1 m_VersionName: + hmiPlayerDataPath: + hmiForceSRGBBlit: 0 + embeddedLinuxEnableGamepadInput: 0 + hmiCpuConfiguration: + hmiLogStartupTiming: 0 + qnxGraphicConfPath: apiCompatibilityLevel: 6 + captureStartupLogs: {} activeInputHandler: 0 + windowsGamepadBackendHint: 0 + enableDirectStorage: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -675,4 +774,13 @@ PlayerSettings: organizationId: cloudEnabled: 0 legacyClampBlendShapeWeights: 0 + hmiLoadingImage: {fileID: 0} + platformRequiresReadableAssets: 0 virtualTexturingSupportEnabled: 0 + insecureHttpOption: 0 + androidVulkanDenyFilterList: [] + androidVulkanAllowFilterList: [] + androidVulkanDeviceFilterListAsset: {fileID: 0} + d3d12DeviceFilterListAsset: {fileID: 0} + webGPUDeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/minimalproject/ProjectSettings/ProjectVersion.txt b/minimalproject/ProjectSettings/ProjectVersion.txt index 47df254537..02866a2258 100644 --- a/minimalproject/ProjectSettings/ProjectVersion.txt +++ b/minimalproject/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.0.61f1 -m_EditorVersionWithRevision: 6000.0.61f1 (74a0adb02c31) +m_EditorVersion: 6000.6.0b5 +m_EditorVersionWithRevision: 6000.6.0b5 (b5238eaafb35) diff --git a/pvpExceptions.json b/pvpExceptions.json index 83e996338b..e92be2a8a5 100644 --- a/pvpExceptions.json +++ b/pvpExceptions.json @@ -5,9 +5,18 @@ "CHANGELOG.md: line 9: Unreleased section is not allowed for public release" ] }, + "PVP-130-2": { + "errors": [ + "Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor.CodeGen", + "Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.PackageChecker.Editor", + "Editor/Unity.Netcode.Editor.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor", + "Tests/Editor/Unity.Netcode.Editor.Tests.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor.Tests" + ] + }, "PVP-132-2": { "errors": [ - "Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef: name of editor assembly should end with '.Editor'" + "Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef: name of editor assembly should end with '.Editor'", + "Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef: name of editor assembly should end with '.Editor'" ] } } diff --git a/testproject/Assets/AddressableAssetsData/AddressableAssetSettings.asset b/testproject/Assets/AddressableAssetsData/AddressableAssetSettings.asset index 6a1c39a006..69bec9b722 100644 --- a/testproject/Assets/AddressableAssetsData/AddressableAssetSettings.asset +++ b/testproject/Assets/AddressableAssetsData/AddressableAssetSettings.asset @@ -15,7 +15,8 @@ MonoBehaviour: m_DefaultGroup: 93aa504d1b753cb41a8a779ae63f5795 m_currentHash: serializedVersion: 2 - Hash: c105cac3e36a87a6dabc4f3755bc1fd9 + Hash: 00000000000000000000000000000000 + m_ExtractTypeTreeData: 0 m_OptimizeCatalogSize: 0 m_BuildRemoteCatalog: 0 m_CatalogRequestsTimeout: 0 @@ -33,6 +34,7 @@ MonoBehaviour: m_UniqueBundleIds: 0 m_EnableJsonCatalog: 0 m_NonRecursiveBuilding: 1 + m_AllowNestedBundleFolders: 0 m_CCDEnabled: 0 m_maxConcurrentWebRequests: 500 m_UseUWRForLocalBundles: 0 @@ -41,6 +43,7 @@ MonoBehaviour: m_BundleRedirectLimit: -1 m_SharedBundleSettings: 0 m_SharedBundleSettingsCustomGroupIndex: 0 + m_simulatedLoadDelay: 0.1 m_ContiguousBundles: 0 m_StripUnityVersionFromBundleBuild: 0 m_DisableVisibleSubAssetRepresentations: 0 @@ -70,32 +73,32 @@ MonoBehaviour: m_Id: b7ba5fc73af2a49449023b732cdf652d m_ProfileName: Default m_Values: - - m_Id: a2553e7788fb43741926b3128a0aa641 - m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' - - m_Id: 8bc200e556c0a2f4daf1a4696958eaa6 - m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + - m_Id: 2a3d80e942fdbfe49a979d4a22bbe893 + m_Value: 'http://localhost/[BuildTarget]' - m_Id: 80085c797452bb94ca0bf0a4b2ec258c m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8bc200e556c0a2f4daf1a4696958eaa6 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + - m_Id: a2553e7788fb43741926b3128a0aa641 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' - m_Id: eaae5cc67c56cfe4299363a742a284b3 m_Value: 'ServerData/[BuildTarget]' - - m_Id: 2a3d80e942fdbfe49a979d4a22bbe893 - m_Value: 'http://localhost/[BuildTarget]' m_ProfileEntryNames: - - m_Id: a2553e7788fb43741926b3128a0aa641 - m_Name: BuildTarget + - m_Id: 2a3d80e942fdbfe49a979d4a22bbe893 + m_Name: Remote.LoadPath + m_InlineUsage: 0 + - m_Id: 80085c797452bb94ca0bf0a4b2ec258c + m_Name: Local.LoadPath m_InlineUsage: 0 - m_Id: 8bc200e556c0a2f4daf1a4696958eaa6 m_Name: Local.BuildPath m_InlineUsage: 0 - - m_Id: 80085c797452bb94ca0bf0a4b2ec258c - m_Name: Local.LoadPath + - m_Id: a2553e7788fb43741926b3128a0aa641 + m_Name: BuildTarget m_InlineUsage: 0 - m_Id: eaae5cc67c56cfe4299363a742a284b3 m_Name: Remote.BuildPath m_InlineUsage: 0 - - m_Id: 2a3d80e942fdbfe49a979d4a22bbe893 - m_Name: Remote.LoadPath - m_InlineUsage: 0 m_ProfileVersion: 1 m_LabelTable: m_LabelNames: diff --git a/testproject/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/testproject/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset index 5d957b4c4e..36d70b4b1c 100644 --- a/testproject/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset +++ b/testproject/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -13,19 +13,16 @@ MonoBehaviour: m_Name: Default Local Group m_EditorClassIdentifier: m_GroupName: Default Local Group - m_Data: - m_SerializedData: [] m_GUID: 93aa504d1b753cb41a8a779ae63f5795 m_SerializeEntries: - m_GUID: ffa1ab8ed58b72343ad93116ded1700a m_Address: AddressableTestObject.prefab m_ReadOnly: 0 m_SerializedLabels: [] - m_MainAsset: {fileID: 0} - m_TargetAsset: {fileID: 0} + FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: 75e5cd8b6bfca5d49a5818e70d71b64d, type: 2} m_SchemaSet: m_Schemas: - - {fileID: 11400000, guid: 6b8a226e7c996ad4b841b997672f866c, type: 2} - {fileID: 11400000, guid: 07e6ff907f5779a42b6e632340d10c58, type: 2} + - {fileID: 11400000, guid: 6b8a226e7c996ad4b841b997672f866c, type: 2} diff --git a/testproject/Assets/NetCodeConfig.asset b/testproject/Assets/NetCodeConfig.asset new file mode 100644 index 0000000000..f6b1280970 --- /dev/null +++ b/testproject/Assets/NetCodeConfig.asset @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: abd30ee0214cf6a45b2d76765a4615b1, type: 3} + m_Name: NetCodeConfig + m_EditorClassIdentifier: Unity.NetCode::Unity.NetCode.NetCodeConfig + IsGlobalConfig: 1 + EnableClientServerBootstrap: 0 + HostWorldModeSelection: 1 + ClientServerTickRate: + SimulationTickRate: 30 + PredictedFixedStepSimulationTickRatio: 1 + NetworkTickRate: 0 + MaxSimulationStepsPerFrame: 1 + MaxSimulationStepBatchSize: 4 + TargetFrameRateMode: 0 + m_SendSnapshotsForCatchUpTicks: 0 + SnapshotAckMaskCapacity: 4096 + m_ClampPartialTicksThreshold: 5 + HandshakeApprovalTimeoutMS: 5000 + ClientTickRate: + InterpolationTimeNetTicks: 2 + InterpolationTimeMS: 0 + MaxExtrapolationTimeSimTicks: 20 + ForcedInputLatencyTicks: 0 + MaxPredictAheadTimeMS: 500 + NumAdditionalClientPredictedGhostLifetimeTicks: 0 + DefaultClassificationAllowableTickPeriod: 5 + TargetCommandSlack: 2 + NumAdditionalCommandsToSend: 2 + MaxPredictionStepBatchSizeRepeatedTick: 0 + MaxPredictionStepBatchSizeFirstTimeTick: 0 + PredictionLoopUpdateMode: 0 + InterpolationDelayJitterScale: 1.25 + InterpolationDelayMaxDeltaTicksFraction: 0.1 + InterpolationDelayCorrectionFraction: 0.1 + InterpolationTimeScaleMin: 0.85 + InterpolationTimeScaleMax: 1.1 + CommandAgeCorrectionFraction: 0.1 + PredictionTimeScaleMin: 0.9 + PredictionTimeScaleMax: 1.1 + GhostSendSystemData: + DefaultSnapshotPacketSize: 0 + PercentReservedForDespawnMessages: 0.33 + MinSendImportance: 0 + MinDistanceScaledSendImportance: 0 + MaxIterateChunks: 0 + MaxSendChunks: 0 + MaxSendEntities: 0 + m_ForceSingleBaseline: 0 + m_ForcePreSerialize: 0 + m_KeepSnapshotHistoryOnStructuralChange: 1 + m_EnablePerComponentProfiling: 0 + CleanupConnectionStatePerTick: 1 + m_FirstSendImportanceMultiplier: 1 + m_IrrelevantImportanceDownScale: 1 + m_TempStreamSize: 8192 + m_UseCustomSerializer: 0 + ConnectTimeoutMS: 1000 + MaxConnectAttempts: 60 + DisconnectTimeoutMS: 30000 + HeartbeatTimeoutMS: 500 + ReconnectionTimeoutMS: 2000 + ClientSendQueueCapacity: 64 + ClientReceiveQueueCapacity: 64 + ServerSendQueueCapacity: 512 + ServerReceiveQueueCapacity: 512 + MaxMessageSize: 1400 diff --git a/testproject/Assets/NetCodeConfig.asset.meta b/testproject/Assets/NetCodeConfig.asset.meta new file mode 100644 index 0000000000..222ccfadf4 --- /dev/null +++ b/testproject/Assets/NetCodeConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c547acbddd81d32a0ba5e62ddfc4f4e3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Prefabs/AddressableTestObject.prefab b/testproject/Assets/Prefabs/AddressableTestObject.prefab index 473b199037..2930d6b0f3 100644 --- a/testproject/Assets/Prefabs/AddressableTestObject.prefab +++ b/testproject/Assets/Prefabs/AddressableTestObject.prefab @@ -9,8 +9,8 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 6175900562428407388} - - component: {fileID: 6175900562428407389} - component: {fileID: 6175900562428407386} + - component: {fileID: 6175900562428407389} m_Layer: 0 m_Name: AddressableTestObject m_TagString: Untagged @@ -25,14 +25,15 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6175900562428407387} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &6175900562428407389 +--- !u!114 &6175900562428407386 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -41,12 +42,24 @@ MonoBehaviour: m_GameObject: {fileID: 6175900562428407387} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 6c8e5a8a448245769b69af7de9c47b21, type: 3} + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: - AnIntVal: 1234567 - AStringVal: 1234567 ---- !u!114 &6175900562428407386 + GlobalObjectIdHash: 4054942115 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 1 + AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + k__BackingField: 0 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 1 + SpawnWithObservers: 1 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 +--- !u!114 &6175900562428407389 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -55,10 +68,9 @@ MonoBehaviour: m_GameObject: {fileID: 6175900562428407387} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Script: {fileID: 11500000, guid: 6c8e5a8a448245769b69af7de9c47b21, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 951099334 - AlwaysReplicateAsRoot: 0 - DontDestroyWithOwner: 0 - AutoObjectParentSync: 1 + ShowTopMostFoldoutHeaderGroup: 1 + AnIntVal: 1234567 + AStringVal: 1234567 diff --git a/testproject/Assets/Scenes/AddressableInSceneObject.unity b/testproject/Assets/Scenes/AddressableInSceneObject.unity new file mode 100644 index 0000000000..e9fea46774 --- /dev/null +++ b/testproject/Assets/Scenes/AddressableInSceneObject.unity @@ -0,0 +1,199 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1001 &1879396995 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 6175900562428407386, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: GlobalObjectIdHash + value: 1346653293 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407387, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_Name + value: AddressableTestObject + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalPosition.x + value: 1.09747 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalPosition.z + value: 1.59747 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6175900562428407388, guid: ffa1ab8ed58b72343ad93116ded1700a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: ffa1ab8ed58b72343ad93116ded1700a, type: 3} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1879396995} diff --git a/testproject/Assets/Scenes/AddressableInSceneObject.unity.meta b/testproject/Assets/Scenes/AddressableInSceneObject.unity.meta new file mode 100644 index 0000000000..dbdfd8aca5 --- /dev/null +++ b/testproject/Assets/Scenes/AddressableInSceneObject.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 68d21678646384e6291bb2b568b5d95c +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: addressablebundle + assetBundleVariant: diff --git a/testproject/Assets/Scenes/MultiprocessTestScene.unity b/testproject/Assets/Scenes/MultiprocessTestScene.unity deleted file mode 100644 index f67263407e..0000000000 --- a/testproject/Assets/Scenes/MultiprocessTestScene.unity +++ /dev/null @@ -1,1062 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!29 &1 -OcclusionCullingSettings: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_OcclusionBakeSettings: - smallestOccluder: 5 - smallestHole: 0.25 - backfaceThreshold: 100 - m_SceneGUID: 00000000000000000000000000000000 - m_OcclusionCullingData: {fileID: 0} ---- !u!104 &2 -RenderSettings: - m_ObjectHideFlags: 0 - serializedVersion: 10 - m_Fog: 0 - m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} - m_FogMode: 3 - m_FogDensity: 0.01 - m_LinearFogStart: 0 - m_LinearFogEnd: 300 - m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} - m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} - m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} - m_AmbientIntensity: 1 - m_AmbientMode: 0 - m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} - m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} - m_HaloStrength: 0.5 - m_FlareStrength: 1 - m_FlareFadeSpeed: 3 - m_HaloTexture: {fileID: 0} - m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} - m_DefaultReflectionMode: 0 - m_DefaultReflectionResolution: 128 - m_ReflectionBounces: 1 - m_ReflectionIntensity: 1 - m_CustomReflection: {fileID: 0} - m_Sun: {fileID: 0} - m_UseRadianceAmbientProbe: 0 ---- !u!157 &3 -LightmapSettings: - m_ObjectHideFlags: 0 - serializedVersion: 13 - m_BakeOnSceneLoad: 0 - m_GISettings: - serializedVersion: 2 - m_BounceScale: 1 - m_IndirectOutputScale: 1 - m_AlbedoBoost: 1 - m_EnvironmentLightingMode: 0 - m_EnableBakedLightmaps: 1 - m_EnableRealtimeLightmaps: 0 - m_LightmapEditorSettings: - serializedVersion: 12 - m_Resolution: 2 - m_BakeResolution: 40 - m_AtlasSize: 1024 - m_AO: 0 - m_AOMaxDistance: 1 - m_CompAOExponent: 1 - m_CompAOExponentDirect: 0 - m_ExtractAmbientOcclusion: 0 - m_Padding: 2 - m_LightmapParameters: {fileID: 0} - m_LightmapsBakeMode: 1 - m_TextureCompression: 1 - m_ReflectionCompression: 2 - m_MixedBakeMode: 2 - m_BakeBackend: 1 - m_PVRSampling: 1 - m_PVRDirectSampleCount: 32 - m_PVRSampleCount: 512 - m_PVRBounces: 2 - m_PVREnvironmentSampleCount: 256 - m_PVREnvironmentReferencePointCount: 2048 - m_PVRFilteringMode: 1 - m_PVRDenoiserTypeDirect: 1 - m_PVRDenoiserTypeIndirect: 1 - m_PVRDenoiserTypeAO: 1 - m_PVRFilterTypeDirect: 0 - m_PVRFilterTypeIndirect: 0 - m_PVRFilterTypeAO: 0 - m_PVREnvironmentMIS: 1 - m_PVRCulling: 1 - m_PVRFilteringGaussRadiusDirect: 1 - m_PVRFilteringGaussRadiusIndirect: 5 - m_PVRFilteringGaussRadiusAO: 2 - m_PVRFilteringAtrousPositionSigmaDirect: 0.5 - m_PVRFilteringAtrousPositionSigmaIndirect: 2 - m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ExportTrainingData: 0 - m_TrainingDataDestination: TrainingData - m_LightProbeSampleCountMultiplier: 4 - m_LightingDataAsset: {fileID: 0} - m_LightingSettings: {fileID: 130932425} ---- !u!196 &4 -NavMeshSettings: - serializedVersion: 2 - m_ObjectHideFlags: 0 - m_BuildSettings: - serializedVersion: 3 - agentTypeID: 0 - agentRadius: 0.5 - agentHeight: 2 - agentSlope: 45 - agentClimb: 0.4 - ledgeDropHeight: 0 - maxJumpAcrossDistance: 0 - minRegionArea: 2 - manualCellSize: 0 - cellSize: 0.16666667 - manualTileSize: 0 - tileSize: 256 - buildHeightMesh: 0 - maxJobWorkers: 0 - preserveTilesOutsideBounds: 0 - debug: - m_Flags: 0 - m_NavMeshData: {fileID: 0} ---- !u!1 &127222500 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 127222502} - - component: {fileID: 127222501} - m_Layer: 0 - m_Name: Directional Light - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!108 &127222501 -Light: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 127222500} - m_Enabled: 1 - serializedVersion: 11 - m_Type: 1 - m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} - m_Intensity: 1 - m_Range: 10 - m_SpotAngle: 30 - m_InnerSpotAngle: 21.80208 - m_CookieSize: 10 - m_Shadows: - m_Type: 2 - m_Resolution: -1 - m_CustomResolution: -1 - m_Strength: 1 - m_Bias: 0.05 - m_NormalBias: 0.4 - m_NearPlane: 0.2 - m_CullingMatrixOverride: - e00: 1 - e01: 0 - e02: 0 - e03: 0 - e10: 0 - e11: 1 - e12: 0 - e13: 0 - e20: 0 - e21: 0 - e22: 1 - e23: 0 - e30: 0 - e31: 0 - e32: 0 - e33: 1 - m_UseCullingMatrixOverride: 0 - m_Cookie: {fileID: 0} - m_DrawHalo: 0 - m_Flare: {fileID: 0} - m_RenderMode: 0 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingLayerMask: 1 - m_Lightmapping: 4 - m_LightShadowCasterMode: 0 - m_AreaSize: {x: 1, y: 1} - m_BounceIntensity: 1 - m_ColorTemperature: 6570 - m_UseColorTemperature: 0 - m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} - m_UseBoundingSphereOverride: 0 - m_UseViewFrustumForShadowCasterCull: 1 - m_ForceVisible: 0 - m_ShadowRadius: 0 - m_ShadowAngle: 0 - m_LightUnit: 1 - m_LuxAtDistance: 1 - m_EnableSpotReflector: 1 ---- !u!4 &127222502 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 127222500} - serializedVersion: 2 - m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} - m_LocalPosition: {x: 0, y: 3, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} ---- !u!850595691 &130932425 -LightingSettings: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: - serializedVersion: 9 - m_EnableBakedLightmaps: 0 - m_EnableRealtimeLightmaps: 0 - m_RealtimeEnvironmentLighting: 1 - m_BounceScale: 1 - m_AlbedoBoost: 1 - m_IndirectOutputScale: 1 - m_UsingShadowmask: 1 - m_BakeBackend: 1 - m_LightmapMaxSize: 1024 - m_LightmapSizeFixed: 0 - m_UseMipmapLimits: 1 - m_BakeResolution: 40 - m_Padding: 2 - m_LightmapCompression: 3 - m_AO: 0 - m_AOMaxDistance: 1 - m_CompAOExponent: 1 - m_CompAOExponentDirect: 0 - m_ExtractAO: 0 - m_MixedBakeMode: 2 - m_LightmapsBakeMode: 1 - m_FilterMode: 1 - m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} - m_ExportTrainingData: 0 - m_EnableWorkerProcessBaking: 1 - m_TrainingDataDestination: TrainingData - m_RealtimeResolution: 2 - m_ForceWhiteAlbedo: 0 - m_ForceUpdates: 0 - m_PVRCulling: 1 - m_PVRSampling: 1 - m_PVRDirectSampleCount: 32 - m_PVRSampleCount: 512 - m_PVREnvironmentSampleCount: 256 - m_PVREnvironmentReferencePointCount: 2048 - m_LightProbeSampleCountMultiplier: 4 - m_PVRBounces: 2 - m_PVRMinBounces: 2 - m_PVREnvironmentImportanceSampling: 1 - m_PVRFilteringMode: 1 - m_PVRDenoiserTypeDirect: 1 - m_PVRDenoiserTypeIndirect: 1 - m_PVRDenoiserTypeAO: 1 - m_PVRFilterTypeDirect: 0 - m_PVRFilterTypeIndirect: 0 - m_PVRFilterTypeAO: 0 - m_PVRFilteringGaussRadiusDirect: 1 - m_PVRFilteringGaussRadiusIndirect: 5 - m_PVRFilteringGaussRadiusAO: 2 - m_PVRFilteringAtrousPositionSigmaDirect: 0.5 - m_PVRFilteringAtrousPositionSigmaIndirect: 2 - m_PVRFilteringAtrousPositionSigmaAO: 1 - m_RespectSceneVisibilityWhenBakingGI: 0 ---- !u!1 &160940364 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 160940368} - - component: {fileID: 160940367} - - component: {fileID: 160940366} - - component: {fileID: 160940365} - m_Layer: 0 - m_Name: Boundary bottom left - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!135 &160940365 -SphereCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 160940364} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Radius: 0.5 - m_Center: {x: 0, y: 0, z: 0} ---- !u!23 &160940366 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 160940364} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!33 &160940367 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 160940364} - m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &160940368 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 160940364} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -10, y: -10, z: -10} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &430011403 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 430011407} - - component: {fileID: 430011406} - - component: {fileID: 430011405} - - component: {fileID: 430011404} - m_Layer: 0 - m_Name: ThreeDText - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &430011404 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 430011403} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 04cf3cc1396054b009a1ed283aa50021, type: 3} - m_Name: - m_EditorClassIdentifier: - IsTestCoordinatorActiveAndEnabled: 0 - CommandLineArguments: ---- !u!102 &430011405 -TextMesh: - serializedVersion: 3 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 430011403} - m_Text: Hello World - m_OffsetZ: 0 - m_CharacterSize: 1 - m_LineSpacing: 1 - m_Anchor: 0 - m_Alignment: 0 - m_TabSize: 4 - m_FontSize: 0 - m_FontStyle: 0 - m_RichText: 1 - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_Color: - serializedVersion: 2 - rgba: 4294967295 ---- !u!23 &430011406 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 430011403} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!4 &430011407 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 430011403} - serializedVersion: 2 - m_LocalRotation: {x: 0.2164396, y: 0, z: 0, w: 0.97629607} - m_LocalPosition: {x: -45, y: 10, z: 10} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 25, y: 0, z: 0} ---- !u!1 &941021721 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 941021724} - - component: {fileID: 941021723} - - component: {fileID: 941021722} - m_Layer: 0 - m_Name: Main Camera - m_TagString: MainCamera - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!81 &941021722 -AudioListener: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 941021721} - m_Enabled: 1 ---- !u!20 &941021723 -Camera: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 941021721} - m_Enabled: 1 - serializedVersion: 2 - m_ClearFlags: 1 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} - m_projectionMatrixMode: 1 - m_GateFitMode: 2 - m_FOVAxisMode: 0 - m_Iso: 200 - m_ShutterSpeed: 0.005 - m_Aperture: 16 - m_FocusDistance: 10 - m_FocalLength: 50 - m_BladeCount: 5 - m_Curvature: {x: 2, y: 11} - m_BarrelClipping: 0.25 - m_Anamorphism: 0 - m_SensorSize: {x: 36, y: 24} - m_LensShift: {x: 0, y: 0} - m_NormalizedViewPortRect: - serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 - near clip plane: 0.3 - far clip plane: 1000 - field of view: 60 - orthographic: 0 - orthographic size: 5 - m_Depth: -1 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingPath: -1 - m_TargetTexture: {fileID: 0} - m_TargetDisplay: 0 - m_TargetEye: 3 - m_HDR: 1 - m_AllowMSAA: 1 - m_AllowDynamicResolution: 0 - m_ForceIntoRT: 0 - m_OcclusionCulling: 1 - m_StereoConvergence: 10 - m_StereoSeparation: 0.022 ---- !u!4 &941021724 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 941021721} - serializedVersion: 2 - m_LocalRotation: {x: 0.21736304, y: -0, z: -0, w: 0.97609085} - m_LocalPosition: {x: 0, y: 9.15, z: -27.5} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 25.108, y: 0, z: 0} ---- !u!1 &996484657 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 996484661} - - component: {fileID: 996484660} - - component: {fileID: 996484659} - - component: {fileID: 996484658} - m_Layer: 0 - m_Name: Boundary center - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!135 &996484658 -SphereCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 996484657} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Radius: 0.5 - m_Center: {x: 0, y: 0, z: 0} ---- !u!23 &996484659 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 996484657} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!33 &996484660 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 996484657} - m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &996484661 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 996484657} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1206022453 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1206022457} - - component: {fileID: 1206022456} - - component: {fileID: 1206022455} - - component: {fileID: 1206022454} - m_Layer: 0 - m_Name: Boundary top right - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!135 &1206022454 -SphereCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1206022453} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Radius: 0.5 - m_Center: {x: 0, y: 0, z: 0} ---- !u!23 &1206022455 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1206022453} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RayTracingAccelStructBuildFlagsOverride: 0 - m_RayTracingAccelStructBuildFlags: 1 - m_SmallMeshCulling: 1 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!33 &1206022456 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1206022453} - m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &1206022457 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1206022453} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 10, y: 10, z: 10} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1211923374 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1211923376} - - component: {fileID: 1211923375} - - component: {fileID: 1211923378} - - component: {fileID: 1211923377} - m_Layer: 0 - m_Name: '[NetworkManager] (Multiprocess)' - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1211923375 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1211923374} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3} - m_Name: - m_EditorClassIdentifier: - NetworkManagerExpanded: 0 - NetworkConfig: - ProtocolVersion: 0 - NetworkTransport: {fileID: 2027640073} - PlayerPrefab: {fileID: 4700706668509470175, guid: 7eeaaf9e50c0afc4dab93584a54fb0d6, - type: 3} - Prefabs: - NetworkPrefabsLists: [] - TickRate: 30 - ClientConnectionBufferTimeout: 10 - ConnectionApproval: 0 - ConnectionData: - EnableTimeResync: 0 - TimeResyncInterval: 30 - EnsureNetworkVariableLengthSafety: 0 - EnableSceneManagement: 1 - ForceSamePrefabs: 1 - RecycleNetworkIds: 1 - NetworkIdRecycleDelay: 120 - RpcHashSize: 0 - LoadSceneTimeOut: 120 - SpawnTimeout: 10 - EnableNetworkLogs: 1 - NetworkTopology: 0 - UseCMBService: 0 - AutoSpawnPlayerPrefabClientSide: 1 - NetworkProfilingMetrics: 1 - OldPrefabList: - - Override: 0 - Prefab: {fileID: 9115731988109684252, guid: c2851feb7276442cc86a6f2d1d69ea11, - type: 3} - SourcePrefabToOverride: {fileID: 0} - SourceHashToOverride: 0 - OverridingTargetPrefab: {fileID: 0} - RunInBackground: 1 - LogLevel: 1 ---- !u!4 &1211923376 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1211923374} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 2027640072} - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &1211923377 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1211923374} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 068bf11ceb1344667af4cc40950f44f4, type: 3} - m_Name: - m_EditorClassIdentifier: - ReferencedPrefab: {fileID: 9115731988109684252, guid: c2851feb7276442cc86a6f2d1d69ea11, - type: 3} ---- !u!114 &1211923378 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1211923374} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 55d1c75ce242745ac98f7e7aca6d2d19, type: 3} - m_Name: - m_EditorClassIdentifier: ---- !u!1 &1274245423 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1274245425} - - component: {fileID: 1274245424} - - component: {fileID: 1274245426} - m_Layer: 0 - m_Name: TestCoordinator - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1274245424 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1274245423} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} - m_Name: - m_EditorClassIdentifier: - GlobalObjectIdHash: 2217825759 - InScenePlacedSourceGlobalObjectIdHash: 0 - DeferredDespawnTick: 0 - Ownership: 1 - AlwaysReplicateAsRoot: 0 - SynchronizeTransform: 1 - ActiveSceneSynchronization: 0 - SceneMigrationSynchronization: 1 - SpawnWithObservers: 1 - DontDestroyWithOwner: 0 - AutoObjectParentSync: 1 - SyncOwnerTransformWhenParented: 1 - AllowOwnerToParent: 0 ---- !u!4 &1274245425 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1274245423} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 10, y: 10, z: 10} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &1274245426 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1274245423} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: ef1240e0784f84eadb77fe822e2e03c7, type: 3} - m_Name: - m_EditorClassIdentifier: ---- !u!1 &2027640071 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 2027640072} - - component: {fileID: 2027640073} - m_Layer: 0 - m_Name: UTP - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &2027640072 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2027640071} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 1211923376} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &2027640073 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2027640071} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3} - m_Name: - m_EditorClassIdentifier: - m_ProtocolType: 0 - m_UseWebSockets: 0 - m_UseEncryption: 0 - m_MaxPacketQueueSize: 128 - m_MaxPayloadSize: 4096 - m_HeartbeatTimeoutMS: 500 - m_ConnectTimeoutMS: 1000 - m_MaxConnectAttempts: 60 - m_DisconnectTimeoutMS: 30000 - ConnectionData: - Address: 127.0.0.1 - Port: 7777 - ServerListenAddress: 127.0.0.1 - DebugSimulator: - PacketDelayMS: 0 - PacketJitterMS: 0 - PacketDropRate: 0 ---- !u!1660057539 &9223372036854775807 -SceneRoots: - m_ObjectHideFlags: 0 - m_Roots: - - {fileID: 941021724} - - {fileID: 127222502} - - {fileID: 1211923376} - - {fileID: 1206022457} - - {fileID: 996484661} - - {fileID: 160940368} - - {fileID: 1274245425} - - {fileID: 430011407} diff --git a/testproject/Assets/Scripts/MoveDynamicSpawnInAwake.cs b/testproject/Assets/Scripts/MoveDynamicSpawnInAwake.cs new file mode 100644 index 0000000000..303bf20a62 --- /dev/null +++ b/testproject/Assets/Scripts/MoveDynamicSpawnInAwake.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using Unity.Netcode; +using UnityEngine; +using UnityEngine.SceneManagement; + +#if UNITY_6000_6_OR_NEWER && UNITY_EDITOR +[Unity.Scripting.LifecycleManagement.AutoStaticsCleanup] +#endif +public partial class MoveDynamicSpawnInAwake : MonoBehaviour +{ + public NetworkObject MovedObject { get; private set; } + private static readonly HashSet k_AlreadyMovedObjects = new(); + private void Awake() + { + var networkManager = NetworkManager.Singleton; + if (networkManager == null || !networkManager.IsListening) + { + return; + } + + foreach (var spawnedObject in networkManager.SpawnManager.SpawnedObjects.Values) + { + if (spawnedObject == null || !spawnedObject.IsSpawned || !spawnedObject.HasAuthority || spawnedObject.IsPlayerObject || spawnedObject.InScenePlaced) + { + continue; + } + + if (!k_AlreadyMovedObjects.Add(spawnedObject)) + { + continue; + } + + MovedObject = spawnedObject; + SceneManager.MoveGameObjectToScene(spawnedObject.gameObject, gameObject.scene); + return; + } + } +} diff --git a/testproject/Assets/Scripts/MoveDynamicSpawnInAwake.cs.meta b/testproject/Assets/Scripts/MoveDynamicSpawnInAwake.cs.meta new file mode 100644 index 0000000000..053fb00eb0 --- /dev/null +++ b/testproject/Assets/Scripts/MoveDynamicSpawnInAwake.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 566f22ce0b192499d8728df60af83423 \ No newline at end of file diff --git a/testproject/Assets/Scripts/testproject.asmdef b/testproject/Assets/Scripts/testproject.asmdef index 942bddd1f0..34b7976d61 100644 --- a/testproject/Assets/Scripts/testproject.asmdef +++ b/testproject/Assets/Scripts/testproject.asmdef @@ -3,7 +3,7 @@ "rootNamespace": "", "references": [ "Unity.Netcode.Runtime", - "Unity.Netcode.Editor", + "Unity.Netcode.GameObjects.Editor", "Unity.Netcode.Components", "Unity.Netcode.Adapter.UTP", "Unity.Services.Authentication", diff --git a/testproject/Assets/Tests/Editor/TestProject.Editor.Tests.asmdef b/testproject/Assets/Tests/Editor/TestProject.Editor.Tests.asmdef index 51aa4685de..28f8ebe4c2 100644 --- a/testproject/Assets/Tests/Editor/TestProject.Editor.Tests.asmdef +++ b/testproject/Assets/Tests/Editor/TestProject.Editor.Tests.asmdef @@ -3,7 +3,7 @@ "rootNamespace": "TestProject.EditorTests", "references": [ "Unity.Netcode.Runtime", - "Unity.Netcode.Editor", + "Unity.Netcode.GameObjects.Editor", "UnityEngine.TestRunner", "UnityEditor.TestRunner" ], @@ -22,4 +22,4 @@ ], "versionDefines": [], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs b/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs index 0ab1566882..4cb30cb930 100644 --- a/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs +++ b/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs @@ -9,9 +9,12 @@ namespace TestProject.ManualTests /// public class ObjectToNotDestroyBehaviour : NetworkBehaviour { + public static bool VerboseDebug; + private bool m_ContinueSendingPing; private uint m_PingCounter; + public uint CurrentPing { get @@ -20,12 +23,19 @@ public uint CurrentPing } } - /// - /// When enabled, we move ourself to the DontDestroyOnLoad scene - /// - private void OnEnable() + private void Log(string msg) + { + if (VerboseDebug) + { + Debug.Log(msg); + } + } + + // Migrate into DDOL during pre-spawn + protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) { DontDestroyOnLoad(this); + base.OnNetworkPreSpawn(ref networkManager); } /// @@ -38,11 +48,11 @@ private void PingUpdateClientRpc(uint pingNumber) { if (IsHost) { - Debug.Log($"Sent ping number ({pingNumber})."); + Log($"Sent ping number ({pingNumber})."); } else if (IsClient) { - Debug.Log($"Receiving ping number ({pingNumber}) from server"); + Log($"Receiving ping number ({pingNumber}) from server"); m_PingCounter = pingNumber; } } diff --git a/testproject/Assets/Tests/Manual/IntegrationTestScenes/EmptyScene3.unity.meta b/testproject/Assets/Tests/Manual/IntegrationTestScenes/EmptyScene3.unity.meta index 46669dd13d..88bb22b242 100644 --- a/testproject/Assets/Tests/Manual/IntegrationTestScenes/EmptyScene3.unity.meta +++ b/testproject/Assets/Tests/Manual/IntegrationTestScenes/EmptyScene3.unity.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 -guid: abd4c8b51c445d54faa16c67ac973f1b +guid: bfb4addb64cf6455a88668eba2f759ba DefaultImporter: externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity b/testproject/Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity new file mode 100644 index 0000000000..1cfda7fc2d --- /dev/null +++ b/testproject/Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity @@ -0,0 +1,198 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1633685737 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1633685739} + - component: {fileID: 1633685738} + - component: {fileID: 1633685740} + m_Layer: 0 + m_Name: InSceneObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1633685738 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1633685737} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 974939462 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 1 + AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + m_InScenePlaced: 1 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 0 + SpawnWithObservers: 1 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 +--- !u!4 &1633685739 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1633685737} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1633685740 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1633685737} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 112072ebb4fab6341bfee4bd9d7e58da, type: 3} + m_Name: + m_EditorClassIdentifier: TestProject.ManualTests::TestProject.ManualTests.MoveInScenePlacedToDDOL + ShowTopMostFoldoutHeaderGroup: 1 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1633685739} diff --git a/testproject/Assets/Scenes/MultiprocessTestScene.unity.meta b/testproject/Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity.meta similarity index 74% rename from testproject/Assets/Scenes/MultiprocessTestScene.unity.meta rename to testproject/Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity.meta index 5a9d45d780..dfa51863fd 100644 --- a/testproject/Assets/Scenes/MultiprocessTestScene.unity.meta +++ b/testproject/Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 76743cb7b342c49279327834918a9c6e +guid: 4c20c17b4f92e634f8796b0460851d49 DefaultImporter: externalObjects: {} userData: diff --git a/testproject/Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity b/testproject/Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity new file mode 100644 index 0000000000..f5b385433f --- /dev/null +++ b/testproject/Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity @@ -0,0 +1,170 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 10 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1786247463 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1786247465} + - component: {fileID: 1786247464} + m_Layer: 0 + m_Name: MoveDynamicSpawnInAwake + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1786247464 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1786247463} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 566f22ce0b192499d8728df60af83423, type: 3} + m_Name: + m_EditorClassIdentifier: TestProject::MoveDynamicSpawnInAwake +--- !u!4 &1786247465 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1786247463} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.09747, y: 0, z: 1.59747} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1786247465} diff --git a/testproject/Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity.meta b/testproject/Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity.meta new file mode 100644 index 0000000000..46669dd13d --- /dev/null +++ b/testproject/Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: abd4c8b51c445d54faa16c67ac973f1b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs index f731d6fe95..efaceb4a38 100644 --- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs @@ -45,20 +45,11 @@ public void PlayerIsMoving(float movementDirection) } } - private Transform GetRootParentTransform(Transform transform) - { - if (transform.parent != null) - { - return GetRootParentTransform(transform.parent); - } - return transform; - } - protected override void OnNetworkPostSpawn() { if (CanCommitToTransform) { - m_RootParentTransform = GetRootParentTransform(transform); + m_RootParentTransform = transform.root; if (RandomizeScale) { transform.localScale = transform.localScale * Random.Range(0.5f, 1.5f); diff --git a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity index bab567858b..e1dfeb1a4d 100644 --- a/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity +++ b/testproject/Assets/Tests/Manual/SceneTransitioningAdditive/SceneTransitioningBase1.unity @@ -246,6 +246,7 @@ MonoBehaviour: Ownership: 1 AlwaysReplicateAsRoot: 0 SynchronizeTransform: 1 + k__BackingField: 0 ActiveSceneSynchronization: 0 SceneMigrationSynchronization: 1 SpawnWithObservers: 1 @@ -278,14 +279,14 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 37242881} m_Enabled: 1 - serializedVersion: 11 + serializedVersion: 13 m_Type: 1 m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} m_Intensity: 1 m_Range: 10 m_SpotAngle: 30 m_InnerSpotAngle: 21.80208 - m_CookieSize: 10 + m_CookieSize2D: {x: 10, y: 10} m_Shadows: m_Type: 2 m_Resolution: -1 @@ -330,7 +331,7 @@ Light: m_UseBoundingSphereOverride: 0 m_UseViewFrustumForShadowCasterCull: 1 m_ForceVisible: 0 - m_ShadowRadius: 0 + m_ShapeRadius: 0 m_ShadowAngle: 0 m_LightUnit: 1 m_LuxAtDistance: 1 @@ -646,6 +647,8 @@ MeshRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -667,9 +670,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &57392473 MeshFilter: @@ -979,6 +984,7 @@ Canvas: m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 0 + m_UseReflectionProbes: 0 m_AdditionalShaderChannelsFlag: 0 m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 @@ -1157,6 +1163,7 @@ Canvas: m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 0 + m_UseReflectionProbes: 0 m_AdditionalShaderChannelsFlag: 0 m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 @@ -1408,6 +1415,8 @@ MeshRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -1429,9 +1438,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &336568648 MeshFilter: @@ -2549,7 +2560,7 @@ LightingSettings: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: - serializedVersion: 9 + serializedVersion: 10 m_EnableBakedLightmaps: 1 m_EnableRealtimeLightmaps: 0 m_RealtimeEnvironmentLighting: 1 @@ -2564,6 +2575,12 @@ LightingSettings: m_BakeResolution: 40 m_Padding: 2 m_LightmapCompression: 3 + m_LightmapPackingMode: 1 + m_LightmapPackingMethod: 0 + m_XAtlasPackingAttempts: 16384 + m_XAtlasBruteForce: 0 + m_XAtlasBlockAlign: 0 + m_XAtlasRepackUnderutilizedLightmaps: 1 m_AO: 0 m_AOMaxDistance: 1 m_CompAOExponent: 1 @@ -2876,11 +2893,12 @@ MonoBehaviour: NetworkIdRecycleDelay: 120 RpcHashSize: 0 LoadSceneTimeOut: 120 - SpawnTimeout: 1 + SpawnTimeout: 10 EnableNetworkLogs: 1 NetworkTopology: 0 UseCMBService: 0 AutoSpawnPlayerPrefabClientSide: 1 + NetworkMessageMetrics: 1 NetworkProfilingMetrics: 1 OldPrefabList: - Override: 0 @@ -2934,7 +2952,7 @@ MonoBehaviour: SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} RunInBackground: 1 - LogLevel: 1 + LogLevel: 0 --- !u!4 &1024114720 Transform: m_ObjectHideFlags: 0 @@ -3000,7 +3018,9 @@ MonoBehaviour: ConnectionData: Address: 127.0.0.1 Port: 7777 - ServerListenAddress: + WebSocketPath: / + ServerListenAddress: 127.0.0.1 + ClientBindPort: 0 DebugSimulator: PacketDelayMS: 0 PacketJitterMS: 0 @@ -3088,6 +3108,7 @@ MonoBehaviour: Ownership: 1 AlwaysReplicateAsRoot: 0 SynchronizeTransform: 1 + k__BackingField: 0 ActiveSceneSynchronization: 0 SceneMigrationSynchronization: 1 SpawnWithObservers: 1 @@ -3399,6 +3420,8 @@ MeshRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -3420,9 +3443,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &1336081254 MeshFilter: @@ -5318,6 +5343,8 @@ MeshRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5339,9 +5366,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &1857685346 MeshFilter: @@ -5765,6 +5794,8 @@ MeshRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5786,9 +5817,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &2028091271 MeshFilter: @@ -6134,6 +6167,7 @@ MonoBehaviour: Ownership: 1 AlwaysReplicateAsRoot: 0 SynchronizeTransform: 1 + k__BackingField: 0 ActiveSceneSynchronization: 0 SceneMigrationSynchronization: 1 SpawnWithObservers: 1 diff --git a/testproject/Assets/Tests/Manual/Scripts/MoveInScenePlacedToDDOL.cs b/testproject/Assets/Tests/Manual/Scripts/MoveInScenePlacedToDDOL.cs new file mode 100644 index 0000000000..0e9fa457a9 --- /dev/null +++ b/testproject/Assets/Tests/Manual/Scripts/MoveInScenePlacedToDDOL.cs @@ -0,0 +1,34 @@ +using Unity.Netcode; +using UnityEngine; + +namespace TestProject.ManualTests +{ + public class MoveInScenePlacedToDDOL : NetworkBehaviour + { + public bool ProcessedRpc { get; private set; } + + private void Awake() + { + ProcessedRpc = false; + var networkObject = GetComponent(); + Debug.Log($"[{name}][Moving to DDOL] InScenePlaced: {networkObject.InScenePlaced}"); + DontDestroyOnLoad(gameObject); + } + + protected override void OnNetworkPostSpawn() + { + if (HasAuthority) + { + SendOnSpawnRpc(); + } + + base.OnNetworkPostSpawn(); + } + + [Rpc(SendTo.Everyone)] + private void SendOnSpawnRpc(RpcParams rpcParams = default) + { + ProcessedRpc = true; + } + } +} diff --git a/testproject/Assets/Tests/Manual/Scripts/MoveInScenePlacedToDDOL.cs.meta b/testproject/Assets/Tests/Manual/Scripts/MoveInScenePlacedToDDOL.cs.meta new file mode 100644 index 0000000000..739e4ca2d4 --- /dev/null +++ b/testproject/Assets/Tests/Manual/Scripts/MoveInScenePlacedToDDOL.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 112072ebb4fab6341bfee4bd9d7e58da \ No newline at end of file diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs index 7821d2af27..9f441b8f58 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPool.cs @@ -496,8 +496,8 @@ public void UpdateSpawnsPerSecond() { StartSpawningBoxes(); } - else //Handle case where spawning coroutine is running but we set our spawn rate to zero - if (SpawnsPerSecond == 0 && m_IsSpawningObjects) + //Handle case where spawning coroutine is running but we set our spawn rate to zero + else if (SpawnsPerSecond == 0 && m_IsSpawningObjects) { m_IsSpawningObjects = false; StopCoroutine(SpawnObjects()); diff --git a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs index f714d937de..44decc3dce 100644 --- a/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs +++ b/testproject/Assets/Tests/Manual/Scripts/NetworkPrefabPoolAdditive.cs @@ -342,8 +342,8 @@ public void UpdateSpawnsPerSecond() { StartSpawningBoxes(); } - else //Handle case where spawning coroutine is running but we set our spawn rate to zero - if (SpawnsPerSecond == 0 && m_IsSpawningObjects) + //Handle case where spawning coroutine is running but we set our spawn rate to zero + else if (SpawnsPerSecond == 0 && m_IsSpawningObjects) { m_IsSpawningObjects = false; InternalStopCoroutine(); diff --git a/testproject/Assets/Tests/Runtime/AddressablesTests.cs b/testproject/Assets/Tests/Runtime/AddressablesTests.cs index 4c9151c0f3..d81837ecea 100644 --- a/testproject/Assets/Tests/Runtime/AddressablesTests.cs +++ b/testproject/Assets/Tests/Runtime/AddressablesTests.cs @@ -7,6 +7,8 @@ using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.SceneManagement; using UnityEngine.TestTools; using Assert = UnityEngine.Assertions.Assert; @@ -15,6 +17,10 @@ namespace TestProject.RuntimeTests [TestFixture(HostOrServer.DAHost)] [TestFixture(HostOrServer.Host)] [TestFixture(HostOrServer.Server)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] + [TestFixture(HostOrServer.UnifiedServer)] +#endif public class AddressablesTests : NetcodeIntegrationTest { protected override int NumberOfClients => 2; @@ -24,6 +30,7 @@ public class AddressablesTests : NetcodeIntegrationTest protected override bool m_TearDownIsACoroutine => false; private const string k_ValidObject = "AddressableTestObject.prefab"; + private const string k_ValidScene = "Assets/Scenes/AddressableInSceneObject.unity"; public AddressablesTests(HostOrServer hostOrServer) { @@ -56,6 +63,24 @@ private IEnumerator LoadAsset(AssetReferenceGameObject asset, NetcodeIntegration prefab.Result = handle.Result; } + private IEnumerator LoadSceneWithInSceneObject(AssetReference asset, NetcodeIntegrationTestHelpers.ResultWrapper prefab) + { + var handle = Addressables.LoadSceneAsync(asset, LoadSceneMode.Additive); + while (!handle.IsDone) + { + var nextFrameNumber = Time.frameCount + 1; + yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); + } + + Assert.AreEqual(AsyncOperationStatus.Succeeded, handle.Status, "Addressables.LoadSceneAsync failed!"); + + foreach (var networkObject in FindObjects.FromSceneByType(handle.Result.Scene, false)) + { + prefab.Result = networkObject.gameObject; + break; + } + } + protected void StartWithAddressableAssetAdded() { StartServerAndClientsWithTimeTravel(); @@ -70,7 +95,7 @@ private void AddPrefab(GameObject prefab) } } - private void SpawnAndValidate(GameObject prefab, bool waitAndAddOnClient = false) + private void SpawnAndValidate(GameObject prefab, bool waitAndAddOnClient = false, bool wasLoadedFromScene = false) { // Have to spawn it ourselves. var serverObj = Object.Instantiate(prefab); @@ -80,7 +105,14 @@ private void SpawnAndValidate(GameObject prefab, bool waitAndAddOnClient = false // Prefabs loaded by addressables actually don't show up in this search. // Unlike other tests that make prefabs programmatically, those aren't added to the scene until they're instantiated - Assert.AreEqual(1, objs.Length); + var numExpected = 1; + if (wasLoadedFromScene) + { + // If prefab was loaded from the scene, there'll be an additional object found + numExpected++; + } + + Assert.AreEqual(numExpected, objs.Length); var startTime = MockTimeProvider.StaticRealTimeSinceStartup; @@ -91,7 +123,7 @@ private void SpawnAndValidate(GameObject prefab, bool waitAndAddOnClient = false // Since it's not added, after the CreateObjectMessage is received, it's not spawned yet // Verify that to be the case as a precondition. objs = FindObjects.ByType(); - Assert.AreEqual(1, objs.Length); + Assert.AreEqual(numExpected, objs.Length); WaitForConditionOrTimeOutWithTimeTravel(() => MockTimeProvider.StaticRealTimeSinceStartup - startTime >= m_ClientNetworkManagers[0].NetworkConfig.SpawnTimeout - 0.25); foreach (var client in m_ClientNetworkManagers) { @@ -100,12 +132,22 @@ private void SpawnAndValidate(GameObject prefab, bool waitAndAddOnClient = false } objs = FindObjects.ByType(); - Assert.AreEqual(NumberOfClients + 1, objs.Length); + Assert.AreEqual(NumberOfClients + numExpected, objs.Length); foreach (var obj in objs) { Assert.AreEqual(1234567, obj.AnIntVal); Assert.AreEqual("1234567", obj.AStringVal); Assert.AreEqual("12345671234567", obj.GetValue()); + + // TODO-[MTT-15388]: Object spawned from a scene should be InScenePlaced after this ticket + if (obj.IsSpawned) + { + Assert.IsFalse(obj.NetworkObject.InScenePlaced, "Object was dynamically spawned and should be marked as such!"); + } + else + { + Assert.IsTrue(obj.NetworkObject.InScenePlaced, "Object that was loaded from scene should have been marked as in-scene placed during loading!"); + } } } @@ -144,7 +186,7 @@ public IEnumerator WhenLoadingAValidObjectAfterStarting_SpawningItSucceedsOnServ } [UnityTest] - public IEnumerator WhenSpawningServerPrefabBeforeClientPrefabHasLoaded_SpawningItSucceedsOnServerAndClientAfterConfiguredDelay([Values(1, 2, 3)] int timeout) + public IEnumerator WhenSpawningServerPrefabBeforeClientPrefabHasLoaded_SpawningItSucceedsOnServerAndClientAfterDelay() { var asset = new AssetReferenceGameObject(k_ValidObject); @@ -152,7 +194,7 @@ public IEnumerator WhenSpawningServerPrefabBeforeClientPrefabHasLoaded_SpawningI m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false; foreach (var client in m_ClientNetworkManagers) { - client.NetworkConfig.SpawnTimeout = timeout; + client.NetworkConfig.SpawnTimeout = 3; client.NetworkConfig.ForceSamePrefabs = false; } @@ -163,6 +205,32 @@ public IEnumerator WhenSpawningServerPrefabBeforeClientPrefabHasLoaded_SpawningI SpawnAndValidate(prefabResult.Result, true); } + + // TODO-[MTT-15388]: Reconsider whether this test should be valid + // Reported on Github issue https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues/4049 + //[UnityTest] + //public IEnumerator RegisteringPrefabFromLoadedAddressablesSceneWorks() + //{ + // var asset = new AssetReference(k_ValidScene); + + // CreateServerAndClients(); + // foreach (var manager in m_NetworkManagers) + // { + // manager.NetworkConfig.ForceSamePrefabs = false; + // } + + // StartServerAndClientsWithTimeTravel(); + + // var prefabResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); + // yield return LoadSceneWithInSceneObject(asset, prefabResult); + + // foreach (var manager in m_NetworkManagers) + // { + // manager.AddNetworkPrefab(prefabResult.Result); + // } + + // SpawnAndValidate(prefabResult.Result, wasLoadedFromScene: true); + //} } } #endif diff --git a/testproject/Assets/Tests/Runtime/AnalyticsTests.cs b/testproject/Assets/Tests/Runtime/AnalyticsTests.cs index a841333900..975a01ab7b 100644 --- a/testproject/Assets/Tests/Runtime/AnalyticsTests.cs +++ b/testproject/Assets/Tests/Runtime/AnalyticsTests.cs @@ -2,7 +2,7 @@ using System.Collections; using NUnit.Framework; using Unity.Netcode; -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine.TestTools; @@ -22,7 +22,7 @@ internal class AnalyticsTests : NetcodeIntegrationTest protected override IEnumerator OnSetup() { NetcodeAnalytics.EnableIntegrationTestAnalytics = true; - m_NetcodeAnalytics = Unity.Netcode.Editor.NetworkManagerHelper.Singleton.NetcodeAnalytics; + m_NetcodeAnalytics = Unity.Netcode.GameObjects.Editor.NetworkManagerHelper.Singleton.NetcodeAnalytics; yield return base.OnSetup(); } diff --git a/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs b/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs index 474c9dabbe..d685fb68ca 100644 --- a/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs +++ b/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs @@ -3,9 +3,11 @@ using System.Linq; using NUnit.Framework; using Unity.Netcode; +using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; using UnityEngine.TestTools; +using static Unity.Netcode.Components.NetworkAnimator; namespace TestProject.RuntimeTests @@ -336,7 +338,40 @@ public void ParameterExcludedTests() VerboseDebug($" ------------------ Parameter Test [{m_OwnerShipMode}] Stopping ------------------ "); } + private unsafe void MockWritingParameters(ref FastBufferWriter writer) + { + writer.Seek(0); + writer.Truncate(); + + // Write out how many parameter entries to read + BytePacker.WriteValuePacked(writer, (uint)1); + // Write an invalid index level (anything would be invalid with no parameters) + BytePacker.WriteValuePacked(writer, (uint)1000); + // Write some value for the invalid parameter + BytePacker.WriteValuePacked(writer, (uint)10); + } + + [Test] + public void ParameterBoundsCheck() + { + var gameObject = new GameObject(); + gameObject.AddComponent(); + var networkAnimator = gameObject.AddComponent(); + + var writer = new FastBufferWriter(40, Unity.Collections.Allocator.TempJob); + // Mock a parameter update with an invalid index + MockWritingParameters(ref writer); + + var invalidParameters = new ParametersUpdateMessage() + { + Parameters = writer.ToArray() + }; + // Expect an error message + LogAssert.Expect(LogType.Error, new System.Text.RegularExpressions.Regex($"parameters. Ignoring the remainger of this {nameof(ParametersUpdateMessage)}!")); + // Pass in the invalid ParametersUpdateMessage + networkAnimator.UpdateParameters(ref invalidParameters); + } private bool AllTriggersDetected(OwnerShipMode ownerShipMode) { @@ -527,8 +562,7 @@ private bool AllObserversSameLayerWeight(OwnerShipMode ownerShipMode, int layer, return false; } } - else - if (animatorTestHelper.Value.GetLayerWeight(layer) != targetWeight) + else if (animatorTestHelper.Value.GetLayerWeight(layer) != targetWeight) { return false; } diff --git a/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs b/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs index 105265b08f..2d841049ca 100644 --- a/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs +++ b/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs @@ -11,6 +11,9 @@ namespace TestProject.RuntimeTests { [TestFixture(HostOrServer.Host)] [TestFixture(HostOrServer.DAHost)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] +#endif public class DontDestroyOnLoadTests : NetcodeIntegrationTest { private const int k_ClientsToConnect = 4; diff --git a/testproject/Assets/Tests/Runtime/NetworkBehaviourSessionSynchronized.cs b/testproject/Assets/Tests/Runtime/NetworkBehaviourSessionSynchronized.cs index 771a90b9b8..0582211d0f 100644 --- a/testproject/Assets/Tests/Runtime/NetworkBehaviourSessionSynchronized.cs +++ b/testproject/Assets/Tests/Runtime/NetworkBehaviourSessionSynchronized.cs @@ -9,6 +9,9 @@ namespace TestProject.RuntimeTests { [TestFixture(HostOrServer.Host)] [TestFixture(HostOrServer.DAHost)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] +#endif public class NetworkBehaviourSessionSynchronized : NetcodeIntegrationTest { private const string k_SceneToLoad = "SessionSynchronize"; diff --git a/testproject/Assets/Tests/Runtime/NetworkManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkManagerTests.cs index 411d7e6fec..ccf46cb61c 100644 --- a/testproject/Assets/Tests/Runtime/NetworkManagerTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkManagerTests.cs @@ -155,7 +155,7 @@ public IEnumerator ValidateShutdown([Values] ShutdownChecks shutdownCheck) for (int i = spawnedObjects.Count - 1; i >= 0; i--) { var spawnedObject = spawnedObjects[i]; - if (spawnedObject.IsSceneObject != null && spawnedObject.IsSceneObject.Value) + if (spawnedObject.InScenePlaced) { spawnedObject.Despawn(); } diff --git a/testproject/Assets/Tests/Runtime/NetworkObjectDestroyWithSceneTests.cs b/testproject/Assets/Tests/Runtime/NetworkObjectDestroyWithSceneTests.cs index ad5fe5e1b0..57a2660601 100644 --- a/testproject/Assets/Tests/Runtime/NetworkObjectDestroyWithSceneTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkObjectDestroyWithSceneTests.cs @@ -10,6 +10,9 @@ namespace TestProject.RuntimeTests { [TestFixture(HostOrServer.DAHost)] [TestFixture(HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] +#endif internal class NetworkObjectDestroyWithSceneTests : NetcodeIntegrationTest { private const string k_SceneToLoad = "EmptyScene"; @@ -98,7 +101,7 @@ public IEnumerator DestroyWithScene() // Depending on network topology, spawn the object with the appropriate owner. var owner = m_DistributedAuthority ? m_NotSessionOwner : m_SessionOwner; - m_SpawnedInstance = SpawnObject(m_TestPrefab.gameObject, m_NotSessionOwner, true).GetComponent(); + m_SpawnedInstance = SpawnObject(m_TestPrefab.gameObject, owner, true).GetComponent(); var instanceName = m_SpawnedInstance.name; yield return WaitForConditionOrTimeOut(() => ObjectSpawnedOnAllNetworkManagers(true)); diff --git a/testproject/Assets/Tests/Runtime/NetworkObjectSpawning.cs b/testproject/Assets/Tests/Runtime/NetworkObjectSpawning.cs index 6ed56b04ed..1b1f0e8c32 100644 --- a/testproject/Assets/Tests/Runtime/NetworkObjectSpawning.cs +++ b/testproject/Assets/Tests/Runtime/NetworkObjectSpawning.cs @@ -9,8 +9,11 @@ namespace TestProject.RuntimeTests { - [TestFixture(NetworkTopologyTypes.ClientServer)] - [TestFixture(NetworkTopologyTypes.DistributedAuthority)] + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.UnifiedHost)] +#endif + [TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)] internal class NetworkObjectSpawning : NetcodeIntegrationTest { private const string k_SceneToLoad = "NetworkObjectSpawnerTest"; @@ -27,7 +30,7 @@ protected override bool UseCMBService() return false; } - public NetworkObjectSpawning(NetworkTopologyTypes networkTopology) : base(networkTopology) { } + public NetworkObjectSpawning(NetworkTopologyTypes networkTopology, HostOrServer hostOrServer) : base(networkTopology, hostOrServer) { } protected override IEnumerator OnSetup() diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/AttachableBehaviourSceneLoadTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/AttachableBehaviourSceneLoadTests.cs index 58d056d8d0..a3e31983e1 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/AttachableBehaviourSceneLoadTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/AttachableBehaviourSceneLoadTests.cs @@ -318,7 +318,7 @@ public IEnumerator AttachedUponSceneTransition([Values] bool detachOnDespawn) var persists = m_Persists == Persists.AttachableNode ? m_TargetInstance.NetworkObjectId : m_SourceInstance.NetworkObjectId; m_DoesNotPersistNetworkObjectIds.Add(doesNotPersist); persistedObjects.Add(networkManager, persists); - Debug.Log($"[{networkManager.name}] Spawned attachable and attached it."); + VerboseDebug($"[{networkManager.name}] Spawned attachable and attached it."); } // This is the actual validation point where the scene is unloaded and either the attachable or diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationModeTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationModeTests.cs index f69bec2d12..0da0940b00 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationModeTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationModeTests.cs @@ -50,6 +50,8 @@ public enum ActiveSceneStates private Dictionary> m_ClientScenesLoaded = new Dictionary>(); + private readonly List m_ObjectsInScenes = new(); + public ClientSynchronizationModeTests(ServerPreloadStates serverPreloadStates) { @@ -66,9 +68,9 @@ protected override IEnumerator OnSetup() { m_TempClientPreLoadedScenes.Clear(); m_ServerLoadedScenes.Clear(); + m_ObjectsInScenes.Clear(); if (m_ServerPreloadState == ServerPreloadStates.PreloadOnServer) { - SceneManager.sceneLoaded += SceneManager_sceneLoaded; yield return LoadScenesOnServer(); } yield return base.OnSetup(); @@ -78,6 +80,7 @@ private IEnumerator LoadScenesOnServer() { if (m_ServerPreloadState == ServerPreloadStates.PreloadOnServer) { + SceneManager.sceneLoaded += SceneManager_sceneLoaded; foreach (var sceneToLoad in m_TestScenes) { SceneManager.LoadSceneAsync(sceneToLoad, LoadSceneMode.Additive); @@ -88,12 +91,16 @@ private IEnumerator LoadScenesOnServer() } else { + m_ServerNetworkManager.SceneManager.OnSceneEvent += ServerSide_OnSceneEvent; + foreach (var sceneToLoad in m_TestScenes) { m_ServerNetworkManager.SceneManager.LoadScene(sceneToLoad, LoadSceneMode.Additive); yield return WaitForConditionOrTimeOut(() => SceneLoadedOnServer(sceneToLoad)); AssertOnTimeout($"[{m_ServerPreloadState}] Timed out waiting for scene {sceneToLoad} to be loaded!"); } + m_ServerNetworkManager.SceneManager.OnSceneEvent -= ServerSide_OnSceneEvent; + } } @@ -165,9 +172,21 @@ private bool AllScenesLoadedOnClients() return true; } + private void TrackObjectsInScene(Scene scene) + { + foreach (var networkObject in FindObjects.FromSceneByType(scene, true)) + { + if (networkObject.InScenePlaced) + { + m_ObjectsInScenes.Add(networkObject); + } + } + } + private void SceneManager_sceneLoaded(Scene scene, LoadSceneMode loadSceneMode) { m_ServerLoadedScenes.Add(scene); + TrackObjectsInScene(scene); } @@ -203,7 +222,6 @@ public IEnumerator PreloadedScenesTest([Values] ClientPreloadStates clientPreloa // If we didn't preload the scenes, then load the scenes via NetworkSceneManager if (m_ServerPreloadState == ServerPreloadStates.NoPreloadOnServer) { - m_ServerNetworkManager.SceneManager.OnSceneEvent += ServerSide_OnSceneEvent; yield return LoadScenesOnServer(); } @@ -215,6 +233,18 @@ public IEnumerator PreloadedScenesTest([Values] ClientPreloadStates clientPreloa SceneManager.SetActiveScene(m_ServerLoadedScenes[2]); } + var authority = GetAuthorityNetworkManager(); + + foreach (var networkObject in m_ObjectsInScenes) + { + Assert.IsTrue(networkObject.IsSpawned, $"[Client-{authority.LocalClientId}] Server object {networkObject.name}, {networkObject.GlobalObjectIdHash} is not spawned!"); + Assert.AreEqual(networkObject.NetworkManagerOwner, authority, $"[Client-{authority.LocalClientId}] networkObject doesn't belong to the client"); + } + + var objToDisable = m_ObjectsInScenes[0]; + objToDisable.Despawn(false); + + // Late join some clients for (int i = 0; i < 1; i++) { @@ -223,6 +253,7 @@ public IEnumerator PreloadedScenesTest([Values] ClientPreloadStates clientPreloa if (clientPreloadStates == ClientPreloadStates.PreloadOnClient) { m_TempClientPreLoadedScenes.Clear(); + m_ObjectsInScenes.Clear(); SceneManager.sceneLoaded += PreLoadClient_SceneLoaded; foreach (var sceneToLoad in m_TestScenes) { @@ -232,17 +263,37 @@ public IEnumerator PreloadedScenesTest([Values] ClientPreloadStates clientPreloa SceneManager.sceneLoaded -= PreLoadClient_SceneLoaded; AssertOnTimeout($"[{clientPreloadStates}] Timed out waiting for client-side scenes to be preloaded!"); } - yield return CreateAndStartNewClient(); + + var newClient = CreateNewClient(); + yield return StartClient(newClient); AssertOnTimeout($"[Client Instance {i + 1}] Timed out waiting for client to start and connect!"); yield return WaitForConditionOrTimeOut(AllScenesLoadedOnClients); - AssertOnTimeout($"[Client-{m_ClientNetworkManagers[i].LocalClientId}] Timed out waiting for all scenes to be synchronized for new client!"); + AssertOnTimeout($"[Client-{newClient.LocalClientId}] Timed out waiting for all scenes to be synchronized for new client!"); + + if (clientPreloadStates == ClientPreloadStates.PreloadOnClient) + { + foreach (var networkObject in m_ObjectsInScenes) + { + if (networkObject.GlobalObjectIdHash == objToDisable.GlobalObjectIdHash) + { + Assert.IsFalse(networkObject.IsSpawned); + } + else + { + Assert.IsTrue(networkObject.IsSpawned, $"[Client-{newClient.LocalClientId}] Client side preloaded object {networkObject.name}, {networkObject.GlobalObjectIdHash} is not spawned!"); + } + Assert.AreEqual(networkObject.NetworkManagerOwner, newClient, $"[Client-{newClient.LocalClientId}] networkObject doesn't belong to the client"); + } + } } } + private void PreLoadClient_SceneLoaded(Scene scene, LoadSceneMode loadSceneMode) { m_TempClientPreLoadedScenes.Add(scene); + TrackObjectsInScene(scene); } private void ClientSide_OnSceneEvent(SceneEvent sceneEvent) @@ -273,6 +324,7 @@ private void ServerSide_OnSceneEvent(SceneEvent sceneEvent) case SceneEventType.LoadComplete: { m_ServerLoadedScenes.Add(sceneEvent.Scene); + TrackObjectsInScene(sceneEvent.Scene); break; } } @@ -283,6 +335,7 @@ protected override IEnumerator OnTearDown() SceneManager.sceneLoaded -= SceneManager_sceneLoaded; m_TempClientPreLoadedScenes.Clear(); m_ClientScenesLoaded.Clear(); + m_ObjectsInScenes.Clear(); return base.OnTearDown(); } } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationValidationTest.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationValidationTest.cs index 32f88e9221..ea5dfc47ad 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationValidationTest.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/ClientSynchronizationValidationTest.cs @@ -110,7 +110,7 @@ public IEnumerator ClientVerifySceneBeforeLoading([Values] bool startClientBefor foreach (var spawnedObjectEntry in m_ServerNetworkManager.SpawnManager.SpawnedObjects) { var networkObject = spawnedObjectEntry.Value; - if (!networkObject.IsSceneObject.Value) + if (!networkObject.InScenePlaced) { continue; } @@ -138,7 +138,7 @@ public IEnumerator ClientVerifySceneBeforeLoading([Values] bool startClientBefor foreach (var spawnedObjectEntry in m_ServerNetworkManager.SpawnManager.SpawnedObjects) { var networkObject = spawnedObjectEntry.Value; - if (!networkObject.IsSceneObject.Value) + if (!networkObject.InScenePlaced) { continue; } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InScenePlacedProcessorTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InScenePlacedProcessorTests.cs new file mode 100644 index 0000000000..807ff09eeb --- /dev/null +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InScenePlacedProcessorTests.cs @@ -0,0 +1,75 @@ +using System.Collections; +using NUnit.Framework; +using Unity.Netcode; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; + +namespace TestProject.RuntimeTests +{ + internal class InScenePlacedProcessorTests : NetcodeIntegrationTest + { + protected override int NumberOfClients => 0; + + private static readonly string k_DynamicObjectMoverScene = "MoveADynamicObjectInAwake"; + + private GameObject m_DynamicSpawnPrefab; + + protected override void OnServerAndClientsCreated() + { + m_DynamicSpawnPrefab = CreateNetworkObjectPrefab("DynamicSpawnObject"); + base.OnServerAndClientsCreated(); + } + + [UnityTest] + public IEnumerator InScenePlacedProcessorSkipsMovedObject() + { + var authority = GetAuthorityNetworkManager(); + var spawnedObj = SpawnObject(m_DynamicSpawnPrefab, authority).GetComponent(); + + yield return WaitForSpawnedOnAllOrTimeOut(spawnedObj); + AssertOnTimeout("Timed out waiting for object to spawn!"); + + Assert.IsFalse(spawnedObj.InScenePlaced, "Dynamically spawned object should not be InScenePlaced!"); + + authority.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; + var status = authority.SceneManager.LoadScene(k_DynamicObjectMoverScene, LoadSceneMode.Additive); + Assert.True(status == SceneEventProgressStatus.Started, $"Failed to start lading scene {k_DynamicObjectMoverScene} with status {status}!"); + yield return WaitForConditionOrTimeOut(() => m_SceneLoaded.IsValid()); + AssertOnTimeout("Timed out waiting for scene to load!"); + + var movers = FindObjects.ByType(); + foreach (var mover in movers) + { + var movedObject = mover.MovedObject; + Assert.IsFalse(movedObject == null); + Assert.IsFalse(movedObject.InScenePlaced, "Dynamically spawned object should not be re-processed as InScenePlaced!"); + Assert.AreEqual(mover.gameObject.scene, movedObject.gameObject.scene, "Object should have moved scenes!"); + } + + yield return CreateAndStartNewClient(); + AssertOnTimeout("Timed out waiting for late joining client!"); + + yield return WaitForSpawnedOnAllOrTimeOut(spawnedObj); + AssertOnTimeout("Timed out waiting for object to be spawned on late joining client!"); + } + + private Scene m_SceneLoaded; + private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) + { + var authority = GetAuthorityNetworkManager(); + switch (sceneEvent.SceneEventType) + { + case SceneEventType.LoadComplete: + { + if (sceneEvent.ClientId == authority.LocalClientId) + { + m_SceneLoaded = sceneEvent.Scene; + } + return; + } + } + } + } +} diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InScenePlacedProcessorTests.cs.meta b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InScenePlacedProcessorTests.cs.meta new file mode 100644 index 0000000000..3d0a9439ec --- /dev/null +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObject/InScenePlacedProcessorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9abce9656f174f768488bb052afa8d92 +timeCreated: 1783723861 \ No newline at end of file diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs index d5eb5f427c..736de24acf 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs @@ -1,6 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; using NUnit.Framework; using Unity.Netcode; using Unity.Netcode.TestHelpers.Runtime; @@ -13,27 +16,30 @@ namespace TestProject.RuntimeTests { /// /// NetworkObject Scene Migration Integration Tests - /// - /// /// - public class NetworkObjectSceneMigrationTests : NetcodeIntegrationTest + [TestFixture(HostOrServer.Host)] + [TestFixture(HostOrServer.DAHost)] + internal class NetworkObjectSceneMigrationTests : NetcodeIntegrationTest { - private List m_TestScenes = new List() { "EmptyScene1", "EmptyScene2", "EmptyScene3" }; + private readonly List m_TestScenes = new() { "EmptyScene1", "EmptyScene2", "EmptyScene3" }; protected override int NumberOfClients => 2; + private GameObject m_TestPrefab; private GameObject m_TestPrefabAutoSynchActiveScene; private GameObject m_TestPrefabDestroyWithScene; private Scene m_OriginalActiveScene; - private bool m_ClientsLoadedScene; - private bool m_ClientsUnloadedScene; - private Scene m_SceneLoaded; private List m_ServerSpawnedPrefabInstances = new List(); private List m_ServerSpawnedDestroyWithSceneInstances = new List(); - private List m_ScenesLoaded = new List(); - private string m_CurrentSceneLoading; - private string m_CurrentSceneUnloading; + private readonly List m_ScenesLoaded = new List(); + public NetworkObjectSceneMigrationTests(HostOrServer hostOrServer) : base(hostOrServer) { } + + // TODO: [MTT-15430] Fix automatic scene object migration between clients + protected override bool UseCMBService() + { + return false; + } protected override IEnumerator OnSetup() { @@ -41,106 +47,70 @@ protected override IEnumerator OnSetup() return base.OnSetup(); } + protected override void OnCreatePlayerPrefab() + { + Object.DontDestroyOnLoad(m_PlayerPrefab); + m_PlayerPrefab.GetComponent().ActiveSceneSynchronization = true; + base.OnCreatePlayerPrefab(); + } + protected override void OnServerAndClientsCreated() { // Synchronize Scene Changes (default) Test Network Prefab m_TestPrefab = CreateNetworkObjectPrefab("TestObject"); + m_TestPrefab.AddComponent(); // Auto Synchronize Active Scene Changes Test Network Prefab - m_TestPrefabAutoSynchActiveScene = CreateNetworkObjectPrefab("ASASObject"); + m_TestPrefabAutoSynchActiveScene = CreateNetworkObjectPrefab("ActiveSceneSynchronizationObject"); m_TestPrefabAutoSynchActiveScene.GetComponent().ActiveSceneSynchronization = true; + m_TestPrefabAutoSynchActiveScene.AddComponent(); // Destroy With Scene Test Network Prefab - m_TestPrefabDestroyWithScene = CreateNetworkObjectPrefab("DWSObject"); + m_TestPrefabDestroyWithScene = CreateNetworkObjectPrefab("DestroyWithSceneObject"); m_TestPrefabDestroyWithScene.AddComponent(); + m_TestPrefabDestroyWithScene.AddComponent(); - DestroyWithSceneInstancesTestHelper.ShouldNeverSpawn = m_TestPrefabDestroyWithScene; + var neverSpawnObj = CreateNetworkObjectPrefab("ShouldNeverSpawn"); + var shouldNeverSpawn = neverSpawnObj.AddComponent(); + DestroyWithSceneInstancesTestHelper.ShouldNeverSpawn = shouldNeverSpawn; + + var authority = GetAuthorityNetworkManager(); + authority.OnServerStarted += OnServerStarted; base.OnServerAndClientsCreated(); } - private bool DidClientsSpawnInstance(NetworkObject serverObject, bool checkDestroyWithScene = false) - { - foreach (var networkManager in m_ClientNetworkManagers) - { - if (!s_GlobalNetworkObjects.ContainsKey(networkManager.LocalClientId)) - { - return false; - } - var clientNetworkObjects = s_GlobalNetworkObjects[networkManager.LocalClientId]; - if (!clientNetworkObjects.ContainsKey(serverObject.NetworkObjectId)) - { - return false; - } - - if (checkDestroyWithScene) - { - if (serverObject.DestroyWithScene != clientNetworkObjects[serverObject.NetworkObjectId]) - { - return false; - } - } - } - return true; - } - private bool VerifyAllClientsSpawnedInstances() + private void OnServerStarted() { - foreach (var serverObject in m_ServerSpawnedPrefabInstances) - { - if (!DidClientsSpawnInstance(serverObject)) - { - return false; - } - } - - foreach (var serverObject in m_ServerSpawnedDestroyWithSceneInstances) - { - if (!DidClientsSpawnInstance(serverObject, true)) - { - return false; - } - } - - return true; + var authority = GetAuthorityNetworkManager(); + authority.OnServerStarted -= OnServerStarted; + authority.SceneManager.ActiveSceneSynchronizationEnabled = true; } - private bool AreClientInstancesInTheRightScene(NetworkObject serverObject) + private bool VerifyAllScenesMatch(StringBuilder errorLog, List authorityInstances) { - foreach (var networkManager in m_ClientNetworkManagers) + foreach (var authorityInstance in authorityInstances) { - var clientNetworkObjects = s_GlobalNetworkObjects[networkManager.LocalClientId]; - if (clientNetworkObjects == null) - { - continue; - } - // If a networkObject is null then it was destroyed - if (clientNetworkObjects[serverObject.NetworkObjectId].gameObject.scene.name != serverObject.gameObject.scene.name) + foreach (var networkManager in m_NetworkManagers) { - return false; - } - } - return true; - } + if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(authorityInstance.NetworkObjectId, out var instance)) + { + errorLog.AppendLine($"[{authorityInstance.name}] Client-{networkManager.LocalClientId} doesn't have a local version of network object {authorityInstance.name} with id {authorityInstance.NetworkObjectId}"); + return false; + } - private bool VerifySpawnedObjectsMigrated() - { - foreach (var serverObject in m_ServerSpawnedPrefabInstances) - { - if (!AreClientInstancesInTheRightScene(serverObject)) - { - return false; - } - } + if (instance.gameObject.scene.name != authorityInstance.gameObject.scene.name) + { + errorLog.AppendLine($"[{instance.name}] NetworkObject-{authorityInstance.NetworkObjectId} is in the wrong scene! Expected: {authorityInstance.gameObject.scene.name}, Actual: {instance.gameObject.scene.name}"); + return false; + } - foreach (var serverObject in m_ServerSpawnedDestroyWithSceneInstances) - { - if (!AreClientInstancesInTheRightScene(serverObject)) - { - return false; + // The SceneOrigin should never change + var originalSceneTracker = instance.GetComponent(); + Assert.AreEqual(originalSceneTracker.SceneWhereAwakeHappened, (NetworkSceneHandle)instance.SceneOrigin.handle, "The SceneOrigin of an object should never change!"); } } - return true; } @@ -154,28 +124,31 @@ private bool VerifySpawnedObjectsMigrated() [UnityTest] public IEnumerator MigrateIntoNewSceneTest() { + var authority = GetAuthorityNetworkManager(); + + var authoritySpawnedInstances = new List(); // Spawn 9 NetworkObject instances for (int i = 0; i < k_MaxObjectsToSpawn; i++) { - var serverInstance = Object.Instantiate(m_TestPrefab); - var serverNetworkObject = serverInstance.GetComponent(); - serverNetworkObject.Spawn(); - m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); + var instance = SpawnObject(m_TestPrefab, authority); + var spawnedObject = instance.GetComponent(); + authoritySpawnedInstances.Add(spawnedObject); } - yield return WaitForConditionOrTimeOut(VerifyAllClientsSpawnedInstances); + + yield return WaitForSpawnedOnAllOrTimeOut(authoritySpawnedInstances); AssertOnTimeout($"Timed out waiting for all clients to spawn {nameof(NetworkObject)}s!"); // Now load three scenes to migrate the newly spawned NetworkObjects into - m_ServerNetworkManager.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; - for (int i = 0; i < 3; i++) + authority.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; + foreach (var sceneToLoad in m_TestScenes) { - m_ClientsLoadedScene = false; - m_CurrentSceneLoading = m_TestScenes[i]; - var status = m_ServerNetworkManager.SceneManager.LoadScene(m_TestScenes[i], LoadSceneMode.Additive); - Assert.True(status == SceneEventProgressStatus.Started, $"Failed to start loading scene {m_CurrentSceneLoading}! Return status: {status}"); - yield return WaitForConditionOrTimeOut(() => m_ClientsLoadedScene); - AssertOnTimeout($"Timed out waiting for all clients to load scene {m_CurrentSceneLoading}!"); + yield return LoadScene(authority, sceneToLoad); } + authority.SceneManager.OnSceneEvent -= SceneManager_OnSceneEvent; + Assert.AreEqual(m_TestScenes.Count, m_ScenesLoaded.Count, "Not all the test scenes were loaded!"); + + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); + AssertOnTimeout("[After spawn] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); var objectCount = 0; // Migrate each networkObject into one of the three scenes. @@ -183,31 +156,32 @@ public IEnumerator MigrateIntoNewSceneTest() foreach (var scene in m_ScenesLoaded) { // Now migrate the NetworkObject - SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount].gameObject, scene); - SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount + 1].gameObject, scene); - SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[objectCount + 2].gameObject, scene); + SceneManager.MoveGameObjectToScene(authoritySpawnedInstances[objectCount].gameObject, scene); + SceneManager.MoveGameObjectToScene(authoritySpawnedInstances[objectCount + 1].gameObject, scene); + SceneManager.MoveGameObjectToScene(authoritySpawnedInstances[objectCount + 2].gameObject, scene); objectCount += 3; } - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // Register for the server-side client synchronization so we can send an object scene migration event at the same time // the new client begins to synchronize - m_ServerNetworkManager.SceneManager.OnSynchronize += MigrateObjects_OnSynchronize; + m_ServerSpawnedPrefabInstances = authoritySpawnedInstances; + authority.SceneManager.OnSynchronize += MigrateObjects_OnSynchronize; // Verify that a late joining client synchronizes properly even while new scene migrations occur // during its synchronization yield return CreateAndStartNewClient(); - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"[Late Joined Client] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // Verify that a late joining client synchronizes properly even if we migrate // during its synchronization and despawn some of the NetworkObjects migrated. - m_ServerNetworkManager.SceneManager.OnSynchronize += MigrateAndDespawnObjects_OnSynchronize; + authority.SceneManager.OnSynchronize += MigrateAndDespawnObjects_OnSynchronize; yield return CreateAndStartNewClient(); - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"[Late Joined Client] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); } @@ -219,19 +193,27 @@ public IEnumerator MigrateIntoNewSceneTest() /// private void MigrateAndDespawnObjects_OnSynchronize(ulong clientId) { - var objectCount = 0; + var authority = GetAuthorityNetworkManager(); // Migrate the NetworkObjects into different scenes than they originally were migrated into for (int i = m_ServerSpawnedPrefabInstances.Count - 1; i >= 0; i--) { var scene = m_ScenesLoaded[i % m_ScenesLoaded.Count]; - SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[i].gameObject, scene); + var obj = m_ServerSpawnedPrefabInstances[i]; + if (m_DistributedAuthority) + { + // When the new client joins, authority will be distributed. + // Ensure we have the authority instance. + obj = GetAuthorityInstance(obj); + } + SceneManager.MoveGameObjectToScene(obj.gameObject, scene); // De-spawn every-other object if (i % 2 == 0) { - m_ServerSpawnedPrefabInstances[objectCount + i].Despawn(); + obj.Despawn(); m_ServerSpawnedPrefabInstances.RemoveAt(i); } } + authority.SceneManager.OnSynchronize -= MigrateObjects_OnSynchronize; } /// @@ -252,7 +234,24 @@ private void MigrateObjects_OnSynchronize(ulong clientId) } // Unsubscribe to this event for this part of the test - m_ServerNetworkManager.SceneManager.OnSynchronize -= MigrateObjects_OnSynchronize; + GetAuthorityNetworkManager().SceneManager.OnSynchronize -= MigrateObjects_OnSynchronize; + } + + protected override void OnNewClientCreated(NetworkManager networkManager) + { + var authority = GetAuthorityNetworkManager(); + foreach (var prefab in authority.NetworkConfig.Prefabs.Prefabs) + { + networkManager.NetworkConfig.Prefabs.Add(prefab); + } + networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab; + base.OnNewClientCreated(networkManager); + } + + private void SetActiveScene(Scene scene) + { + Debug.Log($"[Previous = {SceneManager.GetActiveScene().name}][New = {scene.name}] Changing the active scene!"); + SceneManager.SetActiveScene(scene); } /// @@ -263,36 +262,47 @@ private void MigrateObjects_OnSynchronize(ulong clientId) [UnityTest] public IEnumerator ActiveSceneSynchronizationTest() { + var authority = GetAuthorityNetworkManager(); // Disable resynchronization for this test to avoid issues with trying // to synchronize them. NetworkSceneManager.DisableReSynchronization = true; + // Load three scenes first + authority.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; + foreach (var sceneName in m_TestScenes) + { + yield return LoadScene(authority, sceneName); + } + authority.SceneManager.OnSceneEvent -= SceneManager_OnSceneEvent; + + // Set the active scene to be the 1st scene loaded so we don't instantiate within the test runner scene. + SetActiveScene(m_ScenesLoaded[0]); + + var autoSyncActive = new List(); // Spawn 3 NetworkObject instances that auto synchronize to active scene changes for (int i = 0; i < 3; i++) { - var serverInstance = Object.Instantiate(m_TestPrefabAutoSynchActiveScene); - var serverNetworkObject = serverInstance.GetComponent(); // We are also testing that objects marked to synchronize with changes to // the active scene and marked to destroy with scene =are destroyed= if // the scene being unloaded is currently the active scene and the scene that // the NetworkObjects reside within. - serverNetworkObject.Spawn(true); - m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); + var serverInstance = SpawnObject(m_TestPrefabAutoSynchActiveScene, authority, true); + var serverNetworkObject = serverInstance.GetComponent(); + autoSyncActive.Add(serverNetworkObject); } - // Spawn 3 NetworkObject instances that do not auto synchronize to active scene changes // and ==should not be== destroyed with the scene (these should be the only remaining // instances) + var autoSyncInactive = new List(); for (int i = 0; i < 3; i++) { - var serverInstance = Object.Instantiate(m_TestPrefab); - var serverNetworkObject = serverInstance.GetComponent(); // This set of NetworkObjects will be used to verify that NetworkObjets // spawned with DestroyWithScene set to false will migrate into the current // active scene if the scene they currently reside within is destroyed and // is not the currently active scene. - serverNetworkObject.Spawn(); - m_ServerSpawnedPrefabInstances.Add(serverNetworkObject); + var serverInstance = SpawnObject(m_TestPrefab, authority); + var serverNetworkObject = serverInstance.GetComponent(); + autoSyncInactive.Add(serverNetworkObject); } // Spawn 3 NetworkObject instances that do not auto synchronize to active scene changes @@ -307,104 +317,90 @@ public IEnumerator ActiveSceneSynchronizationTest() serverNetworkObject.Spawn(true); m_ServerSpawnedDestroyWithSceneInstances.Add(serverNetworkObject); } + var authoritySpawnedInstances = new List(); + authoritySpawnedInstances.AddRange(autoSyncActive); + authoritySpawnedInstances.AddRange(autoSyncInactive); + authoritySpawnedInstances.AddRange(m_ServerSpawnedDestroyWithSceneInstances); - yield return WaitForConditionOrTimeOut(VerifyAllClientsSpawnedInstances); + yield return WaitForSpawnedOnAllOrTimeOut(authoritySpawnedInstances); AssertOnTimeout($"Timed out waiting for all clients to spawn {nameof(NetworkObject)}s!"); - // Now load three scenes - m_ServerNetworkManager.SceneManager.OnSceneEvent += SceneManager_OnSceneEvent; - for (int i = 0; i < 3; i++) - { - m_ClientsLoadedScene = false; - m_CurrentSceneLoading = m_TestScenes[i]; - var loadStatus = m_ServerNetworkManager.SceneManager.LoadScene(m_TestScenes[i], LoadSceneMode.Additive); - Assert.True(loadStatus == SceneEventProgressStatus.Started, $"Failed to start loading scene {m_CurrentSceneLoading}! Return status: {loadStatus}"); - yield return WaitForConditionOrTimeOut(() => m_ClientsLoadedScene); - AssertOnTimeout($"Timed out waiting for all clients to load scene {m_CurrentSceneLoading}!"); - } - + var sceneToMigrateTo = m_ScenesLoaded[2]; // Migrate the instances that don't synchronize with active scene changes into the 3rd loaded scene // (We are making sure these stay in the same scene they are migrated into) - for (int i = 3; i < m_ServerSpawnedPrefabInstances.Count; i++) + foreach (var spawnedObject in autoSyncInactive) { - SceneManager.MoveGameObjectToScene(m_ServerSpawnedPrefabInstances[i].gameObject, m_ScenesLoaded[2]); + SceneManager.MoveGameObjectToScene(spawnedObject.gameObject, sceneToMigrateTo); } // Migrate the instances that don't synchronize with active scene changes and are destroyed with the // scene unloading into the 3rd loaded scene // (We are making sure these get destroyed when the scene is unloaded) - for (int i = 0; i < m_ServerSpawnedDestroyWithSceneInstances.Count; i++) + foreach (var spawnedObject in m_ServerSpawnedDestroyWithSceneInstances) { - SceneManager.MoveGameObjectToScene(m_ServerSpawnedDestroyWithSceneInstances[i].gameObject, m_ScenesLoaded[2]); + SceneManager.MoveGameObjectToScene(spawnedObject.gameObject, sceneToMigrateTo); } // Make sure they migrated to the proper scene - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // Now change the active scene - SceneManager.SetActiveScene(m_ScenesLoaded[1]); - // We have to do this - Object.DontDestroyOnLoad(m_TestPrefabAutoSynchActiveScene); + var newActiveScene = m_ScenesLoaded[1]; + SetActiveScene(newActiveScene); // First, make sure server-side scenes and client side scenes match - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // Verify that the auto-active-scene synchronization NetworkObjects migrated to the newly // assigned active scene - for (int i = 0; i < 3; i++) + foreach (var obj in autoSyncActive) { - Assert.True(m_ServerSpawnedPrefabInstances[i].gameObject.scene == m_ScenesLoaded[1], - $"{m_ServerSpawnedPrefabInstances[i].gameObject.name} did not migrate into scene {m_ScenesLoaded[1].name}!"); + Assert.AreEqual(newActiveScene, obj.gameObject.scene, $"{obj.gameObject.name} did not migrate into scene {newActiveScene.name}!"); } // Verify that the other NetworkObjects that don't synchronize with active scene changes did // not migrate into the active scene. - for (int i = 3; i < m_ServerSpawnedPrefabInstances.Count; i++) + foreach (var obj in autoSyncInactive) { - Assert.False(m_ServerSpawnedPrefabInstances[i].gameObject.scene == m_ScenesLoaded[1], - $"{m_ServerSpawnedPrefabInstances[i].gameObject.name} migrated into scene {m_ScenesLoaded[1].name}!"); + Assert.AreNotEqual(newActiveScene, obj.gameObject.scene, $"{obj.gameObject.name} migrated into scene {newActiveScene.name}!"); } - for (int i = 0; i < 3; i++) + foreach (var obj in m_ServerSpawnedDestroyWithSceneInstances) { - Assert.False(m_ServerSpawnedDestroyWithSceneInstances[i].gameObject.scene == m_ScenesLoaded[1], - $"{m_ServerSpawnedDestroyWithSceneInstances[i].gameObject.name} migrated into scene {m_ScenesLoaded[1].name}!"); + Assert.AreNotEqual(newActiveScene, obj.gameObject.scene, $"{obj.gameObject.name} migrated into scene {newActiveScene.name}!"); } // Verify that a late joining client synchronizes properly and destroys the appropriate NetworkObjects yield return CreateAndStartNewClient(); - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + AssertOnTimeout("Failed to start or create a new client!"); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"[Late Joined Client #1] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // Now, unload the scene containing the NetworkObjects that don't synchronize with active scene changes DestroyWithSceneInstancesTestHelper.NetworkObjectDestroyed += OnNonActiveSynchDestroyWithSceneNetworkObjectDestroyed; - m_ClientsUnloadedScene = false; - m_CurrentSceneUnloading = m_ScenesLoaded[2].name; - var status = m_ServerNetworkManager.SceneManager.UnloadScene(m_ScenesLoaded[2]); - Assert.True(status == SceneEventProgressStatus.Started, $"Failed to start unloading scene {m_ScenesLoaded[2].name} with status {status}!"); - yield return WaitForConditionOrTimeOut(() => m_ClientsUnloadedScene); + + yield return UnloadScene(authority, sceneToMigrateTo); // Clean up any destroyed NetworkObjects - for (int i = m_ServerSpawnedPrefabInstances.Count - 1; i >= 0; i--) + for (int i = authoritySpawnedInstances.Count - 1; i >= 0; i--) { - if (m_ServerSpawnedPrefabInstances[i] == null) + if (authoritySpawnedInstances[i] == null) { - m_ServerSpawnedPrefabInstances.RemoveAt(i); + authoritySpawnedInstances.RemoveAt(i); } } - AssertOnTimeout($"Timed out waiting for all clients to unload scene {m_CurrentSceneUnloading}!"); - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + AssertOnTimeout($"Timed out waiting for all clients to unload scene {sceneToMigrateTo.name}!"); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // Verify that the NetworkObjects that don't synchronize with active scene changes but marked to not // destroy with the scene are migrated into the current active scene - for (int i = 3; i < m_ServerSpawnedPrefabInstances.Count; i++) + foreach (var obj in autoSyncInactive) { - Assert.True(m_ServerSpawnedPrefabInstances[i].gameObject.scene == m_ScenesLoaded[1], - $"{m_ServerSpawnedPrefabInstances[i].gameObject.name} did not migrate into scene {m_ScenesLoaded[1].name} but are in scene {m_ServerSpawnedPrefabInstances[i].gameObject.scene.name}!"); + Assert.True(obj.gameObject.scene == newActiveScene, $"{obj.gameObject.name} did not migrate into scene {newActiveScene.name} but are in scene {obj.gameObject.scene.name}!"); } // Verify all NetworkObjects that should have been destroyed with the scene unloaded were destroyed @@ -414,18 +410,14 @@ public IEnumerator ActiveSceneSynchronizationTest() // Now unload the active scene to verify all remaining NetworkObjects are migrated into the SceneManager // assigned active scene - m_ClientsUnloadedScene = false; - m_CurrentSceneUnloading = m_ScenesLoaded[1].name; - m_ServerNetworkManager.SceneManager.UnloadScene(m_ScenesLoaded[1]); - yield return WaitForConditionOrTimeOut(() => m_ClientsUnloadedScene); - AssertOnTimeout($"Timed out waiting for all clients to unload scene {m_CurrentSceneUnloading}!"); + yield return UnloadScene(authority, newActiveScene); // Clean up any destroyed NetworkObjects - for (int i = m_ServerSpawnedPrefabInstances.Count - 1; i >= 0; i--) + for (int i = authoritySpawnedInstances.Count - 1; i >= 0; i--) { - if (m_ServerSpawnedPrefabInstances[i] == null) + if (authoritySpawnedInstances[i] == null) { - m_ServerSpawnedPrefabInstances.RemoveAt(i); + authoritySpawnedInstances.RemoveAt(i); } } @@ -433,22 +425,63 @@ public IEnumerator ActiveSceneSynchronizationTest() yield return CreateAndStartNewClient(); // Verify the late joining client spawns all instances - yield return WaitForConditionOrTimeOut(VerifyAllClientsSpawnedInstances); - AssertOnTimeout($"Timed out waiting for all clients to spawn {nameof(NetworkObject)}s!"); + yield return WaitForSpawnedOnAllOrTimeOut(authoritySpawnedInstances); + AssertOnTimeout($"[Late Joined Client #2] Timed out waiting for all clients to spawn {nameof(NetworkObject)}s!"); // Verify the instances are in the correct scenes - yield return WaitForConditionOrTimeOut(VerifySpawnedObjectsMigrated); + yield return WaitForConditionOrTimeOut(errorLog => VerifyAllScenesMatch(errorLog, authoritySpawnedInstances)); AssertOnTimeout($"[Late Joined Client #2] Timed out waiting for all clients to migrate all NetworkObjects into the appropriate scenes!"); // All but 3 instances should be destroyed - Assert.True(m_ServerSpawnedPrefabInstances.Count == 3, $"{nameof(m_ServerSpawnedPrefabInstances)} still has a count of {m_ServerSpawnedPrefabInstances.Count} " + - $"NetworkObject instances!"); - Assert.True(m_ServerSpawnedDestroyWithSceneInstances.Count == 0, $"{nameof(m_ServerSpawnedDestroyWithSceneInstances)} still has a count of " + - $"{m_ServerSpawnedDestroyWithSceneInstances.Count} NetworkObject instances!"); - for (int i = 0; i < 3; i++) - { - Assert.True(m_ServerSpawnedPrefabInstances[i].gameObject.name.Contains(m_TestPrefab.gameObject.name), $"Expected {m_ServerSpawnedPrefabInstances[i].gameObject.name} to contain {m_TestPrefab.gameObject.name}!"); - } + Assert.IsEmpty(autoSyncActive.Where(obj => obj != null), $"All the NetworkObjects with {nameof(NetworkObject.ActiveSceneSynchronization)}=true should have been destroyed when the active scene was unloaded!"); + Assert.IsEmpty(m_ServerSpawnedDestroyWithSceneInstances.Where(obj => obj != null), $"All the NetworkObjects with {nameof(NetworkObject.DestroyWithScene)} should have been destroyed when the active scene was unloaded!"); + Assert.AreEqual(3, autoSyncInactive.Count(obj => obj != null), $"All the NetworkObjects with {nameof(NetworkObject.ActiveSceneSynchronization)}=false should have survived the active scene change!"); + } + + /// + /// Helper method to load a scene and wait for the OnLoadEventCompleted event to trigger. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private IEnumerator LoadScene(NetworkManager authority, string sceneToLoad) + { + m_LoadEventCompleted = false; + authority.SceneManager.OnLoadEventCompleted += OnLoadEventCompleted; + var loadStatus = authority.SceneManager.LoadScene(sceneToLoad, LoadSceneMode.Additive); + Assert.True(loadStatus == SceneEventProgressStatus.Started, $"Failed to start loading scene {sceneToLoad}! Return status: {loadStatus}"); + yield return WaitForConditionOrTimeOut(() => m_LoadEventCompleted); + // Remove subscription prior to potentially asserting. + authority.SceneManager.OnLoadEventCompleted -= OnLoadEventCompleted; + AssertOnTimeout($"Timed out waiting for all clients to load scene {sceneToLoad}!"); + } + + /// + /// Helper method to load a scene and wait for the OnUnloadEventCompleted event to trigger. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private IEnumerator UnloadScene(NetworkManager authority, Scene sceneToUnload) + { + m_UnloadEventCompleted = false; + authority.SceneManager.OnUnloadEventCompleted += OnUnloadEventCompleted; + authority.SceneManager.UnloadScene(sceneToUnload); + + // Always make sure the scene event has completed. Trying to check if the scenes are loaded as a metric can + // create edge case scenarios where the scene might have been just loaded but not processed during synchronization. + yield return WaitForConditionOrTimeOut(() => m_UnloadEventCompleted); + // Remove subscription prior to potentially asserting. + authority.SceneManager.OnUnloadEventCompleted -= OnUnloadEventCompleted; + AssertOnTimeout($"Timed out waiting for all clients to unload scene {sceneToUnload.name}!"); + } + + private bool m_LoadEventCompleted; + private void OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, List clientsCompleted, List clientsTimedOut) + { + m_LoadEventCompleted = true; + } + + private bool m_UnloadEventCompleted; + private void OnUnloadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, List clientsCompleted, List clientsTimedOut) + { + m_UnloadEventCompleted = true; } /// @@ -462,30 +495,16 @@ private void OnNonActiveSynchDestroyWithSceneNetworkObjectDestroyed(NetworkObjec private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) { + var authority = GetAuthorityNetworkManager(); switch (sceneEvent.SceneEventType) { case SceneEventType.LoadComplete: { - if (sceneEvent.ClientId == m_ServerNetworkManager.LocalClientId) + if (sceneEvent.ClientId == authority.LocalClientId) { - m_SceneLoaded = sceneEvent.Scene; m_ScenesLoaded.Add(sceneEvent.Scene); } - break; - } - case SceneEventType.LoadEventCompleted: - { - Assert.IsTrue(sceneEvent.ClientsThatTimedOut.Count == 0, $"{sceneEvent.ClientsThatTimedOut.Count} clients timed out while trying to load scene {m_CurrentSceneLoading}!"); - m_ClientsLoadedScene = true; - break; - } - case SceneEventType.UnloadEventCompleted: - { - if (sceneEvent.SceneName == m_CurrentSceneUnloading) - { - m_ClientsUnloadedScene = true; - } - break; + return; } } } @@ -495,12 +514,51 @@ protected override IEnumerator OnTearDown() m_TestPrefab = null; m_TestPrefabAutoSynchActiveScene = null; m_TestPrefabDestroyWithScene = null; + // Any static event that could be subscribed to but not unsubscribed to due to an assert needs to be cleaned up here. + DestroyWithSceneInstancesTestHelper.NetworkObjectDestroyed -= OnNonActiveSynchDestroyWithSceneNetworkObjectDestroyed; SceneManager.SetActiveScene(m_OriginalActiveScene); m_ServerSpawnedDestroyWithSceneInstances.Clear(); m_ServerSpawnedPrefabInstances.Clear(); m_ScenesLoaded.Clear(); yield return base.OnTearDown(); } + + private NetworkObject GetAuthorityInstance(NetworkObject instance) + { + if (instance.IsOwner) + { + return instance; + } + + var owner = instance.OwnerClientId; + foreach (var networkManager in m_NetworkManagers) + { + if (networkManager.LocalClientId == owner) + { + networkManager.SpawnManager.SpawnedObjects.TryGetValue(instance.NetworkObjectId, out var networkObject); + return networkObject; + } + } + + return null; + } + } + + internal class SceneOriginTracker : NetworkBehaviour + { + public NetworkSceneHandle SceneWhereAwakeHappened; + private void Awake() + { + SceneWhereAwakeHappened = gameObject.scene.handle; + } + } + + internal class ShouldNeverSpawn : NetworkBehaviour + { + public override void OnNetworkSpawn() + { + Assert.Fail("Should never spawn!"); + } } /// @@ -509,7 +567,7 @@ protected override IEnumerator OnTearDown() /// internal class DestroyWithSceneInstancesTestHelper : NetworkBehaviour { - public static GameObject ShouldNeverSpawn; + public static ShouldNeverSpawn ShouldNeverSpawn; public static Dictionary> ObjectRelativeInstances = new Dictionary>(); @@ -544,12 +602,9 @@ public override void OnNetworkDespawn() public override void OnDestroy() { - if (NetworkManager != null) + if (IsSpawned && HasAuthority) { - if (NetworkManager.LocalClientId == NetworkManager.ServerClientId) - { - NetworkObjectDestroyed?.Invoke(NetworkObject); - } + NetworkObjectDestroyed?.Invoke(NetworkObject); } base.OnDestroy(); } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs index f38a311ea8..70ca7c24d2 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs @@ -94,8 +94,8 @@ public IEnumerator InSceneNetworkObjectState([Values(DefaultState.IsEnabled, Def [Values(NetworkObjectType.InScenePlaced, NetworkObjectType.DynamicallySpawned)] NetworkObjectType networkObjectType) { var waitForFullNetworkTick = new WaitForSeconds(1.0f / m_ServerNetworkManager.NetworkConfig.TickRate); - var isActive = activeState == DefaultState.IsEnabled ? true : false; - var isInScene = networkObjectType == NetworkObjectType.InScenePlaced ? true : false; + var isActive = activeState == DefaultState.IsEnabled; + var isInScene = networkObjectType == NetworkObjectType.InScenePlaced; var objectInstance = Object.Instantiate(m_DDOL_ObjectToSpawn); var networkObject = objectInstance.GetComponent(); @@ -110,7 +110,7 @@ public IEnumerator InSceneNetworkObjectState([Values(DefaultState.IsEnabled, Def } // Sets whether we are in-scene or dynamically spawned NetworkObject - ddolBehaviour.SetInScene(isInScene); + networkObject.InScenePlaced = isInScene; networkObject.Spawn(); yield return waitForFullNetworkTick; @@ -148,12 +148,6 @@ public override void OnNetworkSpawn() NetworkObject.DestroyWithScene = false; base.OnNetworkSpawn(); } - - public void SetInScene(bool isInScene) - { - var networkObject = GetComponent(); - networkObject.IsSceneObject = isInScene; - } } } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerEventDataPoolTest.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerEventDataPoolTest.cs index 7c448830f6..107db75314 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerEventDataPoolTest.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerEventDataPoolTest.cs @@ -381,11 +381,11 @@ private bool CheckNetworkObjectsToSynchronizeSceneChanges(NetworkManager network m_ErrorMsg.Clear(); if (networkManager.SpawnManager.NetworkObjectsToSynchronizeSceneChanges.Count > 0) { - foreach (var entry in networkManager.SpawnManager.NetworkObjectsToSynchronizeSceneChanges) + foreach (var obj in networkManager.SpawnManager.NetworkObjectsToSynchronizeSceneChanges.Values) { - if (entry.Value.IsSceneObject.HasValue && entry.Value.IsSceneObject.Value) + if (obj.InScenePlaced) { - m_ErrorMsg.AppendLine($"{entry.Value.name} still exists within {nameof(NetworkSpawnManager.NetworkObjectsToSynchronizeSceneChanges)}!"); + m_ErrorMsg.AppendLine($"{obj.name} still exists within {nameof(NetworkSpawnManager.NetworkObjectsToSynchronizeSceneChanges)}!"); } } } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerPopulateInSceneTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerPopulateInSceneTests.cs index bc3a24cb78..1d2302592e 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerPopulateInSceneTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerPopulateInSceneTests.cs @@ -40,7 +40,7 @@ protected override void OnServerAndClientsCreated() // the scene is loaded (i.e. IsSceneObject is null) var inScenePrefab = CreateNetworkObjectPrefab("NewSceneObject"); var networkObject = inScenePrefab.GetComponent(); - networkObject.IsSceneObject = null; + networkObject.InScenePlaced = true; networkObject.NetworkManagerOwner = m_ServerNetworkManager; m_InSceneObjectList.Add(networkObject.GlobalObjectIdHash, inScenePrefab); @@ -49,7 +49,7 @@ protected override void OnServerAndClientsCreated() // unloading/reloading any scenes. inScenePrefab = CreateNetworkObjectPrefab("SetInSceneObject"); networkObject = inScenePrefab.GetComponent(); - networkObject.IsSceneObject = true; + networkObject.InScenePlaced = true; networkObject.NetworkManagerOwner = m_ServerNetworkManager; m_InSceneObjectList.Add(networkObject.GlobalObjectIdHash, inScenePrefab); } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs new file mode 100644 index 0000000000..28cd0b49d2 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs @@ -0,0 +1,205 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Netcode; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; + +namespace TestProject.RuntimeTests +{ + /// + /// Validates client synchronization. + /// + /// + /// This includes both client synchronization mode passes along with verifying + /// that s migrated into the DDOL will still be spawned + /// and preserve their value. + /// + internal class NetworkSceneManagerStartupTests : NetcodeIntegrationTest + { + private const string k_ActiveScene = "SessionSynchronize"; + private const string k_AdditionalScene = "InSceneNetworkObjectMovesToDDOL"; + + private readonly List m_ObjectsInScenes = new List(); + private Scene m_OriginalActiveScene; + private Scene m_SceneLoaded; + private bool m_CanStart = false; + + // Used with scene pre-loading + private string m_SceneToLoad; + private bool m_SceneWasLoaded; + + #region NetcodeIntegrationTest overrides + protected override int NumberOfClients => 0; + protected override bool CanStartServerAndClients() => m_CanStart; + + protected override void OnOneTimeSetup() + { + // Get the active scene prior to any interation running through the OnSetup. + m_OriginalActiveScene = SceneManager.GetActiveScene(); + base.OnOneTimeSetup(); + } + + protected override IEnumerator OnSetup() + { + // Always reset + m_CanStart = false; + return base.OnSetup(); + } + + protected override void OnCreatePlayerPrefab() + { + // Avoid trying to spawn this + Object.DontDestroyOnLoad(m_PlayerPrefab); + base.OnCreatePlayerPrefab(); + } + + protected override IEnumerator OnTearDown() + { + LogAssert.ignoreFailingMessages = false; + m_ObjectsInScenes.Clear(); + // Restore the integration test scene as the active scene. + SceneManager.SetActiveScene(m_OriginalActiveScene); + + // Unload everything else. + for (int i = 0; i < SceneManager.sceneCount - 1; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (scene == m_OriginalActiveScene) + { + continue; + } + SceneManager.UnloadSceneAsync(scene); + yield return WaitForConditionOrTimeOut(() => !scene.isLoaded); + } + yield return base.OnTearDown(); + } + #endregion + + /// + /// Validates things migrated into the DDOL will be included when synchronizing clients. + /// + /// The client synchronization mode to use for the current pass. + [UnityTest] + public IEnumerator AllExistingObjectsAreSpawnedAtStartup([Values] LoadSceneMode clientSynchronizationMode) + { + LogAssert.ignoreFailingMessages = true; + yield return PreLoadScene(k_ActiveScene); + yield return PreLoadScene(k_AdditionalScene); + SceneManager.SetActiveScene(m_SceneLoaded); + + var existingObjects = new List(); + var dontDestroyOnLoadCount = 0; + + // Now get everything migrated into the DDOL and the DDOL scene itself. + var ddolScene = GetNetworkObjectsInDDOL(); + // Validate NetworkObjects in DDOL + foreach (var obj in m_ObjectsInScenes) + { + Assert.IsFalse(obj.IsSpawned, $"NetworkObject {obj.name} should not have been spawned!"); + + existingObjects.Add(obj); + if (obj.gameObject.scene.name == ddolScene.name) + { + dontDestroyOnLoadCount++; + } + } + + Assert.IsNotEmpty(existingObjects, $"Found no existing {nameof(NetworkObject)}s!"); + Assert.That(dontDestroyOnLoadCount, Is.GreaterThan(0), "Found no {nameof(NetworkObject)}s in the DDOL scene!"); + + // Now enable starting server and clients and start the server + m_CanStart = true; + yield return StartServerAndClients(); + + // Apply the test's client synchronization mode + GetAuthorityNetworkManager().SceneManager.SetClientSynchronizationMode(clientSynchronizationMode); + + // Validate the existing objects + foreach (var existingObject in existingObjects) + { + Assert.IsFalse(existingObject == null, "Expected existing object to still exist!"); + Assert.IsTrue(existingObject.IsSpawned, $"NetworkObject {existingObject.name} in scene {existingObject.gameObject.scene.name} was not spawned!"); + Assert.IsTrue(existingObject.InScenePlaced, $"NetworkObject {existingObject.name} in scene {existingObject.gameObject.scene.name} was not inScenePlaced!"); + } + + // If additive client synchronization mode, load the scenes that are already loaded + // on the scene authority instance so they will be used during client synchronization. + if (clientSynchronizationMode == LoadSceneMode.Additive) + { + yield return PreLoadScene(k_ActiveScene); + yield return PreLoadScene(k_AdditionalScene); + } + + // Late join a client + yield return CreateAndStartNewClient(); + + // Wait for all existing objects to spawn on the client + yield return WaitForSpawnedOnAllOrTimeOut(existingObjects); + AssertOnTimeout("Timed out waiting for objects to spawn on all clients!"); + } + + #region Scene loading and related methods + + /// + /// Uses the 's current scene which should be + /// the DDOL scene. + /// + /// The DDOL scene + private Scene GetNetworkObjectsInDDOL() + { + // This does catch any newly instantiated in-scene placed NetworkObjects moved into DDOL + // during awake. + var sceneToUse = NetworkManager.Singleton.gameObject.scene; + Assert.IsTrue(sceneToUse.IsValid() && sceneToUse.name == "DontDestroyOnLoad", $"[{NetworkManager.Singleton.name}] Is not in the DDOL! Is this being invoked too early?"); + + foreach (var rootObject in sceneToUse.GetRootGameObjects()) + { + foreach (var networkObject in rootObject.GetComponentsInChildren()) + { + if (!m_ObjectsInScenes.Contains(networkObject) && networkObject.InScenePlaced) + { + m_ObjectsInScenes.Add(networkObject); + } + } + } + return sceneToUse; + } + + private IEnumerator PreLoadScene(string sceneName) + { + m_SceneToLoad = sceneName; + m_SceneWasLoaded = false; + SceneManager.sceneLoaded += OnSceneLoad; + SceneManager.LoadScene(sceneName, LoadSceneMode.Additive); + yield return WaitForConditionOrTimeOut(() => m_SceneWasLoaded); + AssertOnTimeout("Timed out waiting for scene to load!"); + SceneManager.sceneLoaded -= OnSceneLoad; + } + + private void TrackObjectsInScene(Scene scene) + { + // This does not catch things moved into the DDOL during awake. + foreach (var rootObject in scene.GetRootGameObjects()) + { + foreach (var networkObject in rootObject.GetComponentsInChildren()) + { + m_ObjectsInScenes.Add(networkObject); + } + } + } + + private void OnSceneLoad(Scene scene, LoadSceneMode loadSceneMode) + { + if (m_SceneToLoad == scene.name) + { + m_SceneWasLoaded = true; + m_SceneLoaded = scene; + TrackObjectsInScene(scene); + } + } + #endregion + } +} diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs.meta b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs.meta new file mode 100644 index 0000000000..ddf1a7550e --- /dev/null +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ca8e58d04b6fc6c48a6661c90d4c12dd \ No newline at end of file diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs index a37a3244d2..f1bdc67c66 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs @@ -38,12 +38,10 @@ public IEnumerator FastReaderAllocationTest() var networkManager = networkManagerGameObject.AddComponent(); var unityTransport = networkManagerGameObject.AddComponent(); - var prefabs = ScriptableObject.CreateInstance(); - prefabs.Add(new NetworkPrefab()); networkManager.NetworkConfig = new NetworkConfig() { ConnectionApproval = false, - Prefabs = new NetworkPrefabs { NetworkPrefabsLists = new List { prefabs } }, + Prefabs = new NetworkPrefabs { NetworkPrefabsLists = new List { } }, NetworkTransport = unityTransport }; diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs index 3bf12e7011..2770ed4ce5 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentDynamicUnderInScenePlaced.cs @@ -74,7 +74,7 @@ private bool TestParentedAndNotInScenePlaced() { // Always assign m_FailedValidation to avoid possible null reference crashes. var serverPlayer = m_FailedValidation = m_ServerNetworkManager.LocalClient.PlayerObject; - if (serverPlayer.transform.parent == null || serverPlayer.IsSceneObject.Value == true) + if (serverPlayer.transform.parent == null || serverPlayer.InScenePlaced) { m_FailedValidation = serverPlayer; return false; @@ -83,7 +83,7 @@ private bool TestParentedAndNotInScenePlaced() foreach (var clientNetworkManager in m_ClientNetworkManagers) { var lateJoinPlayer = clientNetworkManager.LocalClient.PlayerObject; - if (lateJoinPlayer.transform.parent == null || lateJoinPlayer.IsSceneObject.Value == true) + if (lateJoinPlayer.transform.parent == null || lateJoinPlayer.InScenePlaced) { m_FailedValidation = lateJoinPlayer; return false; @@ -93,7 +93,7 @@ private bool TestParentedAndNotInScenePlaced() foreach (var dynamicallySpawned in ParentDynamicUnderInScenePlacedHelper.Instances) { var networkObject = dynamicallySpawned.Value; - if (networkObject.transform.parent == null || networkObject.IsSceneObject.Value == true) + if (networkObject.transform.parent == null || networkObject.InScenePlaced) { m_FailedValidation = networkObject; return false; @@ -124,7 +124,7 @@ public IEnumerator ParentUnderInSceneplaced() // Wait for the host-server's player to be parented under the in-scene placed NetworkObject yield return WaitForConditionOrTimeOut(TestParentedAndNotInScenePlaced); - AssertOnTimeout($"[{m_FailedValidation.name}] Failed validation! InScenePlaced ({m_FailedValidation.IsSceneObject.Value}) | Was Parented ({m_FailedValidation.transform.position != null})"); + AssertOnTimeout($"[{m_FailedValidation.name}] Failed validation! InScenePlaced ({m_FailedValidation.InScenePlaced}) | Was Parented ({m_FailedValidation.transform.position != null})"); m_TargetScenePlacedId = m_ServerNetworkManager.LocalClient.PlayerObject.transform.parent.GetComponent().NetworkObjectId; // Now dynamically spawn a NetworkObject to also test dynamically spawned NetworkObjects being parented @@ -141,7 +141,7 @@ public IEnumerator ParentUnderInSceneplaced() AssertOnTimeout($"[Client-{i + 1}] Failed to find in-scene placed NetworkObject or failed to parent under it!"); } yield return WaitForConditionOrTimeOut(TestParentedAndNotInScenePlaced); - AssertOnTimeout($"[{m_FailedValidation.name}] Failed validation! InScenePlaced ({m_FailedValidation.IsSceneObject.Value}) | Was Parented ({m_FailedValidation.transform.position != null})"); + AssertOnTimeout($"[{m_FailedValidation.name}] Failed validation! InScenePlaced ({m_FailedValidation.InScenePlaced}) | Was Parented ({m_FailedValidation.transform.position != null})"); } } diff --git a/testproject/Assets/Tests/Runtime/OnNetworkSpawnExceptionTests.cs b/testproject/Assets/Tests/Runtime/OnNetworkSpawnExceptionTests.cs index ea81ac8409..224460e8c8 100644 --- a/testproject/Assets/Tests/Runtime/OnNetworkSpawnExceptionTests.cs +++ b/testproject/Assets/Tests/Runtime/OnNetworkSpawnExceptionTests.cs @@ -2,11 +2,12 @@ using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; +using NUnit.Framework; using Unity.Netcode; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; -using UnityEngine.Assertions; using UnityEngine.TestTools; +using Assert = UnityEngine.Assertions.Assert; using Random = UnityEngine.Random; namespace TestProject.RuntimeTests @@ -82,6 +83,10 @@ public override void OnNetworkDespawn() } } + [TestFixture(HostOrServer.Server)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedServer)] +#endif public class OnNetworkSpawnExceptionTests : NetcodeIntegrationTest { protected override int NumberOfClients => 1; @@ -101,6 +106,11 @@ protected override bool UseCMBService() return false; } + public OnNetworkSpawnExceptionTests(HostOrServer hostOrServer) : base(hostOrServer) + { + + } + [UnityTest] public IEnumerator WhenOnNetworkSpawnThrowsException_FutureOnNetworkSpawnsAreNotPrevented() { @@ -238,7 +248,6 @@ public IEnumerator WhenOnNetworkDespawnThrowsException_FutureOnNetworkDespawnsAr protected override IEnumerator OnSetup() { - m_UseHost = false; OnNetworkSpawnThrowsExceptionComponent.NumClientSpawns = 0; OnNetworkSpawnThrowsExceptionComponent.NumServerSpawns = 0; OnNetworkSpawnNoExceptionComponent.NumClientSpawns = 0; diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntChangeOwnershipTest.cs b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntChangeOwnershipTest.cs deleted file mode 100644 index 4f3c651cb3..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntChangeOwnershipTest.cs +++ /dev/null @@ -1,95 +0,0 @@ -// using System.Collections; -// using NUnit.Framework; -// using Unity.Netcode.Components; -// using Unity.Netcode.Samples; -// using UnityEngine; -// using UnityEngine.TestTools; -// -// // Tests for ClientNetworkTransform (CNT) + NetworkRigidbody. This test is in TestProject because it needs access to ClientNetworkTransform -// namespace Unity.Netcode.RuntimeTests -// { -// public class NetworkRigidbody2DDynamicCntChangeOwnershipTest : NetworkRigidbody2DCntChangeOwnershipTestBase -// { -// public override bool Kinematic => false; -// } -// -// public class NetworkRigidbody2DKinematicCntChangeOwnershipTest : NetworkRigidbody2DCntChangeOwnershipTestBase -// { -// public override bool Kinematic => true; -// } -// -// public abstract class NetworkRigidbody2DCntChangeOwnershipTestBase : NetcodeIntegrationTest -// { -// protected override int NumberOfClients => 1; -// -// public abstract bool Kinematic { get; } -// -// [UnitySetUp] -// public override IEnumerator Setup() -// { -// yield return StartSomeClientsAndServerWithPlayers(true, NumberOfClients, playerPrefab => -// { -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.GetComponent().isKinematic = Kinematic; -// }); -// } -// /// -// /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. -// /// -// /// -// [UnityTest] -// public IEnumerator TestRigidbodyKinematicEnableDisable() -// { -// // This is the *SERVER VERSION* of the *CLIENT PLAYER* -// var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult); -// var serverPlayer = serverClientPlayerResult.Result.gameObject; -// -// // This is the *CLIENT VERSION* of the *CLIENT PLAYER* -// var clientClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult); -// var clientPlayer = clientClientPlayerResult.Result.gameObject; -// -// Assert.IsNotNull(serverPlayer); -// Assert.IsNotNull(clientPlayer); -// -// int waitFor = Time.frameCount + 2; -// yield return new WaitUntil(() => Time.frameCount >= waitFor); -// -// TestKinematicSetCorrectly(clientPlayer, serverPlayer); -// -// -// // give server ownership over the player -// -// serverPlayer.GetComponent().ChangeOwnership(NetworkManager.ServerClientId); -// -// yield return null; -// yield return null; -// -// // server should now be able to commit to transform -// TestKinematicSetCorrectly(serverPlayer, clientPlayer); -// -// // return ownership to client -// serverPlayer.GetComponent().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId); -// yield return null; -// yield return null; -// -// // client should again be able to commit -// TestKinematicSetCorrectly(clientPlayer, serverPlayer); -// } -// -// -// -// private void TestKinematicSetCorrectly(GameObject canCommitPlayer, GameObject canNotCommitPlayer) -// { -// -// // can commit player has authority and should have a kinematic mode of false (or true in case body was already kinematic). -// Assert.True(canCommitPlayer.GetComponent().isKinematic == Kinematic); -// -// // can not commit player has no authority and should have a kinematic mode of true -// Assert.True(canNotCommitPlayer.GetComponent().isKinematic); -// } -// } -// } diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntChangeOwnershipTest.cs.meta b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntChangeOwnershipTest.cs.meta deleted file mode 100644 index b1c3736256..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntChangeOwnershipTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a51c7f7b93f04e3499089963d9bbb254 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntTest.cs b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntTest.cs deleted file mode 100644 index eac6d42d7e..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -// using System.Collections; -// using NUnit.Framework; -// using Unity.Netcode.Components; -// using Unity.Netcode.Samples; -// using UnityEngine; -// using UnityEngine.TestTools; -// -// // Tests for ClientNetworkTransform (CNT) + NetworkRigidbody2D. This test is in TestProject because it needs access to ClientNetworkTransform -// namespace Unity.Netcode.RuntimeTests -// { -// public class NetworkRigidbody2DDynamicCntTest : NetworkRigidbody2DCntTestBase -// { -// public override bool Kinematic => false; -// } -// -// public class NetworkRigidbody2DKinematicCntTest : NetworkRigidbody2DCntTestBase -// { -// public override bool Kinematic => true; -// } -// -// public abstract class NetworkRigidbody2DCntTestBase : NetcodeIntegrationTest -// { -// protected override int NumberOfClients => 1; -// -// public abstract bool Kinematic { get; } -// -// [UnitySetUp] -// public override IEnumerator Setup() -// { -// yield return StartSomeClientsAndServerWithPlayers(true, NumberOfClients, playerPrefab => -// { -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.GetComponent().isKinematic = Kinematic; -// }); -// } -// -// /// -// /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. -// /// -// /// -// [UnityTest] -// public IEnumerator TestRigidbodyKinematicEnableDisable() -// { -// // This is the *SERVER VERSION* of the *CLIENT PLAYER* -// var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult); -// var serverPlayer = serverClientPlayerResult.Result.gameObject; -// -// // This is the *CLIENT VERSION* of the *CLIENT PLAYER* -// var clientClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult); -// var clientPlayer = clientClientPlayerResult.Result.gameObject; -// -// Assert.IsNotNull(serverPlayer); -// Assert.IsNotNull(clientPlayer); -// -// int waitFor = Time.frameCount + 2; -// yield return new WaitUntil(() => Time.frameCount >= waitFor); -// -// TestKinematicSetCorrectly(clientPlayer, serverPlayer); -// -// // despawn the server player -// serverPlayer.GetComponent().Despawn(false); -// -// yield return null; -// yield return null; -// Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned. -// } -// -// private void TestKinematicSetCorrectly(GameObject canCommitPlayer, GameObject canNotCommitPlayer) -// { -// -// // can commit player has authority and should have a kinematic mode of false (or true in case body was already kinematic). -// Assert.True(canCommitPlayer.GetComponent().isKinematic == Kinematic); -// -// // can not commit player has no authority and should have a kinematic mode of true -// Assert.True(canNotCommitPlayer.GetComponent().isKinematic); -// } -// } -// } diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntTest.cs.meta b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntTest.cs.meta deleted file mode 100644 index f0d4cff600..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbody2DCntTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5ae5587c06cba7a46b93ee6774741c0d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntChangeOwnershipTest.cs b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntChangeOwnershipTest.cs deleted file mode 100644 index 2e45162f52..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntChangeOwnershipTest.cs +++ /dev/null @@ -1,95 +0,0 @@ -// using System.Collections; -// using NUnit.Framework; -// using Unity.Netcode.Components; -// using Unity.Netcode.Samples; -// using UnityEngine; -// using UnityEngine.TestTools; -// -// // Tests for ClientNetworkTransform (CNT) + NetworkRigidbody. This test is in TestProject because it needs access to ClientNetworkTransform -// namespace Unity.Netcode.RuntimeTests -// { -// public class NetworkRigidbodyDynamicCntChangeOwnershipTest : NetworkRigidbodyCntChangeOwnershipTestBase -// { -// public override bool Kinematic => false; -// } -// -// public class NetworkRigidbodyKinematicCntChangeOwnershipTest : NetworkRigidbodyCntChangeOwnershipTestBase -// { -// public override bool Kinematic => true; -// } -// -// public abstract class NetworkRigidbodyCntChangeOwnershipTestBase : NetcodeIntegrationTest -// { -// protected override int NumberOfClients => 1; -// -// public abstract bool Kinematic { get; } -// -// [UnitySetUp] -// public override IEnumerator Setup() -// { -// yield return StartSomeClientsAndServerWithPlayers(true, NumberOfClients, playerPrefab => -// { -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.GetComponent().isKinematic = Kinematic; -// }); -// } -// /// -// /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. -// /// -// /// -// [UnityTest] -// public IEnumerator TestRigidbodyKinematicEnableDisable() -// { -// // This is the *SERVER VERSION* of the *CLIENT PLAYER* -// var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult); -// var serverPlayer = serverClientPlayerResult.Result.gameObject; -// -// // This is the *CLIENT VERSION* of the *CLIENT PLAYER* -// var clientClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult); -// var clientPlayer = clientClientPlayerResult.Result.gameObject; -// -// Assert.IsNotNull(serverPlayer); -// Assert.IsNotNull(clientPlayer); -// -// int waitFor = Time.frameCount + 2; -// yield return new WaitUntil(() => Time.frameCount >= waitFor); -// -// TestKinematicSetCorrectly(clientPlayer, serverPlayer); -// -// -// // give server ownership over the player -// -// serverPlayer.GetComponent().ChangeOwnership(NetworkManager.ServerClientId); -// -// yield return null; -// yield return null; -// -// // server should now be able to commit to transform -// TestKinematicSetCorrectly(serverPlayer, clientPlayer); -// -// // return ownership to client -// serverPlayer.GetComponent().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId); -// yield return null; -// yield return null; -// -// // client should again be able to commit -// TestKinematicSetCorrectly(clientPlayer, serverPlayer); -// } -// -// -// -// private void TestKinematicSetCorrectly(GameObject canCommitPlayer, GameObject canNotCommitPlayer) -// { -// -// // can commit player has authority and should have a kinematic mode of false (or true in case body was already kinematic). -// Assert.True(canCommitPlayer.GetComponent().isKinematic == Kinematic); -// -// // can not commit player has no authority and should have a kinematic mode of true -// Assert.True(canNotCommitPlayer.GetComponent().isKinematic); -// } -// } -// } diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntChangeOwnershipTest.cs.meta b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntChangeOwnershipTest.cs.meta deleted file mode 100644 index 3eb4f88f5b..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntChangeOwnershipTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 941008c8040f03c44bd835d24b073260 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntTest.cs b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntTest.cs deleted file mode 100644 index 72f0c72b96..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -// using System.Collections; -// using NUnit.Framework; -// using Unity.Netcode.Components; -// using Unity.Netcode.Samples; -// using UnityEngine; -// using UnityEngine.TestTools; -// -// // Tests for ClientNetworkTransform (CNT) + NetworkRigidbody. This test is in TestProject because it needs access to ClientNetworkTransform -// namespace Unity.Netcode.RuntimeTests -// { -// public class NetworkRigidbodyDynamicCntTest : NetworkRigidbodyCntTestBase -// { -// public override bool Kinematic => false; -// } -// -// public class NetworkRigidbodyKinematicCntTest : NetworkRigidbodyCntTestBase -// { -// public override bool Kinematic => true; -// } -// -// public abstract class NetworkRigidbodyCntTestBase : NetcodeIntegrationTest -// { -// protected override int NumberOfClients => 1; -// -// public abstract bool Kinematic { get; } -// -// [UnitySetUp] -// public override IEnumerator Setup() -// { -// yield return StartSomeClientsAndServerWithPlayers(true, NumberOfClients, playerPrefab => -// { -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.AddComponent(); -// playerPrefab.GetComponent().isKinematic = Kinematic; -// }); -// } -// /// -// /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. -// /// -// /// -// [UnityTest] -// public IEnumerator TestRigidbodyKinematicEnableDisable() -// { -// // This is the *SERVER VERSION* of the *CLIENT PLAYER* -// var serverClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield returnNetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult); -// var serverPlayer = serverClientPlayerResult.Result.gameObject; -// -// // This is the *CLIENT VERSION* of the *CLIENT PLAYER* -// var clientClientPlayerResult = new NetcodeIntegrationTestHelpers.ResultWrapper(); -// yield return NetcodeIntegrationTestHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult); -// var clientPlayer = clientClientPlayerResult.Result.gameObject; -// -// Assert.IsNotNull(serverPlayer); -// Assert.IsNotNull(clientPlayer); -// -// int waitFor = Time.frameCount + 2; -// yield return new WaitUntil(() => Time.frameCount >= waitFor); -// -// TestKinematicSetCorrectly(clientPlayer, serverPlayer); -// -// // despawn the server player -// serverPlayer.GetComponent().Despawn(false); -// -// yield return null; -// yield return null; -// -// Assert.IsTrue(clientPlayer == null); // safety check that object is actually despawned. -// } -// -// private void TestKinematicSetCorrectly(GameObject canCommitPlayer, GameObject canNotCommitPlayer) -// { -// -// // can commit player has authority and should have a kinematic mode of false (or true in case body was already kinematic). -// Assert.True(canCommitPlayer.GetComponent().isKinematic == Kinematic); -// -// // can not commit player has no authority and should have a kinematic mode of true -// Assert.True(canNotCommitPlayer.GetComponent().isKinematic); -// } -// } -// } diff --git a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntTest.cs.meta b/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntTest.cs.meta deleted file mode 100644 index cea97efc01..0000000000 --- a/testproject/Assets/Tests/Runtime/Physics/NetworkRigidbodyCntTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e89712f9406aaa84d85508fa07d97655 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs b/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs index d0f7e294cb..9760465f7e 100644 --- a/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs +++ b/testproject/Assets/Tests/Runtime/PrefabExtendedTests.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Text; using NUnit.Framework; using TestProject.ManualTests; @@ -13,8 +12,12 @@ namespace TestProject.RuntimeTests { // DAMODE-TODO: When scene management is working in distributed authority mode we need to update this test - [TestFixture(SceneManagementTypes.SceneManagementEnabled)] - [TestFixture(SceneManagementTypes.SceneManagementDisabled)] + [TestFixture(SceneManagementTypes.SceneManagementEnabled, HostOrServer.Host)] + [TestFixture(SceneManagementTypes.SceneManagementDisabled, HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(SceneManagementTypes.SceneManagementEnabled, HostOrServer.UnifiedHost)] + [TestFixture(SceneManagementTypes.SceneManagementDisabled, HostOrServer.UnifiedHost)] +#endif public class PrefabExtendedTests : NetcodeIntegrationTest { private const string k_PrefabTestScene = "PrefabTestScene"; @@ -42,7 +45,7 @@ protected override bool UseCMBService() return false; } - public PrefabExtendedTests(SceneManagementTypes sceneManagementType) + public PrefabExtendedTests(SceneManagementTypes sceneManagementType, HostOrServer hostOrServer) : base(hostOrServer) { m_SceneManagementEnabled = sceneManagementType == SceneManagementTypes.SceneManagementEnabled; } @@ -163,7 +166,7 @@ private bool ValidateAllClientsSpawnedObjects() var clientSpawnedObject = s_GlobalNetworkObjects[client.LocalClientId][spawnedObject.NetworkObjectId]; // When scene management is disabled, we match against the InScenePlacedSourceGlobalObjectIdHash for in-scene placed NetworkObjects - var spawnedObjectGlobalObjectIdHash = !m_SceneManagementEnabled && spawnedObject.IsSceneObject.Value ? spawnedObject.InScenePlacedSourceGlobalObjectIdHash : spawnedObject.GlobalObjectIdHash; + var spawnedObjectGlobalObjectIdHash = !m_SceneManagementEnabled && spawnedObject.InScenePlaced ? spawnedObject.InScenePlacedSourceGlobalObjectIdHash : spawnedObject.GlobalObjectIdHash; // Validate the GlobalObjectIdHash values match if (clientSpawnedObject.GlobalObjectIdHash != spawnedObjectGlobalObjectIdHash) { @@ -213,9 +216,6 @@ public enum InstantiateAndSpawnMethods [UnityTest] public IEnumerator TestPrefabsSpawning([Values] InstantiateAndSpawnMethods instantiateAndSpawnType) { - var gloabalObjectId = m_SceneManagementEnabled ? 0 : InScenePlacedHelper.ServerInSceneDefined.First().GlobalObjectIdHash; - var firstError = $"[Netcode] Failed to create object locally. [globalObjectIdHash={gloabalObjectId}]. NetworkPrefab could not be found. Is the prefab registered with NetworkManager?"; - var secondError = $"[Netcode] Failed to spawn NetworkObject for Hash {gloabalObjectId}."; m_InstantiateAndSpawnType = instantiateAndSpawnType; // We have to spawn the first client manually in order to account for the errors when scene management is disabled. @@ -224,7 +224,6 @@ public IEnumerator TestPrefabsSpawning([Values] InstantiateAndSpawnMethods insta // spawn the original prefab and when spawning dynamically the override is used. yield return CreateAndStartNewClient(); - var spawnManager = m_ServerNetworkManager.SpawnManager; // If scene management is enabled, then we want to verify against the editor // assigned in-scene placed NetworkObjects if (m_SceneManagementEnabled) diff --git a/testproject/Assets/Tests/Runtime/RespawnInSceneObjectsAfterShutdown.cs b/testproject/Assets/Tests/Runtime/RespawnInSceneObjectsAfterShutdown.cs index 7dbdc2635f..857c14ee6f 100644 --- a/testproject/Assets/Tests/Runtime/RespawnInSceneObjectsAfterShutdown.cs +++ b/testproject/Assets/Tests/Runtime/RespawnInSceneObjectsAfterShutdown.cs @@ -8,8 +8,11 @@ namespace TestProject.RuntimeTests { - [TestFixture(NetworkTopologyTypes.DistributedAuthority)] - [TestFixture(NetworkTopologyTypes.ClientServer)] + [TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)] + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.UnifiedHost)] +#endif public class RespawnInSceneObjectsAfterShutdown : NetcodeIntegrationTest { public const string SceneToLoad = "InSceneNetworkObject"; @@ -23,7 +26,7 @@ protected override bool UseCMBService() return false; } - public RespawnInSceneObjectsAfterShutdown(NetworkTopologyTypes networkTopologyType) : base(networkTopologyType) { } + public RespawnInSceneObjectsAfterShutdown(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { } protected override void OnOneTimeSetup() { diff --git a/testproject/Assets/Tests/Runtime/RpcObserverTests.cs b/testproject/Assets/Tests/Runtime/RpcObserverTests.cs index b71c7e03dd..3ce2642a6c 100644 --- a/testproject/Assets/Tests/Runtime/RpcObserverTests.cs +++ b/testproject/Assets/Tests/Runtime/RpcObserverTests.cs @@ -18,6 +18,10 @@ namespace TestProject.RuntimeTests [TestFixture(HostOrServer.DAHost)] [TestFixture(HostOrServer.Host)] [TestFixture(HostOrServer.Server)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] + [TestFixture(HostOrServer.UnifiedServer)] +#endif public class RpcObserverTests : NetcodeIntegrationTest { protected override int NumberOfClients => 9; @@ -155,6 +159,12 @@ private IEnumerator RunRpcObserverTest(List nonObservers) [UnityTest] public IEnumerator DespawnRespawnObserverTest() { +#if UNIFIED_NETCODE + if (m_AllPrefabsAsHybrid) + { + Assert.Ignore("Hybrid spawning does not support despawn-without-destroy."); + } +#endif var nonObservers = new List(); m_ServerRpcObserverObject.ResetTest(); // Wait for all clients to report they have spawned an instance of our test prefab diff --git a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs index 228bfbfdd5..2edec8d26a 100644 --- a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs +++ b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs @@ -6,10 +6,13 @@ using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; using UnityEngine.TestTools; -using Debug = UnityEngine.Debug; namespace TestProject.RuntimeTests { + [TestFixture(HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] +#endif public class RpcTestsAutomated : NetcodeIntegrationTest { private bool m_TimedOut; @@ -23,6 +26,11 @@ protected override bool UseCMBService() return false; } + public RpcTestsAutomated(HostOrServer hostOrServer) : base(hostOrServer) + { + + } + protected override NetworkManagerInstatiationMode OnSetIntegrationTestMode() { return NetworkManagerInstatiationMode.DoNotCreate; @@ -120,16 +128,16 @@ private IEnumerator AutomatedRpcTestsHandler(int numClients) Assert.IsFalse(m_TimedOut); // Log the output for visual confirmation (Acceptance Test for this test) that all RPC test types (tracked by counters) executed multiple times - Debug.Log("Final Host-Server Status Info:"); - Debug.Log(serverRpcTests.GetCurrentServerStatusInfo()); + VerboseDebug("Final Host-Server Status Info:"); + VerboseDebug(serverRpcTests.GetCurrentServerStatusInfo()); foreach (var rpcClientSideTest in clientRpcQueueManualTestInstsances) { - Debug.Log($"Final Client {rpcClientSideTest.NetworkManager.LocalClientId} Status Info:"); - Debug.Log(rpcClientSideTest.GetCurrentClientStatusInfo()); + VerboseDebug($"Final Client {rpcClientSideTest.NetworkManager.LocalClientId} Status Info:"); + VerboseDebug(rpcClientSideTest.GetCurrentClientStatusInfo()); } - Debug.Log($"Total frames updated = {Time.frameCount - startFrameCount} within {Time.realtimeSinceStartup - startTime} seconds."); + VerboseDebug($"Total frames updated = {Time.frameCount - startFrameCount} within {Time.realtimeSinceStartup - startTime} seconds."); } } } diff --git a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs index ae10ea60c9..5a92112f32 100644 --- a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs +++ b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs @@ -49,8 +49,11 @@ public void NetworkSerialize(BufferSerializer } - [TestFixture(NetworkTopologyTypes.DistributedAuthority)] - [TestFixture(NetworkTopologyTypes.ClientServer)] + [TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)] + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.UnifiedHost)] +#endif public class RpcUserSerializableTypesTest : NetcodeIntegrationTest { private UserSerializableClass m_UserSerializableClass; @@ -88,7 +91,7 @@ protected override bool UseCMBService() return false; } - public RpcUserSerializableTypesTest(NetworkTopologyTypes networkTopologyType) : base(networkTopologyType) { } + public RpcUserSerializableTypesTest(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { } protected override NetworkManagerInstatiationMode OnSetIntegrationTestMode() { diff --git a/testproject/Assets/Tests/Runtime/SceneObjectsNotDestroyedOnShutdownTest.cs b/testproject/Assets/Tests/Runtime/SceneObjectsNotDestroyedOnShutdownTest.cs index f82b7ed400..3fcc9feae6 100644 --- a/testproject/Assets/Tests/Runtime/SceneObjectsNotDestroyedOnShutdownTest.cs +++ b/testproject/Assets/Tests/Runtime/SceneObjectsNotDestroyedOnShutdownTest.cs @@ -9,8 +9,11 @@ namespace TestProject.RuntimeTests { - [TestFixture(NetworkTopologyTypes.ClientServer)] - [TestFixture(NetworkTopologyTypes.DistributedAuthority)] + [TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)] + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.UnifiedHost)] +#endif public class SceneObjectsNotDestroyedOnShutdownTest : NetcodeIntegrationTest { protected override int NumberOfClients => 0; @@ -20,7 +23,7 @@ public class SceneObjectsNotDestroyedOnShutdownTest : NetcodeIntegrationTest private Scene m_TestScene; private WaitForSeconds m_DefaultWaitForTick = new(1.0f / 30); - public SceneObjectsNotDestroyedOnShutdownTest(NetworkTopologyTypes topology) : base(topology) { } + public SceneObjectsNotDestroyedOnShutdownTest(NetworkTopologyTypes topology, HostOrServer hostOrServer) : base(topology, hostOrServer) { } [UnityTest] public IEnumerator SceneObjectsNotDestroyedOnShutdown() diff --git a/testproject/Assets/Tests/Runtime/SenderIdTests.cs b/testproject/Assets/Tests/Runtime/SenderIdTests.cs index 92ebf2f4a1..768a51ae53 100644 --- a/testproject/Assets/Tests/Runtime/SenderIdTests.cs +++ b/testproject/Assets/Tests/Runtime/SenderIdTests.cs @@ -10,6 +10,10 @@ namespace TestProject.RuntimeTests { + [TestFixture(HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(HostOrServer.UnifiedHost)] +#endif public class SenderIdTests : NetcodeIntegrationTest { protected override int NumberOfClients => 2; @@ -17,6 +21,8 @@ public class SenderIdTests : NetcodeIntegrationTest private NetworkManager FirstClient => m_ClientNetworkManagers[0]; private NetworkManager SecondClient => m_ClientNetworkManagers[1]; + public SenderIdTests(HostOrServer hostOrServer) : base(hostOrServer) { } + [UnityTest] public IEnumerator WhenSendingMessageFromServerToClient_SenderIdIsCorrect() { diff --git a/testproject/Assets/Tests/Runtime/ServerDisconnectsClientTest.cs b/testproject/Assets/Tests/Runtime/ServerDisconnectsClientTest.cs index c16cceda66..cd730b2099 100644 --- a/testproject/Assets/Tests/Runtime/ServerDisconnectsClientTest.cs +++ b/testproject/Assets/Tests/Runtime/ServerDisconnectsClientTest.cs @@ -9,8 +9,11 @@ namespace TestProject.RuntimeTests { - [TestFixture(NetworkTopologyTypes.DistributedAuthority)] - [TestFixture(NetworkTopologyTypes.ClientServer)] + [TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)] + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)] +#if UNIFIED_NETCODE + [TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.UnifiedHost)] +#endif public class ServerDisconnectsClientTest : NetcodeIntegrationTest { protected override int NumberOfClients => 1; @@ -21,7 +24,7 @@ protected override bool UseCMBService() return false; } - public ServerDisconnectsClientTest(NetworkTopologyTypes networkTopologyType) : base(networkTopologyType) { } + public ServerDisconnectsClientTest(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { } protected override void OnCreatePlayerPrefab() { diff --git a/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef b/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef index d94d369852..51b61982a5 100644 --- a/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef +++ b/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef @@ -4,7 +4,7 @@ "references": [ "Unity.Netcode.Runtime", "Unity.Netcode.RuntimeTests", - "Unity.Netcode.Editor", + "Unity.Netcode.GameObjects.Editor", "TestProject.ManualTests", "TestProject", "Unity.Addressables", @@ -33,7 +33,12 @@ "name": "com.unity.addressables", "expression": "", "define": "TESTPROJECT_USE_ADDRESSABLES" + }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs b/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs deleted file mode 100644 index 8e853a0b19..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.SceneManagement; -using UnityEngine.TestTools; -using Object = UnityEngine.Object; -using Unity.Netcode.Transports.UTP; - - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class MultiprocessTestsAttribute : CategoryAttribute - { - public const string MultiprocessCategoryName = "Multiprocess"; - public MultiprocessTestsAttribute() : base(MultiprocessCategoryName) { } - } - - [MultiprocessTests] - public abstract class BaseMultiprocessTests - { - protected string[] platformList { get; set; } - - protected int GetWorkerCount() - { - platformList = MultiprocessOrchestration.GetRemotePlatformList(); - return platformList == null ? WorkerCount : platformList.Length; - } - protected bool m_LaunchRemotely; - private bool m_HasSceneLoaded = false; - // TODO: Remove UTR check once we have Multiprocess tests fully working - protected bool IgnoreMultiprocessTests => MultiprocessOrchestration.ShouldIgnoreUTRTests(); - - protected virtual bool IsPerformanceTest => false; - - /// - /// Implement this to specify the amount of workers to spawn from your main test runner - /// Note: If using remote workers, the woorker count will come from the environment variable - /// - protected abstract int WorkerCount { get; } - - private const string k_FirstPartOfTestRunnerSceneName = "InitTestScene"; - - // Since we want to additively load our BuildMultiprocessTestPlayer.MainSceneName - // We want to keep a reference to the - private Scene m_OriginalActiveScene; - - [OneTimeSetUp] - public virtual void SetupTestSuite() - { - MultiprocessLogger.Log("Running SetupTestSuite - OneTimeSetup"); - MultiprocessOrchestration.IsPerformanceTest = IsPerformanceTest; - if (IgnoreMultiprocessTests) - { - Assert.Ignore("Ignoring tests under UTR. For testing, include the \"-bypassIgnoreUTR\" command line parameter."); - } - - if (IsPerformanceTest) - { - Assert.Ignore("Performance tests should be run from remote test execution on device (this can be ran using the \"run selected tests (your platform)\" button"); - } - MultiprocessLogger.Log($"Currently active scene {SceneManager.GetActiveScene().name}"); - var currentlyActiveScene = SceneManager.GetActiveScene(); - - // Just adding a sanity check here to help with debugging in the event that SetupTestSuite is - // being invoked and the TestRunner scene has not been set to the active scene yet. - // This could mean that TeardownSuite wasn't called or SceneManager is not finished unloading - // or could not unload the BuildMultiprocessTestPlayer.MainSceneName. - if (!currentlyActiveScene.name.StartsWith(k_FirstPartOfTestRunnerSceneName)) - { - MultiprocessLogger.LogError( - $"Expected the currently active scene to begin with ({k_FirstPartOfTestRunnerSceneName}) but" + - $" currently active scene is {currentlyActiveScene.name}"); - } - m_OriginalActiveScene = currentlyActiveScene; - - SceneManager.sceneLoaded += OnSceneLoaded; - SceneManager.LoadScene(BuildMultiprocessTestPlayer.MainSceneName, LoadSceneMode.Additive); - } - - private void OnSceneLoaded(Scene scene, LoadSceneMode mode) - { - SceneManager.sceneLoaded -= OnSceneLoaded; - if (scene.name == BuildMultiprocessTestPlayer.MainSceneName) - { - SceneManager.SetActiveScene(scene); - } - - var transport = NetworkManager.Singleton.NetworkConfig.NetworkTransport; - switch (transport) - { - case UnityTransport unityTransport: - unityTransport.ConnectionData.ServerListenAddress = "0.0.0.0"; - MultiprocessLogger.Log($"Setting unityTransport.ConnectionData.Port {unityTransport.ConnectionData.ServerListenAddress}"); - break; - default: - MultiprocessLogger.LogError($"The transport {transport} has no case"); - break; - } - - MultiprocessLogger.Log("Starting Host"); - NetworkManager.Singleton.StartHost(); - - // Use scene verification to make sure we don't try to get clients to synchronize the TestRunner scene - NetworkManager.Singleton.SceneManager.VerifySceneBeforeLoading = VerifySceneIsValidForClientsToLoad; - - m_HasSceneLoaded = true; - } - - /// - /// We want to exclude the TestRunner scene on the host-server side so it won't try to tell clients to - /// synchronize to this scene when they connect (host-server side only for multiprocess) - /// - /// true - scene is fine to synchronize/inform clients to load and false - scene should not be loaded by clients - private bool VerifySceneIsValidForClientsToLoad(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode) - { - if (sceneName.StartsWith(k_FirstPartOfTestRunnerSceneName)) - { - return false; - } - return true; - } - - [UnitySetUp] - public virtual IEnumerator Setup() - { - yield return new WaitUntil(() => NetworkManager.Singleton != null); - yield return new WaitUntil(() => NetworkManager.Singleton.IsServer); - yield return new WaitUntil(() => NetworkManager.Singleton.IsListening); - yield return new WaitUntil(() => m_HasSceneLoaded == true); - var startTime = Time.time; - m_LaunchRemotely = MultiprocessOrchestration.IsRemoteOperationEnabled(); - - MultiprocessLogger.Log($"Active Worker Count is {MultiprocessOrchestration.ActiveWorkerCount()}" + - $" and connected client count is {NetworkManager.Singleton.ConnectedClients.Count} " + - $" and WorkerCount is {GetWorkerCount()} " + - $" and LaunchRemotely is {m_LaunchRemotely}"); - if (MultiprocessOrchestration.ActiveWorkerCount() + 1 < NetworkManager.Singleton.ConnectedClients.Count) - { - MultiprocessLogger.Log("Is this a bad state?"); - } - - // Moved this out of OnSceneLoaded as OnSceneLoaded is a callback from the SceneManager and just wanted to avoid creating - // processes from within the same callstack/context as the SceneManager. This will instantiate up to the WorkerCount and - // then any subsequent calls to Setup if there are already workers it will skip this step - if (!m_LaunchRemotely) - { - if (NetworkManager.Singleton.ConnectedClients.Count - 1 < WorkerCount) - { - var numProcessesToCreate = WorkerCount - (NetworkManager.Singleton.ConnectedClients.Count - 1); - for (int i = 1; i <= numProcessesToCreate; i++) - { - MultiprocessLogger.Log($"Spawning testplayer {i} since connected client count is {NetworkManager.Singleton.ConnectedClients.Count} is less than {WorkerCount} and Number of spawned external players is {MultiprocessOrchestration.ActiveWorkerCount()} "); - string logPath = MultiprocessOrchestration.StartWorkerNode(); // will automatically start built player as clients - MultiprocessLogger.Log($"logPath to new process is {logPath}"); - MultiprocessLogger.Log($"Active Worker Count {MultiprocessOrchestration.ActiveWorkerCount()} and connected client count is {NetworkManager.Singleton.ConnectedClients.Count}"); - } - } - } - else - { - var launchProcessList = new List(); - if (NetworkManager.Singleton.ConnectedClients.Count - 1 < GetWorkerCount()) - { - var machines = MultiprocessOrchestration.GetRemoteMachineList(); - foreach (var machine in machines) - { - MultiprocessLogger.Log($"Would launch on {machine.Name} too get worker count to {GetWorkerCount()} from {NetworkManager.Singleton.ConnectedClients.Count - 1}"); - launchProcessList.Add(MultiprocessOrchestration.StartWorkersOnRemoteNodes(machine)); - } - } - } - - var timeOutTime = Time.realtimeSinceStartup + TestCoordinator.MaxWaitTimeoutSec; - while (NetworkManager.Singleton.ConnectedClients.Count <= WorkerCount) - { - yield return new WaitForSeconds(0.2f); - - if (Time.realtimeSinceStartup > timeOutTime) - { - throw new Exception($" {DateTime.Now:T} Waiting too long to see clients to connect, got {NetworkManager.Singleton.ConnectedClients.Count - 1} clients, and ActiveWorkerCount: {MultiprocessOrchestration.ActiveWorkerCount()} but was expecting {WorkerCount}, failing"); - } - } - TestCoordinator.Instance.KeepAliveClientRpc(); - MultiprocessLogger.Log($"Active Worker Count {MultiprocessOrchestration.ActiveWorkerCount()} and connected client count is {NetworkManager.Singleton.ConnectedClients.Count}"); - } - - - [TearDown] - public virtual void Teardown() - { - MultiprocessLogger.Log("Running teardown"); - if (!IgnoreMultiprocessTests) - { - TestCoordinator.Instance.TestRunTeardown(); - } - } - - [OneTimeTearDown] - public virtual void TeardownSuite() - { - MultiprocessLogger.Log($"TeardownSuite"); - if (!IgnoreMultiprocessTests) - { - MultiprocessLogger.Log($"TeardownSuite - ShutdownAllProcesses"); - MultiprocessOrchestration.ShutdownAllProcesses(); - MultiprocessLogger.Log($"TeardownSuite - NetworkManager.Singleton.Shutdown"); - NetworkManager.Singleton.Shutdown(); - Object.Destroy(NetworkManager.Singleton.gameObject); // making sure we clear everything before reloading our scene - MultiprocessLogger.Log($"Currently active scene {SceneManager.GetActiveScene().name}"); - MultiprocessLogger.Log($"m_OriginalActiveScene.IsValid {m_OriginalActiveScene.IsValid()}"); - if (m_OriginalActiveScene.IsValid()) - { - SceneManager.SetActiveScene(m_OriginalActiveScene); - } - MultiprocessLogger.Log($"TeardownSuite - Unload {BuildMultiprocessTestPlayer.MainSceneName}"); - SceneManager.UnloadSceneAsync(BuildMultiprocessTestPlayer.MainSceneName); - MultiprocessLogger.Log($"TeardownSuite - Unload {BuildMultiprocessTestPlayer.MainSceneName}"); - } - } - } -} - diff --git a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs.meta deleted file mode 100644 index 6b52bd9e90..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f77e60aa394b9419784b6c46618fb553 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs deleted file mode 100644 index f09dc0f412..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using NUnit.Framework; -using NUnit.Framework.Interfaces; -using Unity.Netcode; -using Unity.Netcode.MultiprocessRuntimeTests; -using UnityEngine; -using UnityEngine.TestTools; -using Debug = UnityEngine.Debug; - -/// -/// Allows for context based delegate execution. -/// Can specify where you want that lambda executed (client side? server side?) and it'll automatically wait for the end -/// of a clientRPC server side and vice versa. -/// todo this could be used as an in-game tool too? for protocols that require a lot of back and forth? -/// -public class ExecuteStepInContext : CustomYieldInstruction -{ - public enum StepExecutionContext - { - Server, - Clients - } - - [AttributeUsage(AttributeTargets.Method)] - public class MultiprocessContextBasedTestAttribute : NUnitAttribute, IOuterUnityTestAction - { - public IEnumerator BeforeTest(ITest test) - { - yield return new WaitUntil(() => TestCoordinator.Instance != null && HasRegistered); - } - - public IEnumerator AfterTest(ITest test) - { - yield break; - } - } - - private StepExecutionContext m_ActionContext; - private Action m_StepToExecute; - private string m_CurrentActionId; - - // as a remote worker, I store all available actions so I can execute them when triggered from RPCs - public static Dictionary AllActions = new Dictionary(); - private static Dictionary s_MethodIdCounter = new Dictionary(); - - private NetworkManager m_NetworkManager; - private bool m_IsRegistering; - private List> m_ClientIsFinishedChecks = new List>(); - private Func m_AdditionalIsFinishedWaiter; - - private bool m_WaitMultipleUpdates; - private bool m_IgnoreTimeoutException; - - private float m_StartTime; - private bool isTimingOut => Time.time - m_StartTime > TestCoordinator.MaxWaitTimeoutSec; - private bool shouldExecuteLocally => (m_ActionContext == StepExecutionContext.Server && m_NetworkManager.IsServer) || (m_ActionContext == StepExecutionContext.Clients && !m_NetworkManager.IsServer); - - public static bool IsRegistering; - public static bool HasRegistered; - private static List s_AllClientTestInstances = new List(); // to keep an instance for each tests, so captured context in each step is kept - - /// - /// This MUST be called at the beginning of each test in order to use context based steps. - /// Assumes this is called from same callsite as ExecuteStepInContext (and assumes this is called from IEnumerator, the method full name is unique - /// even with the same method name and different parameters). - /// This relies on the name to be unique for each generated IEnumerator state machines - /// - public static void InitializeContextSteps() - { - var callerMethod = new StackFrame(1).GetMethod(); - var methodIdentifier = GetMethodIdentifier(callerMethod); // since this is called from IEnumerator, this should be a generated class, making it unique - s_MethodIdCounter[methodIdentifier] = 0; - } - - private static string GetMethodIdentifier(MethodBase callerMethod) - { - return callerMethod.DeclaringType.FullName; - } - - internal static void InitializeAllSteps() - { - MultiprocessLogger.Log("InitializeAllSteps - Start"); - // registering magically all context based steps - IsRegistering = true; - var registeredMethods = typeof(TestCoordinator).Assembly.GetTypes().SelectMany(t => t.GetMethods()) - .Where(m => m.GetCustomAttributes(typeof(MultiprocessContextBasedTestAttribute), true).Length > 0) - .ToArray(); - var typesWithContextMethods = new HashSet(); - foreach (var method in registeredMethods) - { - typesWithContextMethods.Add(method.ReflectedType); - } - - if (registeredMethods.Length == 0) - { - throw new Exception($"Couldn't find any registered methods for multiprocess testing. Is {nameof(TestCoordinator)} in same assembly as test methods?"); - } - - object[] GetParameterValuesToPassFunc(ParameterInfo[] parameterInfo) - { - object[] parametersToReturn = new object[parameterInfo.Length]; - for (int i = 0; i < parameterInfo.Length; i++) - { - var paramType = parameterInfo[i].GetType(); - object defaultObj = null; - if (paramType.IsValueType) - { - defaultObj = Activator.CreateInstance(paramType); - } - - parametersToReturn[i] = defaultObj; - } - - return parametersToReturn; - } - - foreach (var contextType in typesWithContextMethods) - { - var allConstructors = contextType.GetConstructors(); - if (allConstructors.Length > 1) - { - throw new NotImplementedException("Case not implemented where test has more than one constructor"); - } - - var instance = Activator.CreateInstance(contextType, allConstructors.Length > 0 ? GetParameterValuesToPassFunc(allConstructors[0].GetParameters()) : null); - s_AllClientTestInstances.Add(instance); // keeping that instance so tests can use captured local attributes - - var typeMethodsWithContextSteps = new List(); - foreach (var method in contextType.GetMethods()) - { - if (method.GetCustomAttributes(typeof(MultiprocessContextBasedTestAttribute), true).Length > 0) - { - typeMethodsWithContextSteps.Add(method); - } - } - - foreach (var method in typeMethodsWithContextSteps) - { - var parametersToPass = GetParameterValuesToPassFunc(method.GetParameters()); - var enumerator = (IEnumerator)method.Invoke(instance, parametersToPass.ToArray()); - while (enumerator.MoveNext()) { } - } - } - - IsRegistering = false; - HasRegistered = true; - MultiprocessLogger.Log("InitializeAllSteps - Done"); - } - - /// - /// Executes an action with the specified context. This allows writing tests all in the same sequential flow, - /// making it more readable. This allows not having to jump between static client methods and test method - /// - /// context to use. for example, should execute client side? server side? - /// action to execute - /// waits for timeout and just finishes step execution silently - /// parameters to pass to action - /// - /// waits multiple frames before allowing the execution to continue. This means ClientFinishedServerRpc must be called manually - /// - public ExecuteStepInContext(StepExecutionContext actionContext, Action stepToExecute, bool ignoreTimeoutException = false, byte[] paramToPass = default, NetworkManager networkManager = null, bool waitMultipleUpdates = false, Func additionalIsFinishedWaiter = null) - { - m_StartTime = Time.time; - m_IsRegistering = IsRegistering; - m_ActionContext = actionContext; - m_StepToExecute = stepToExecute; - m_WaitMultipleUpdates = waitMultipleUpdates; - m_IgnoreTimeoutException = ignoreTimeoutException; - - if (additionalIsFinishedWaiter != null) - { - m_AdditionalIsFinishedWaiter = additionalIsFinishedWaiter; - } - - if (networkManager == null) - { - networkManager = NetworkManager.Singleton; - } - - m_NetworkManager = networkManager; // todo test using this for multiinstance tests too? - - var callerMethod = new StackFrame(1).GetMethod(); // one skip frame for current method - - var methodId = GetMethodIdentifier(callerMethod); // assumes called from IEnumerator MoveNext, which should be the case since we're a CustomYieldInstruction. This will return a generated class name which should be unique - if (!s_MethodIdCounter.ContainsKey(methodId)) - { - s_MethodIdCounter[methodId] = 0; - } - - string currentActionId = $"{methodId}-{s_MethodIdCounter[methodId]++}"; - m_CurrentActionId = currentActionId; - - if (m_IsRegistering) - { - Assert.That(AllActions, Does.Not.Contain(currentActionId)); // sanity check - AllActions[currentActionId] = this; - MultiprocessLogger.Log($"InitializeAllSteps - Registering {currentActionId}"); - } - else - { - MultiprocessLogger.Log($"InitializeAllSteps - Not Registering {currentActionId}"); - if (shouldExecuteLocally) - { - m_StepToExecute.Invoke(paramToPass); - } - else - { - if (networkManager.IsServer) - { - TestCoordinator.Instance.TriggerActionIdClientRpc(currentActionId, paramToPass, m_IgnoreTimeoutException, - clientRpcParams: new ClientRpcParams - { - Send = new ClientRpcSendParams { TargetClientIds = TestCoordinator.AllClientIdsExceptMine.ToArray() } - }); - foreach (var clientId in TestCoordinator.AllClientIdsExceptMine) - { - m_ClientIsFinishedChecks.Add(TestCoordinator.ConsumeClientIsFinished(clientId)); - } - } - else - { - throw new NotImplementedException(); - } - } - } - } - - public void Invoke(byte[] args) - { - m_StepToExecute.Invoke(args); - if (!m_WaitMultipleUpdates) - { - if (!m_NetworkManager.IsServer) - { - TestCoordinator.Instance.ClientFinishedServerRpc(); - } - else - { - throw new NotImplementedException("todo implement"); - } - } - } - - public override bool keepWaiting - { - get - { - if (isTimingOut) - { - if (m_IgnoreTimeoutException) - { - Debug.LogWarning($"Timeout ignored for action ID {m_CurrentActionId}"); - return false; - } - - throw new Exception($"timeout for Context Step with action ID {m_CurrentActionId}"); - } - - if (m_AdditionalIsFinishedWaiter != null) - { - var isFinished = m_AdditionalIsFinishedWaiter.Invoke(); - if (!isFinished) - { - return true; - } - } - - if (m_IsRegistering || shouldExecuteLocally || m_ClientIsFinishedChecks == null) - { - return false; - } - - for (int i = m_ClientIsFinishedChecks.Count - 1; i >= 0; i--) - { - if (m_ClientIsFinishedChecks[i].Invoke()) - { - m_ClientIsFinishedChecks.RemoveAt(i); - } - else - { - return true; - } - } - - return false; - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs.meta b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs.meta deleted file mode 100644 index 2b5f013d83..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1ff1efc1d00c64914905497db918aadc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs deleted file mode 100644 index ebacd0ecda..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Collections; -using System.Text.RegularExpressions; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; -using static ExecuteStepInContext; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - /// - /// Smoke tests for ExecuteStepInContext, to make sure it's working properly before being used in other tests - /// - [TestFixture(1)] - [TestFixture(2)] - public class ExecuteStepInContextTests : BaseMultiprocessTests - { - private int m_WorkerCountToTest; - - public ExecuteStepInContextTests(int workerCountToTest) - { - m_WorkerCountToTest = workerCountToTest; - } - - protected override int WorkerCount => m_WorkerCountToTest; - protected override bool IsPerformanceTest => false; - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestWithSameName([Values(1)] int a) - { - // ExecuteStepInContext bases itself on method name to identify steps. We need to make sure that methods with - // same names, but different signatures behave correctly - InitializeContextSteps(); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.That(a, Is.EqualTo(1)); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Assert.That(BitConverter.ToInt32(bytes, 0), Is.EqualTo(1)); - }, paramToPass: BitConverter.GetBytes(a)); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestWithSameName([Values(2)] int a, [Values(3)] int b) - { - InitializeContextSteps(); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.That(b, Is.EqualTo(3)); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Assert.That(BitConverter.ToInt32(bytes, 0), Is.EqualTo(3)); - }, paramToPass: BitConverter.GetBytes(b)); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestWithParameters([Values(1, 2, 3)] int a) - { - InitializeContextSteps(); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.Less(a, 4); - Assert.Greater(a, 0); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - var clientA = BitConverter.ToInt32(bytes, 0); - Assert.True(!NetworkManager.Singleton.IsServer); - Assert.Less(clientA, 4); - Assert.Greater(clientA, 0); - }, paramToPass: BitConverter.GetBytes(a)); - } - - [UnityTest, MultiprocessContextBasedTest] - [TestCase(1, 2, ExpectedResult = null)] - [TestCase(2, 3, ExpectedResult = null)] - [TestCase(3, 4, ExpectedResult = null)] - public IEnumerator TestWithParameters(int a, int b) - { - InitializeContextSteps(); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.Less(a, 4); - Assert.Greater(a, 0); - Assert.Less(b, 5); - Assert.Greater(b, 1); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - var clientB = BitConverter.ToInt32(bytes, 0); - Assert.True(!NetworkManager.Singleton.IsServer); - Assert.Less(clientB, 5); - Assert.Greater(clientB, 1); - }, paramToPass: BitConverter.GetBytes(b)); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestExceptionClientSide() - { - InitializeContextSteps(); - - const string exceptionMessageToTest = "This is an exception for TestCoordinator that's expected"; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - throw new Exception(exceptionMessageToTest); - }, ignoreTimeoutException: true); - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - for (int i = 0; i < m_WorkerCountToTest; i++) - { - LogAssert.Expect(LogType.Error, new Regex($".*{exceptionMessageToTest}.*")); - } - }); - - const string exceptionUpdateMessageToTest = "This is an exception for update loop client side that's expected"; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - void UpdateFunc(float _) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - throw new Exception(exceptionUpdateMessageToTest); - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, ignoreTimeoutException: true); - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - for (int i = 0; i < m_WorkerCountToTest; i++) - { - LogAssert.Expect(LogType.Error, new Regex($".*{exceptionUpdateMessageToTest}.*")); - } - }); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator ContextTestWithAdditionalWait() - { - InitializeContextSteps(); - - const int maxValue = 10; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - int count = 0; - - void UpdateFunc(float _) - { - TestCoordinator.Instance.WriteTestResultsServerRpc(count++); - if (count > maxValue) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, additionalIsFinishedWaiter: () => - { - int nbFinished = 0; - for (int i = 0; i < m_WorkerCountToTest; i++) - { - if (TestCoordinator.PeekLatestResult(TestCoordinator.AllClientIdsExceptMine[i]) == maxValue) - { - nbFinished++; - } - } - - return nbFinished == m_WorkerCountToTest; - }); - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - Assert.That(TestCoordinator.AllClientIdsExceptMine.Count, Is.EqualTo(m_WorkerCountToTest)); - foreach (var clientId in TestCoordinator.AllClientIdsExceptMine) - { - var current = 0; - foreach (var res in TestCoordinator.ConsumeCurrentResult(clientId)) - { - Assert.That(res, Is.EqualTo(current++)); - } - - Assert.That(current - 1, Is.EqualTo(maxValue)); - } - }); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestExecuteInContext() - { - InitializeContextSteps(); - - int stepCountExecuted = 0; - yield return new ExecuteStepInContext(StepExecutionContext.Server, args => - { - stepCountExecuted++; - int count = BitConverter.ToInt32(args, 0); - Assert.That(count, Is.EqualTo(1)); - }, paramToPass: BitConverter.GetBytes(1)); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, args => - { - int count = BitConverter.ToInt32(args, 0); - Assert.That(count, Is.EqualTo(2)); - TestCoordinator.Instance.WriteTestResultsServerRpc(12345); -#if UNITY_EDITOR - Assert.Fail("Should not be here!! This should only execute on client!!"); -#endif - }, paramToPass: BitConverter.GetBytes(2)); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - stepCountExecuted++; - int resultCountFromWorkers = 0; - foreach (var res in TestCoordinator.ConsumeCurrentResult()) - { - resultCountFromWorkers++; - Assert.AreEqual(12345, res.result); - } - - Assert.That(resultCountFromWorkers, Is.EqualTo(WorkerCount)); - }); - - const int timeToWait = 4; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - void UpdateFunc(float _) - { - if (Time.time > timeToWait) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - TestCoordinator.Instance.WriteTestResultsServerRpc(Time.time); - - TestCoordinator.Instance.ClientFinishedServerRpc(); // since finishOnInvoke is false, we need to do this manually - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, waitMultipleUpdates: true); // waits multiple frames before allowing the next action to continue. - - yield return new ExecuteStepInContext(StepExecutionContext.Server, args => - { - stepCountExecuted++; - int count = 0; - foreach (var res in TestCoordinator.ConsumeCurrentResult()) - { - count++; - Assert.GreaterOrEqual(res.result, timeToWait); - } - - Assert.Greater(count, 0); - }); - - if (!IsRegistering) - { - Assert.AreEqual(3, stepCountExecuted); - } - } - } -} - diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs.meta deleted file mode 100644 index 3f89ca9cb5..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 80b877babc0ce4b0d8cf31e73216d49a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs deleted file mode 100644 index b992dfb53b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.IO; -#if UNITY_EDITOR -using UnityEditor; -using UnityEditor.Build.Reporting; -#endif -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - /// - /// This is needed as Unity throws "An abnormal situation has occurred: the PlayerLoop internal function has been called recursively. Please contact Customer Support with a sample project so that we can reproduce the problem and troubleshoot it." - /// when trying to build from Setup() steps in tests. - /// - public static class BuildMultiprocessTestPlayer - { - public const string MultiprocessBaseMenuName = "Netcode/Multiprocess Test"; - public const string BuildAndExecuteMenuName = MultiprocessBaseMenuName + "/Build Test Player #t"; - public const string MainSceneName = "MultiprocessTestScene"; - - private static string BuildPathDirectory => Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", "MultiprocessTests"); - public static string BuildPath => Path.Combine(BuildPathDirectory, "MultiprocessTestPlayer"); - public const string BuildInfoFileName = "BuildInfo.json"; - -#if UNITY_EDITOR - /// - /// Build the standalone player on the current platform - /// This method is both a menu item as well as a public method that can be called from CI - /// in order to build the standalone player - /// - [MenuItem(BuildAndExecuteMenuName)] - public static void BuildRelease() - { - var report = BuildPlayerUtility(); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build Test Player (Debug)")] - public static void BuildDebug() - { - var report = BuildPlayerUtility(BuildTarget.NoTarget, null, true); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Delete Test Build")] - public static void DeleteBuild() - { - if (Directory.Exists(BuildPathDirectory)) - { - Directory.Delete(BuildPathDirectory, recursive: true); - } - else - { - Debug.Log($"[{nameof(BuildMultiprocessTestPlayer)}] build directory does not exist ({BuildPathDirectory}) not deleting anything"); - } - } - - private static BuildReport BuildPlayerUtility(BuildTarget buildTarget = BuildTarget.NoTarget, string buildPathExtension = null, bool buildDebug = false) - { - SaveBuildInfo(new BuildInfo() { BuildPath = BuildPath }); - - // deleting so we don't end up testing on outdated builds if there's a build failure - DeleteBuild(); - - if (buildTarget == BuildTarget.NoTarget) - { - if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor) - { - buildPathExtension += ".exe"; - buildTarget = BuildTarget.StandaloneWindows64; - } - else if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor) - { - buildPathExtension += ".app"; - buildTarget = BuildTarget.StandaloneOSX; - } - else if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxPlayer) - { - buildPathExtension += ""; - buildTarget = BuildTarget.StandaloneLinux64; - } - } - - var buildPathToUse = BuildPath; - buildPathToUse += buildPathExtension; - - var buildPlayerOptions = new BuildPlayerOptions - { - scenes = new[] { "Assets/Scenes/MultiprocessTestScene.unity" }, - locationPathName = buildPathToUse, - target = buildTarget - }; - var buildOptions = BuildOptions.None; - if (buildDebug || buildTarget == BuildTarget.Android) - { - buildOptions |= BuildOptions.Development; - buildOptions |= BuildOptions.AllowDebugging; - } - - buildOptions |= BuildOptions.StrictMode; - buildOptions |= BuildOptions.IncludeTestAssemblies; - buildPlayerOptions.options = buildOptions; - - BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions); - BuildSummary summary = report.summary; - - if (summary.result == BuildResult.Succeeded) - { - Debug.Log($"Build succeeded: {summary.totalSize} bytes at {summary.outputPath}"); - } - - return report; - } - - [MenuItem(MultiprocessBaseMenuName + "/Windows Standalone Player")] - public static void BuildWindowsStandalonePlayer() - { - var report = BuildPlayerUtility(BuildTarget.StandaloneWindows64, ".exe"); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build OSX")] - public static void BuildOSX() - { - var report = BuildPlayerUtility(BuildTarget.StandaloneOSX, ".app"); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build Linux")] - public static void BuildLinux() - { - var report = BuildPlayerUtility(BuildTarget.StandaloneLinux64, ""); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build Android")] - public static void BuildAndroid() - { - var report = BuildPlayerUtility(BuildTarget.Android, ".apk"); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } -#endif - - [Serializable] - public struct BuildInfo - { - public string BuildPath; - public bool IsDebug; - } - - public static bool DoesBuildInfoExist() - { - var buildfileInfo = new FileInfo(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName)); - return buildfileInfo.Exists; - } - - public static BuildInfo ReadBuildInfo() - { - var jsonString = File.ReadAllText(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName)); - return JsonUtility.FromJson(jsonString); - } - - public static void SaveBuildInfo(BuildInfo toSave) - { - var buildInfoJson = JsonUtility.ToJson(toSave); - File.WriteAllText(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName), buildInfoJson); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta deleted file mode 100644 index 87acf05da8..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b389565fd8544431db4c24940cb569c6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs deleted file mode 100644 index df0e14f61c..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using UnityEngine; - -/// -/// Component who's purpose is to expose callbacks to code tests -/// -public class CallbackComponent : MonoBehaviour -{ - public Action OnUpdate; - - // Update is called once per frame - private void Update() - { - try - { - OnUpdate?.Invoke(Time.deltaTime); - } - catch (Exception e) - { - TestCoordinator.Instance.WriteErrorServerRpc(e.ToString()); - throw; - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs.meta deleted file mode 100644 index 7347e89e7b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 55d1c75ce242745ac98f7e7aca6d2d19 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs deleted file mode 100644 index efca1c8970..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class ConfigurationTools - { - public static async Task GetRemoteConfig() - { - var theList = new JobQueueItemArray(); - using var client = new HttpClient(); - var cancelAfterDelay = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var response = await client.GetAsync("https://multiprocess-log-event-manager.cds.internal.unity3d.com/api/JobWithFile", - HttpCompletionOption.ResponseHeadersRead, cancelAfterDelay.Token).ConfigureAwait(false); - var content = await response.Content.ReadAsStringAsync(); - JsonUtility.FromJsonOverwrite(content, theList); - return theList; - } - - public static async void CompleteJobQueueItem(JobQueueItem item) - { - await PostJobQueueItem(item, "/complete"); - } - - public static async void ClaimJobQueueItem(JobQueueItem item) - { - await PostJobQueueItem(item, "/claim"); - } - - public static async Task PostJobQueueItem(JobQueueItem item, string path = "") - { - using var client = new HttpClient(); - using var request = new HttpRequestMessage(HttpMethod.Post, "https://multiprocess-log-event-manager.cds.internal.unity3d.com/api/JobWithFile" + path); - var json = JsonUtility.ToJson(item); - using var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - request.Content = stringContent; - MultiprocessLogger.Log($"Posting remoteConfig to server {json}"); - var cancelAfterDelay = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - var response = await client - .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancelAfterDelay.Token).ConfigureAwait(false); - MultiprocessLogger.Log($"remoteConfig posted, checking response {response.StatusCode}"); - } - } - - [Serializable] - public class JobQueueItemArray - { - public List JobQueueItems; - } - - [Serializable] - public class JobQueueItem - { - public int Id; - public long JobId; - public string GitHash; - public string HostIp; - public int PlatformId; - public int JobStateId; - public string CreatedBy; - public string UpdatedBy; - public string TransportName; - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs.meta deleted file mode 100644 index daf965ddca..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ad11397b80c04404cb24e82cc7872334 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs deleted file mode 100644 index bb9d097f62..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs +++ /dev/null @@ -1,9 +0,0 @@ - -public enum ConfigurationType -{ - Unknown, - Remote, - CommandLine, - ResourceFile, - Host -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs.meta deleted file mode 100644 index 9e89a3cb23..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5b189bac115ca4587ba64943bfcc67f6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs deleted file mode 100644 index 41558e04dc..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class CustomPrefabSpawnerForPerformanceTests : INetworkPrefabInstanceHandler, IDisposable where T : NetworkBehaviour - { - private GameObjectPool m_ObjectPool; - private Action m_SetupSpawnedObject; - private Action m_OnRelease; - - public CustomPrefabSpawnerForPerformanceTests(T prefabToSpawn, int maxObjectsToSpawn, Action setupSpawnedObject, Action onRelease) - { - m_ObjectPool = new GameObjectPool(); - m_ObjectPool.Initialize(maxObjectsToSpawn, prefabToSpawn); - m_SetupSpawnedObject = setupSpawnedObject; - m_OnRelease = onRelease; - } - - public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) - { - var netBehaviour = m_ObjectPool.Get(); - var networkObject = netBehaviour.NetworkObject; - Transform netTransform = networkObject.transform; - netTransform.position = position; - netTransform.rotation = rotation; - m_SetupSpawnedObject(netBehaviour); - return networkObject; - } - - public void Destroy(NetworkObject networkObject) - { - var behaviour = networkObject.gameObject.GetComponent(); // todo expensive, only used in teardown for now, should optimize eventually - m_OnRelease(behaviour); - Transform netTransform = networkObject.transform; - netTransform.position = Vector3.zero; - netTransform.rotation = Quaternion.identity; - m_ObjectPool.Release(behaviour); - } - - public void Dispose() - { - m_ObjectPool.Dispose(); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs.meta deleted file mode 100644 index e36dcb4466..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dbe82ff88ee654428b03632ec6ffff07 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs deleted file mode 100644 index 41224a47c5..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using Object = UnityEngine.Object; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - /// - /// Have to implement our own pool here for compatibility with Unity 2020LTS - /// This shouldn't be needed if we were supporting only 2021 (and its new Pool) - /// - public class GameObjectPool : IDisposable where T : NetworkBehaviour - { - private List m_AllGameObject; - private Stack m_FreeIndexes; - private Dictionary m_ReverseLookup = new Dictionary(); - - public void Initialize(int originalCount, T prefabToSpawn) - { - m_AllGameObject = new List(originalCount); - m_FreeIndexes = new Stack(originalCount); - for (int i = 0; i < originalCount; i++) - { - var go = Object.Instantiate(prefabToSpawn); - go.gameObject.SetActive(false); - m_AllGameObject.Add(go); - m_FreeIndexes.Push(i); - m_ReverseLookup[go] = i; - } - } - - public void Dispose() - { - foreach (var gameObject in m_AllGameObject) - { - Object.Destroy(gameObject); - } - m_AllGameObject = null; - m_FreeIndexes = null; - m_ReverseLookup = null; - } - - public T Get() - { - if (m_FreeIndexes.Count == 0) - { - throw new Exception("Pool full!"); - } - var o = m_AllGameObject[m_FreeIndexes.Pop()]; - o.gameObject.SetActive(true); - return o; - } - - public void Release(T toRelease) - { - int index = m_ReverseLookup[toRelease]; - m_FreeIndexes.Push(index); - toRelease.gameObject.SetActive(false); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs.meta deleted file mode 100644 index 5a5f42a8e8..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: dc81fb2a3c7347a3a0b5e98d2ba49ed7 -timeCreated: 1622655847 \ No newline at end of file diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs deleted file mode 100644 index 37ea12c6ee..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using NUnit.Framework; -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class MultiprocessLogger - { - private static Logger s_Logger; - - static MultiprocessLogger() => s_Logger = new Logger(logHandler: new MultiprocessLogHandler()); - - public static void Log(string msg) - { - s_Logger.Log(msg); - } - - public static void LogError(string msg) - { - s_Logger.LogError("", msg); - } - - public static void LogWarning(string msg) - { - s_Logger.LogWarning("", msg); - } - } - - public class MultiprocessLogHandler : ILogHandler - { - public static long JobId; - static MultiprocessLogHandler() - { - if (JobId == 0) - { - string sJobId = Environment.GetEnvironmentVariable("YAMATO_JOB_ID"); - if (!long.TryParse(sJobId, out JobId)) - { - JobId = -2; - } - } - } - public void LogException(Exception exception, UnityEngine.Object context) - { - Debug.unityLogger.LogException(exception, context); - } - - public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args) - { - string testName = null; - try - { - testName = TestContext.CurrentContext.Test.Name; - } - catch (Exception) - { - // ignored - } - - if (string.IsNullOrEmpty(testName)) - { - testName = "unknown"; - } - - Debug.LogFormat(logType, LogOption.NoStacktrace, context, $"MPLOG({DateTime.Now:T}) : {testName} : {format}", args); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs.meta deleted file mode 100644 index 2c4934f392..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8c1a196a93520415cbf79751b2bb8eee -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs deleted file mode 100644 index 6776bc4151..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs +++ /dev/null @@ -1,278 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using Unity.Netcode.MultiprocessRuntimeTests; -using UnityEngine; -using Debug = UnityEngine.Debug; - -public class MultiprocessOrchestration -{ - public static bool IsPerformanceTest; - public const string IsWorkerArg = "-isWorker"; - private static DirectoryInfo s_MultiprocessDirInfo; - public static DirectoryInfo MultiprocessDirInfo - { - private set => s_MultiprocessDirInfo = value; - get => s_MultiprocessDirInfo ?? initMultiprocessDirinfo(); - } - private static List s_Processes = new List(); - private static int s_TotalProcessCounter = 0; - public static string PathToDll { get; private set; } - public static List ProcessList = new List(); - private static FileInfo s_Localip_fileinfo; - - private static DirectoryInfo initMultiprocessDirinfo() - { - string userprofile = ""; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - userprofile = Environment.GetEnvironmentVariable("USERPROFILE"); - } - else - { - userprofile = Environment.GetEnvironmentVariable("HOME"); - } - s_MultiprocessDirInfo = new DirectoryInfo(Path.Combine(userprofile, ".multiprocess")); - if (!MultiprocessDirInfo.Exists) - { - MultiprocessDirInfo.Create(); - } - s_Localip_fileinfo = new FileInfo(Path.Combine(s_MultiprocessDirInfo.FullName, "localip")); - - return s_MultiprocessDirInfo; - } - - static MultiprocessOrchestration() - { - initMultiprocessDirinfo(); - MultiprocessLogger.Log($" userprofile: {s_MultiprocessDirInfo.FullName} localipfile: {s_Localip_fileinfo}"); - var rootdir_FileInfo = new FileInfo(Path.Combine(MultiprocessDirInfo.FullName, "rootdir")); - MultiprocessLogger.Log($"Checking for the existence of {rootdir_FileInfo.FullName}"); - if (rootdir_FileInfo.Exists) - { - var rootDirText = (File.ReadAllText(rootdir_FileInfo.FullName)).Trim(); - PathToDll = Path.Combine(rootDirText, "multiplayer-multiprocess-test-tools/BokkenForNetcode/ProvisionBokkenMachines/bin/Debug/netcoreapp3.1/osx-x64", "ProvisionBokkenMachines.dll"); - } - else - { - MultiprocessLogger.Log("PathToDll cannot be set as rootDir doesn't exist"); - PathToDll = "unknown"; - } - } - - /// - /// This is to detect if we should ignore Multiprocess tests - /// For testing, include the -bypassIgnoreUTR command line parameter when running UTR. - /// - public static bool ShouldIgnoreUTRTests() - { - return Environment.GetCommandLineArgs().Contains("-automated") && !Environment.GetCommandLineArgs().Contains("-bypassIgnoreUTR"); - } - - public static int ActiveWorkerCount() - { - int activeWorkerCount = 0; - if (s_Processes == null) - { - return activeWorkerCount; - } - - if (s_Processes.Count > 0) - { - foreach (var p in s_Processes) - { - if ((p != null) && (!p.HasExited)) - { - activeWorkerCount++; - } - } - } - return activeWorkerCount; - } - - public static string StartWorkerNode() - { - if (s_Processes == null) - { - s_Processes = new List(); - } - - var workerProcess = new Process(); - s_TotalProcessCounter++; - if (s_Processes.Count > 0) - { - string message = ""; - foreach (var p in s_Processes) - { - message += $" {p.Id} {p.HasExited} {p.StartTime} "; - } - MultiprocessLogger.Log($"Current process count {s_Processes.Count} with data {message}"); - } - - //TODO this should be replaced eventually by proper orchestration for all supported platforms - // Starting new local processes is a solution to help run perf tests locally. CI should have multi machine orchestration to - // run performance tests with more realistic conditions. - string buildInstructions = $"You probably didn't generate your build. Please make sure you build a player using the '{BuildMultiprocessTestPlayer.BuildAndExecuteMenuName}' menu"; - string extraArgs = ""; - try - { - var buildPath = BuildMultiprocessTestPlayer.ReadBuildInfo().BuildPath; - switch (Application.platform) - { - case RuntimePlatform.OSXPlayer: - case RuntimePlatform.OSXEditor: - workerProcess.StartInfo.FileName = $"{buildPath}.app/Contents/MacOS/testproject"; - // extraArgs += "-popupwindow -screen-width 100 -screen-height 100"; - extraArgs += "-batchmode -nographics"; - break; - case RuntimePlatform.WindowsPlayer: - case RuntimePlatform.WindowsEditor: - workerProcess.StartInfo.FileName = $"{buildPath}.exe"; - //extraArgs += "-popupwindow -screen-width 100 -screen-height 100"; - extraArgs += "-popupwindow"; - break; - case RuntimePlatform.LinuxPlayer: - case RuntimePlatform.LinuxEditor: - workerProcess.StartInfo.FileName = $"{buildPath}"; - // extraArgs += "-popupwindow -screen-width 100 -screen-height 100"; - extraArgs += "-batchmode -nographics"; - break; - default: - throw new NotImplementedException($"{nameof(StartWorkerNode)}: Current platform is not supported"); - } - } - catch (FileNotFoundException) - { - Debug.LogError($"Could not find build info file. {buildInstructions}"); - throw; - } - - string logPath = Path.Combine(MultiprocessDirInfo.FullName, $"logfile-mp{s_TotalProcessCounter}.log"); - - workerProcess.StartInfo.UseShellExecute = false; - workerProcess.StartInfo.RedirectStandardError = true; - workerProcess.StartInfo.RedirectStandardOutput = true; - workerProcess.StartInfo.Arguments = $"{IsWorkerArg} {extraArgs} -logFile {logPath}"; - - try - { - MultiprocessLogger.Log($"Attempting to start new process, current process count: {s_Processes.Count} with arguments {workerProcess.StartInfo.Arguments}"); - var newProcessStarted = workerProcess.Start(); - if (!newProcessStarted) - { - throw new Exception("Failed to start worker process!"); - } - s_Processes.Add(workerProcess); - } - catch (Win32Exception e) - { - MultiprocessLogger.LogError($"Error starting player, {buildInstructions}, {e}"); - throw; - } - return logPath; - } - - public static void ShutdownAllProcesses() - { - MultiprocessLogger.Log("Shutting down all processes.."); - foreach (var process in s_Processes) - { - MultiprocessLogger.Log($"Shutting down process {process.Id} with state {process.HasExited}"); - try - { - if (!process.HasExited) - { - // Close process by sending a close message to its main window. - process.CloseMainWindow(); - - // Free resources associated with process. - process.Close(); - } - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - s_Processes.Clear(); - } - - public static bool IsRemoteOperationEnabled() - { - string encodedPlatformList = Environment.GetEnvironmentVariable("MP_PLATFORM_LIST"); - if (encodedPlatformList != null && encodedPlatformList.Split(',').Length > 1) - { - return true; - } - return false; - } - - public static string[] GetRemotePlatformList() - { - // "default-win:test-win,default-mac:test-mac" - if (!IsRemoteOperationEnabled()) - { - return null; - } - string encodedPlatformList = Environment.GetEnvironmentVariable("MP_PLATFORM_LIST"); - string[] separated = encodedPlatformList.Split(','); - return separated; - } - - public static List GetRemoteMachineList() - { - var machineJson = new List(); - foreach (var f in MultiprocessDirInfo.GetFiles("*.json")) - { - if (f.Name.Equals("remoteConfig.json")) - { - continue; - } - else - { - machineJson.Add(f); - } - } - return machineJson; - } - - public static Process StartWorkersOnRemoteNodes(FileInfo machine) - { - string command = $" --command launch " + - $"--input-path {machine.FullName} "; - - var workerProcess = new Process(); - - workerProcess.StartInfo.FileName = Path.Combine("dotnet"); - workerProcess.StartInfo.UseShellExecute = false; - workerProcess.StartInfo.RedirectStandardError = true; - workerProcess.StartInfo.RedirectStandardOutput = true; - workerProcess.StartInfo.Arguments = $"{PathToDll} {command} "; - try - { - var newProcessStarted = workerProcess.Start(); - - if (!newProcessStarted) - { - throw new Exception("Failed to start worker process!"); - } - } - catch (Win32Exception e) - { - MultiprocessLogger.LogError($"Error starting bokken process, {e.Message} {e.Data} {e.ErrorCode}"); - throw; - } - - - ProcessList.Add(workerProcess); - - MultiprocessLogger.Log($"Execute Command: {PathToDll} {command} End"); - return workerProcess; - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta deleted file mode 100644 index 4797a6c5a7..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2b59a46cbb2c54f4d977a05103227453 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs b/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs deleted file mode 100644 index 218c80d61a..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using NUnit.Framework; -using Unity.PerformanceTesting; -using UnityEngine; -using UnityEngine.Profiling; -using UnityEngine.SceneManagement; -using UnityEngine.TestTools; -using static ExecuteStepInContext; -using Object = UnityEngine.Object; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class NetworkVariablePerformanceTests : BaseMultiprocessTests - { - protected override int WorkerCount { get; } = 1; - private const int k_MaxObjectsToSpawn = 10000; - private List m_ServerSpawnedObjects = new List(); - private static GameObjectPool s_ServerObjectPool; - private CustomPrefabSpawnerForPerformanceTests m_ClientPrefabHandler; - private OneNetVar m_PrefabToSpawn; - protected override bool IsPerformanceTest => true; - - private class OneNetVar : NetworkBehaviour - { - public static int InstanceCount; - public NetworkVariable OneInt = new NetworkVariable(); - - public void Initialize() - { - InstanceCount++; - if (IsServer) - { - OneInt.Value = 1; - } - } - - public static void Stop() - { - InstanceCount--; - } - } - - [OneTimeSetUp] - public override void SetupTestSuite() - { - base.SetupTestSuite(); - if (!IgnoreMultiprocessTests) - { - SceneManager.sceneLoaded += OnSceneLoadedInitSetupSuite; - } - } - - private void OnSceneLoadedInitSetupSuite(Scene scene, LoadSceneMode loadSceneMode) - { - SceneManager.sceneLoaded -= OnSceneLoadedInitSetupSuite; - InitializePrefab(); - s_ServerObjectPool = new GameObjectPool(); - s_ServerObjectPool.Initialize(k_MaxObjectsToSpawn, m_PrefabToSpawn); - } - - private void InitializePrefab() - { - if (m_PrefabToSpawn == null) - { - var prefabCopy = Object.Instantiate(PrefabReference.Instance.ReferencedPrefab); - m_PrefabToSpawn = prefabCopy.AddComponent(); - } - } - - [UnityTest, Performance, MultiprocessContextBasedTest] - public IEnumerator TestSpawningManyObjects([Values(1, 2, 1000, 2000, 10000)] int nbObjects) - { - InitializeContextSteps(); - - if (!IsRegistering && TestCoordinator.Instance.NetworkManager.IsServer && BuildMultiprocessTestPlayer.ReadBuildInfo().IsDebug) - { - // build test player in debug mode to enable this - var timeToWait = 20; - Debug.Log($"Debug mode tests enabled, waiting {timeToWait} seconds to give some time to attach debugger"); - yield return new WaitForSeconds(timeToWait); - } - - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - Assert.LessOrEqual(nbObjects, k_MaxObjectsToSpawn); // sanity check - }); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, stepToExecute: nbObjectsBytes => - { - // setup clients - InitializePrefab(); - var targetCount = BitConverter.ToInt32(nbObjectsBytes, 0); - - m_ClientPrefabHandler = new CustomPrefabSpawnerForPerformanceTests(m_PrefabToSpawn, k_MaxObjectsToSpawn, SetupSpawnedObject, StopSpawnedObject); - var hasAddedHandler = NetworkManager.Singleton.PrefabHandler.AddHandler(m_PrefabToSpawn.NetworkObject, m_ClientPrefabHandler); - Assert.That(hasAddedHandler); - - // add client side reporter for later spawn steps - void UpdateFunc(float deltaTime) - { - var count = OneNetVar.InstanceCount; - if (count > 0) - { - TestCoordinator.Instance.WriteTestResultsServerRpc(count); - - if (count >= targetCount) - { - // we got what we want, don't update results any longer - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - } - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, paramToPass: BitConverter.GetBytes(nbObjects)); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - // start test - using (Measure.Scope($"Time Taken For Spawning {nbObjects} objects server side and getting report")) - { - // spawn prefabs for test - var totalAllocSampleGroup = new SampleGroup("GC Alloc", SampleUnit.Kilobyte); - var beforeAllocatedMemory = Profiler.GetTotalAllocatedMemoryLong(); - Measure.Custom(totalAllocSampleGroup, beforeAllocatedMemory / 1024f); - for (int i = 0; i < nbObjects; i++) - { - var spawnedObject = s_ServerObjectPool.Get(); - SetupSpawnedObject(spawnedObject); - spawnedObject.NetworkObject.Spawn(destroyWithScene: true); - m_ServerSpawnedObjects.Add(spawnedObject); - } - - var afterAllocatedMemory = Profiler.GetTotalAllocatedMemoryLong(); - Measure.Custom(totalAllocSampleGroup, afterAllocatedMemory / 1024f); - var diffAllocSampleGroup = new SampleGroup("GC Alloc diff for Spawn Server side", SampleUnit.Byte); - Measure.Custom(diffAllocSampleGroup, afterAllocatedMemory - beforeAllocatedMemory); - } - }, additionalIsFinishedWaiter: () => - { - // wait for spawn results coming from clients - int finishedCount = 0; - if (TestCoordinator.AllClientIdsWithResults.Count != WorkerCount) - { - return false; - } - - foreach (var clientIdWithResult in TestCoordinator.AllClientIdsWithResults) - { - var latestResult = TestCoordinator.PeekLatestResult(clientIdWithResult); - if (latestResult == nbObjects) - { - finishedCount++; - } - } - - return finishedCount == WorkerCount; - }); - - var serverLastResult = 0f; - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - // add measurements - // todo add more client-side metrics like memory usage, time taken to execute, etc - var allocated = new SampleGroup("NbSpawnedPerFrame client side", SampleUnit.Undefined); - - foreach (var clientId in TestCoordinator.AllClientIdsWithResults) - { - var lastResult = TestCoordinator.PeekLatestResult(clientId); - Assert.That(lastResult, Is.EqualTo(nbObjects)); - } - - Assert.That(TestCoordinator.AllClientIdsWithResults.Count, Is.EqualTo(WorkerCount)); - foreach (var (clientId, result) in TestCoordinator.ConsumeCurrentResult()) - { - Measure.Custom(allocated, result); - serverLastResult = result; - } - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, nbObjectsBytes => - { - var nbObjectsParam = BitConverter.ToInt32(nbObjectsBytes, 0); -#if UNITY_2023_1_OR_NEWER - Assert.That(Object.FindObjectsByType(FindObjectsSortMode.None).Length, Is.EqualTo(nbObjectsParam + 1), "Wrong number of spawned objects client side"); // +1 for the prefab to spawn -#else - Assert.That(Object.FindObjectsOfType(typeof(OneNetVar)).Length, Is.EqualTo(nbObjectsParam + 1), "Wrong number of spawned objects client side"); // +1 for the prefab to spawn -#endif - - - }, paramToPass: BitConverter.GetBytes(nbObjects)); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Debug.Log($"finished with test for {nbObjects} expected objects and got {serverLastResult} objects"); - }); - } - - [UnityTearDown, MultiprocessContextBasedTest] - public IEnumerator UnityTeardown() - { - if (!IgnoreMultiprocessTests) - { - InitializeContextSteps(); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - foreach (var spawnedObject in m_ServerSpawnedObjects) - { - spawnedObject.NetworkObject.Despawn(false); - s_ServerObjectPool.Release(spawnedObject); - StopSpawnedObject(spawnedObject); - } - - m_ServerSpawnedObjects.Clear(); - }); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate = null; // todo move access to callbackcomponent to singleton - - void UpdateWaitForAllOneNetVarToDespawnFunc(float deltaTime) - { - if (OneNetVar.InstanceCount == 0) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateWaitForAllOneNetVarToDespawnFunc; - TestCoordinator.Instance.ClientFinishedServerRpc(); - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateWaitForAllOneNetVarToDespawnFunc; - }, waitMultipleUpdates: true, ignoreTimeoutException: true); // ignoring timeout since you don't want to hide any issues in the main tests - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - m_ClientPrefabHandler.Dispose(); - NetworkManager.Singleton.PrefabHandler.RemoveHandler(m_PrefabToSpawn.NetworkObject); - }); - } - yield return null; - } - - [OneTimeTearDown] - public override void TeardownSuite() - { - base.TeardownSuite(); - if (!IsPerformanceTest && !IgnoreMultiprocessTests) - { - s_ServerObjectPool.Dispose(); - } - } - - private static void SetupSpawnedObject(OneNetVar spawnedObject) - { - spawnedObject.Initialize(); - } - - private static void StopSpawnedObject(OneNetVar destroyedObject) - { - OneNetVar.Stop(); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs.meta deleted file mode 100644 index 492f738794..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 91f4160a1de3f40c1bcdb9c18daf9bf2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs b/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs deleted file mode 100644 index 0e62bf0edb..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs +++ /dev/null @@ -1,506 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using Unity.Netcode; -using NUnit.Framework; -using UnityEngine; -using Unity.Netcode.MultiprocessRuntimeTests; -using Unity.Netcode.Transports.UTP; - -/// -/// TestCoordinator -/// Used for coordinating multiprocess end to end tests. Used to call RPCs on other nodes and gather results -/// This is needed to coordinate server and client execution steps. The current remote player test runner hardcodes test -/// to run in a bootstrap scene before launching the player and doesn't call each tests individually. There's not opportunity -/// to coordinate test execution between client and server with that model. -/// The only per tests communication already existing is to get the results per test as they are running -/// With this test coordinator, it's not possible to start a main test node with the test runner and have that server start other worker nodes -/// on which to execute client tests. We use netcode as both a test framework and as the target of our performance tests. -/// -[RequireComponent(typeof(NetworkObject))] -public class TestCoordinator : NetworkBehaviour -{ - public const int PerTestTimeoutSec = 5 * 60; // seconds - - public const float MaxWaitTimeoutSec = 60; - private const char k_MethodFullNameSplitChar = '@'; - - private bool m_ShouldShutdown; - private float m_TimeSinceLastConnected; - private float m_TimeSinceLastKeepAlive; - - public static TestCoordinator Instance; - - private Dictionary> m_TestResultsLocal = new Dictionary>(); // this isn't super efficient, but since it's used for signaling around the tests, shouldn't be too bad - private Dictionary m_ClientIsFinished = new Dictionary(); - - public static List AllClientIdsWithResults => Instance.m_TestResultsLocal.Keys.ToList(); - public static List AllClientIdsExceptMine => NetworkManager.Singleton.ConnectedClients.Keys.ToList().FindAll(client => client != NetworkManager.Singleton.LocalClientId); - - // Multimachine support - private static int s_ProcessId; - public static string Rawgithash; - - private ConfigurationType m_ConfigurationType; - public ConfigurationType ConfigurationType - { - get { return m_ConfigurationType; } - private set - { - if (m_ConfigurationType != value) - { - m_ConfigurationType = value; - } - } - } - private string m_ConnectAddress = "127.0.0.1"; - public static string Port = "7777"; - private bool m_IsClient; - - private void SetConfigurationTypeAndConnect(ConfigurationType type) - { - ConfigurationType = type; - SetAddressAndPort(); - bool startClientResult = NetworkManager.Singleton.StartClient(); - MultiprocessLogger.Log($"Starting client"); - } - - public void Awake() - { - enabled = false; - NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback; - - MultiprocessLogger.Log("Awake - Initialize All Steps"); - ExecuteStepInContext.InitializeAllSteps(); - - s_ProcessId = Process.GetCurrentProcess().Id; - ReadGitHashFile(); - - // Configuration via command line (supported for many but not all platforms) - bool isClient = Environment.GetCommandLineArgs().Any(value => value == MultiprocessOrchestration.IsWorkerArg); - if (isClient) - { - MultiprocessLogger.Log("Setting up via command line - client"); - m_IsClient = isClient; - var cli = new CommandLineProcessor(Environment.GetCommandLineArgs()); - if (Environment.GetCommandLineArgs().Any(value => value == "-ip")) - { - m_ConnectAddress = cli.TransportAddress; - } - if (Environment.GetCommandLineArgs().Any(value => value == "-p")) - { - Port = cli.TransportPort; - } - SetConfigurationTypeAndConnect(ConfigurationType.CommandLine); - } - - if (ConfigurationType == ConfigurationType.Unknown) - { - bool isHost = Environment.GetCommandLineArgs().Any(value => value == "host"); - if (isHost) - { - MultiprocessLogger.Log("Setting up via command line - host"); - var cli = new CommandLineProcessor(Environment.GetCommandLineArgs()); - ConfigurationType = ConfigurationType.CommandLine; - } - } - - - // Configuration via configuration file - all platform support but set at build time - if (ConfigurationType == ConfigurationType.Unknown) - { - //TODO: For next PR - } - - // configuration via WebApi - works on all platforms and is set at run time - if (ConfigurationType == ConfigurationType.Unknown) - { - MultiprocessLogger.Log($"Awake {s_ProcessId} - Calling ConfigureViewWebApi"); - ConfigureViaWebApi(); - MultiprocessLogger.Log($"Awake {s_ProcessId} - Calling ConfigureViewWebApi completed"); - } - - - // if we've tried all the configuration types and none of them are correct then we should log it and just go with the default values - if (ConfigurationType == ConfigurationType.Unknown) - { - MultiprocessLogger.Log("Unable to determine configuration for NetworkManager via commandline, webapi or config file"); - } - - if (Instance != null) - { - MultiprocessLogger.LogError("Multiple test coordinator, destroying this instance"); - Destroy(gameObject); - return; - } - - Instance = this; - } - - private async void ConfigureViaWebApi() - { - MultiprocessLogger.Log($"ConfigureViaWebApi - start"); - var jobQueue = await ConfigurationTools.GetRemoteConfig(); - foreach (var job in jobQueue.JobQueueItems) - { - if (Rawgithash.Equals(job.GitHash)) - { - ConfigurationTools.ClaimJobQueueItem(job); - m_ConnectAddress = job.HostIp; - m_IsClient = true; - MultiprocessLogHandler.JobId = job.JobId; - SetConfigurationTypeAndConnect(ConfigurationType.Remote); - break; - } - else - { - MultiprocessLogger.Log($"No match between {Rawgithash} and {job.GitHash}"); - } - } - MultiprocessLogger.Log($"ConfigureViaWebApi - end {ConfigurationType}"); - } - - private void ReadGitHashFile() - { - Rawgithash = "uninitialized"; - try - { - var githash_resource = Resources.Load("Text/githash"); - if (githash_resource != null) - { - Rawgithash = githash_resource.ToString(); - if (!string.IsNullOrEmpty(Rawgithash)) - { - Rawgithash = Rawgithash.Trim(); - MultiprocessLogger.Log($"Rawgithash is {Rawgithash}"); - } - } - } - catch (Exception e) - { - MultiprocessLogger.Log($"Exception getting githash resource file: {e.Message}"); - } - } - - private void SetAddressAndPort() - { - MultiprocessLogger.Log($"SetAddressAndPort - {Port} {m_ConnectAddress} {m_IsClient} "); - var ushortport = ushort.Parse(Port); - var transport = NetworkManager.Singleton.NetworkConfig.NetworkTransport; - MultiprocessLogger.Log($"transport is {transport}"); - switch (transport) - { - case UnityTransport unityTransport: - MultiprocessLogger.Log($"Setting unityTransport.ConnectionData.Port {ushortport}, isClient: {m_IsClient}, Address {m_ConnectAddress}"); - unityTransport.ConnectionData.Port = ushortport; - unityTransport.ConnectionData.Address = m_ConnectAddress; - break; - default: - MultiprocessLogger.LogError($"The transport {transport} has no case"); - break; - } - } - - public void Start() - { - MultiprocessLogger.Log($"TestCoordinator - Start"); - } - - public void Update() - { - if (Time.time - m_TimeSinceLastKeepAlive > PerTestTimeoutSec) - { - QuitApplication(); - Assert.Fail("Stayed idle too long"); - } - - if ((IsServer && NetworkManager.Singleton.IsListening) || (IsClient && NetworkManager.Singleton.IsConnectedClient)) - { - m_TimeSinceLastConnected = Time.time; - } - else if (Time.time - m_TimeSinceLastConnected > MaxWaitTimeoutSec || m_ShouldShutdown) - { - // Make sure we don't have zombie processes - MultiprocessLogger.Log($"quitting application, shouldShutdown set to {m_ShouldShutdown}, is listening {NetworkManager.Singleton.IsListening}, is connected client {NetworkManager.Singleton.IsConnectedClient}"); - if (!m_ShouldShutdown) - { - QuitApplication(); - Assert.Fail($"something wrong happened, was not connected for {Time.time - m_TimeSinceLastConnected} seconds"); - } - } - } - - private static void QuitApplication() - { -#if UNITY_EDITOR - UnityEditor.EditorApplication.isPlaying = false; -#else - Application.Quit(); -#endif - } - - public void TestRunTeardown() - { - m_TestResultsLocal.Clear(); - } - - public void OnEnable() - { - MultiprocessLogger.Log("OnEnable - Setting OnClientDisconnectCallback"); - NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback; - } - - public void OnDisable() - { - if (IsSpawned && NetworkObject != null && NetworkObject.NetworkManager != null) - { - MultiprocessLogger.Log("OnDisable - Removing OnClientDisconnectCallback"); - NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback; - } - - base.OnDestroy(); - } - - // Once we are connected, we can run the update method - public void OnClientConnectedCallback(ulong clientId) - { - if (enabled == false) - { - MultiprocessLogger.Log($"OnClientConnectedCallback enabling behavior clientId: {clientId} {NetworkManager.Singleton.IsHost}/{NetworkManager.Singleton.IsClient} IsRegistering:{ExecuteStepInContext.IsRegistering}"); - enabled = true; - } - } - - private static void OnClientDisconnectCallback(ulong clientId) - { - if (clientId == NetworkManager.ServerClientId || clientId == NetworkManager.Singleton.LocalClientId) - { - // if disconnect callback is for me or for server, quit, we're done here - MultiprocessLogger.Log($"received disconnect from {clientId}, quitting"); - QuitApplication(); - } - } - - private static string GetMethodInfo(Action method) - { - return $"{method.Method.DeclaringType.FullName}{k_MethodFullNameSplitChar}{method.Method.Name}"; - } - - private static string GetMethodInfo(Action method) - { - return $"{method.Method.DeclaringType.FullName}{k_MethodFullNameSplitChar}{method.Method.Name}"; - } - - public static IEnumerable<(ulong clientId, float result)> ConsumeCurrentResult() - { - foreach (var kv in Instance.m_TestResultsLocal) - { - while (kv.Value.Count > 0) - { - var toReturn = (kv.Key, kv.Value[0]); - kv.Value.RemoveAt(0); - yield return toReturn; - } - } - } - - public static IEnumerable ConsumeCurrentResult(ulong clientId) - { - var allResults = Instance.m_TestResultsLocal[clientId]; - while (allResults.Count > 0) - { - var toReturn = allResults[0]; - allResults.RemoveAt(0); - yield return toReturn; - } - } - - public static float PeekLatestResult(ulong clientId) - { - if (Instance.m_TestResultsLocal.ContainsKey(clientId) && Instance.m_TestResultsLocal[clientId].Count > 0) - { - return Instance.m_TestResultsLocal[clientId].Last(); - } - - return float.NaN; - } - - /// - /// Returns appropriate lambda according to parameters - /// Includes time check to make sure this times out - /// - /// - /// - /// - public static Func ResultIsSet(bool useTimeoutException = true) - { - var startWaitTime = Time.time; - return () => - { - if (Time.time - startWaitTime > MaxWaitTimeoutSec) - { - if (useTimeoutException) - { - throw new Exception($"timeout while waiting for results, didn't get results for {Time.time - startWaitTime} seconds"); - } - - return true; - } - - foreach (var clientIdAndTestResultList in Instance.m_TestResultsLocal) - { - if (clientIdAndTestResultList.Value.Count > 0) - { - return true; - } - } - - return false; - }; - } - - public static Func ConsumeClientIsFinished(ulong clientId, bool useTimeoutException = true) - { - var startWaitTime = Time.time; - return () => - { - if (Time.time - startWaitTime > MaxWaitTimeoutSec) - { - if (useTimeoutException) - { - throw new Exception($"timeout while waiting for client finished, didn't get results for {Time.time - startWaitTime} seconds"); - } - else - { - return true; - } - } - - if (Instance.m_ClientIsFinished.ContainsKey(clientId) && Instance.m_ClientIsFinished[clientId]) - { - Instance.m_ClientIsFinished[clientId] = false; // consume - return true; - } - - return false; - }; - } - - [Rpc(SendTo.Server)] - public void ClientFinishedServerRpc(ServerRpcParams p = default) - { - // signal from clients to the server to say the client is done with it's task - m_ClientIsFinished[p.Receive.SenderClientId] = true; - } - - public void InvokeFromMethodActionRpc(Action methodInfo, params byte[] args) - { - var methodInfoString = GetMethodInfo(methodInfo); - InvokeFromMethodNameClientRpc(methodInfoString, args, new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = AllClientIdsExceptMine.ToArray() } }); - } - - public void InvokeFromMethodActionRpc(Action methodInfo) - { - var methodInfoString = GetMethodInfo(methodInfo); - InvokeFromMethodNameClientRpc(methodInfoString, null, new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = AllClientIdsExceptMine.ToArray() } }); - } - - [ClientRpc] - public void TriggerActionIdClientRpc(string actionId, byte[] args, bool ignoreException, ClientRpcParams clientRpcParams = default) - { - MultiprocessLogger.Log($"received RPC from server, client side triggering action ID {actionId}"); - WriteLogServerRpc($"received RPC from server, client side triggering action ID {actionId} {ExecuteStepInContext.AllActions.Count}"); - try - { - ExecuteStepInContext.AllActions[actionId].Invoke(args); - } - catch (Exception e) - { - WriteErrorServerRpc(e.ToString()); - - if (!ignoreException) - { - throw; - } - else - { - Instance.ClientFinishedServerRpc(); - } - } - } - - [ClientRpc] - public void InvokeFromMethodNameClientRpc(string methodInfoString, byte[] args, ClientRpcParams clientRpcParams = default) - { - try - { - var split = methodInfoString.Split(k_MethodFullNameSplitChar); - var (classToExecute, staticMethodToExecute) = (split[0], split[1]); - - var foundType = Type.GetType(classToExecute) ?? throw new Exception($"couldn't find {classToExecute}"); - var foundMethod = foundType.GetMethod(staticMethodToExecute, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) ?? throw new MissingMethodException($"couldn't find method {staticMethodToExecute}"); - foundMethod.Invoke(null, args != null ? new object[] { args } : null); - } - catch (Exception e) - { - WriteErrorServerRpc(e.ToString()); - throw; - } - } - - [ClientRpc] - public void CloseRemoteClientRpc() - { - try - { - NetworkManager.Singleton.Shutdown(); - m_ShouldShutdown = true; // wait until isConnectedClient is false to run Application Quit in next update - MultiprocessLogger.Log("Quitting player cleanly"); - Application.Quit(); - } - catch (Exception e) - { - WriteErrorServerRpc(e.ToString()); - throw; - } - } - - [ClientRpc] - public void KeepAliveClientRpc() - { - m_TimeSinceLastKeepAlive = Time.time; - } - - [Rpc(SendTo.Server)] - public void WriteTestResultsServerRpc(float result, ServerRpcParams receiveParams = default) - { - var senderId = receiveParams.Receive.SenderClientId; - MultiprocessLogger.Log($"Server received result [{result}] from sender [{senderId}]"); - if (!m_TestResultsLocal.ContainsKey(senderId)) - { - m_TestResultsLocal[senderId] = new List(); - } - - m_TestResultsLocal[senderId].Add(result); - } - - /// - /// Use this to communicate client-side errors for server-side logging using the MultiprocessLogger. - /// - /// - /// Use to log server-side without MultiprocessLogger formatting. - /// - [Rpc(SendTo.Server)] - public void WriteErrorServerRpc(string errorMessage, ServerRpcParams receiveParams = default) - { - MultiprocessLogger.LogError($"[Netcode-Server Sender={receiveParams.Receive.SenderClientId}] {errorMessage}"); - } - - [Rpc(SendTo.Server)] - public void WriteLogServerRpc(string logMessage, ServerRpcParams receiveParams = default) - { - MultiprocessLogger.Log($"[Netcode-Server Sender={receiveParams.Receive.SenderClientId}] {logMessage}"); - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs.meta b/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs.meta deleted file mode 100644 index f8c2a3c472..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ef1240e0784f84eadb77fe822e2e03c7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs b/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs deleted file mode 100644 index 3152627cbd..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections; -using System.Linq; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - [TestFixture(1)] - [TestFixture(2)] - public class TestCoordinatorTests : BaseMultiprocessTests - { - private int m_WorkerCount; - protected override int WorkerCount => m_WorkerCount; - - protected override bool IsPerformanceTest => false; - - public TestCoordinatorTests(int workerCount) - { - m_WorkerCount = workerCount; - } - - private static float s_ValueToValidateAgainst; - private static void ValidateSimpleCoordinatorTestValue(float resultReceived) - { - Assert.AreEqual(s_ValueToValidateAgainst, resultReceived); - } - - private static void ExecuteSimpleCoordinatorTest() - { - s_ValueToValidateAgainst = float.PositiveInfinity; - TestCoordinator.Instance.WriteTestResultsServerRpc(s_ValueToValidateAgainst); - } - - private static void ExecuteWithArgs(byte[] args) - { - s_ValueToValidateAgainst = args[0]; - TestCoordinator.Instance.WriteTestResultsServerRpc(s_ValueToValidateAgainst); - } - - [UnityTest] - public IEnumerator CheckTestCoordinator() - { - // Sanity check for TestCoordinator - // Call the method - TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteSimpleCoordinatorTest); - - var nbResults = 0; - for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients - { - yield return new WaitUntil(TestCoordinator.ResultIsSet()); - - var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); - Assert.Greater(result, 0f); - nbResults++; - } - Assert.That(nbResults, Is.EqualTo(WorkerCount)); - } - - [UnityTest] - public IEnumerator CheckTestCoordinatorWithArgs() - { - TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteWithArgs, 99); - var nbResults = 0; - - for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients - { - yield return new WaitUntil(TestCoordinator.ResultIsSet()); - - var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); - Assert.That(result, Is.EqualTo(99)); - nbResults++; - } - Assert.That(nbResults, Is.EqualTo(WorkerCount)); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs.meta deleted file mode 100644 index 104be914d0..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f5e62651568514685a0b50d623fa8a96 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs b/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs deleted file mode 100644 index 232c8ca05f..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs +++ /dev/null @@ -1,88 +0,0 @@ -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class ThreeDText : MonoBehaviour - { - public bool IsTestCoordinatorActiveAndEnabled = false; - public string CommandLineArguments = ""; - private long m_UpdateCounter; - private bool m_HasFired; - private string m_TransportString; - - public void Awake() - { - if (MultiprocessOrchestration.IsPerformanceTest) - { - gameObject.SetActive(false); - } - } - - // Start is called before the first frame update - public void Start() - { - Debug.Log("ThreeDText - Start"); - m_HasFired = false; - m_UpdateCounter = 0; - m_TransportString = "null"; - var jsonTextFile = Resources.Load("Text/multiprocess_tests"); - Debug.Log(jsonTextFile); - - var t = GetComponent(); - t.text = "On Start"; - CommandLineArguments = System.Environment.CommandLine; - - string[] args = System.Environment.GetCommandLineArgs(); - foreach (var arg in args) - { - if (arg.Length > 15) - { - CommandLineArguments += " " + arg.Substring(0, 14); - } - else - { - CommandLineArguments += "\n" + arg; - } - } - } - - // Update is called once per frame - public void Update() - { - m_UpdateCounter++; - var testCoordinator = TestCoordinator.Instance; - if (testCoordinator == null) - { - return; - } - - var transport = NetworkManager.Singleton?.NetworkConfig.NetworkTransport; - var transportString = ""; - if (transport == null) - { - transportString = "null"; - } - else - { - transportString = transport.ToString(); - } - - var t = GetComponent(); - - if (IsTestCoordinatorActiveAndEnabled != testCoordinator.isActiveAndEnabled || - !m_HasFired || - m_UpdateCounter % 25 == 0 || - !m_TransportString.Equals(transportString)) - { - m_HasFired = true; - m_TransportString = transportString; - IsTestCoordinatorActiveAndEnabled = testCoordinator.isActiveAndEnabled; - t.text = $"On Update -\ntestCoordinator.isActiveAndEnabled:{testCoordinator.isActiveAndEnabled} {testCoordinator.ConfigurationType}\n" + - $"Transport: {transportString}\n" + - $"{CommandLineArguments}\n" + - $"IsHost: {NetworkManager.Singleton.IsHost} IsClient: {NetworkManager.Singleton.IsClient} {NetworkManager.Singleton.IsConnectedClient}\n" + - $"{m_UpdateCounter}\n"; - } - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs.meta b/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs.meta deleted file mode 100644 index 9d9011054b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 04cf3cc1396054b009a1ed283aa50021 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg b/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg deleted file mode 100644 index 8663fcf757..0000000000 Binary files a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg and /dev/null differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg b/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg deleted file mode 100644 index 72f13b3f05..0000000000 Binary files a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg and /dev/null differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg.meta b/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg.meta deleted file mode 100644 index 6d73b54a8f..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg.meta +++ /dev/null @@ -1,96 +0,0 @@ -fileFormatVersion: 2 -guid: d2e6ce5793e6a4e74843dca06fd36778 -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 11 - mipmaps: - mipMapMode: 0 - enableMipMap: 1 - sRGBTexture: 1 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 0 - wrapV: 0 - wrapW: 0 - nPOTScale: 1 - lightmap: 0 - compressionQuality: 50 - spriteMode: 0 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 1 - alphaIsTransparency: 0 - spriteTessellationDetail: -1 - textureType: 0 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - spritePackingTag: - pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg b/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg deleted file mode 100644 index f346dd85b7..0000000000 Binary files a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg and /dev/null differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg.meta b/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg.meta deleted file mode 100644 index 84c6f4f98b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg.meta +++ /dev/null @@ -1,96 +0,0 @@ -fileFormatVersion: 2 -guid: 7b6f909ad23994f9c9cb51710d1e1e07 -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 11 - mipmaps: - mipMapMode: 0 - enableMipMap: 1 - sRGBTexture: 1 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 0 - wrapV: 0 - wrapW: 0 - nPOTScale: 1 - lightmap: 0 - compressionQuality: 50 - spriteMode: 0 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 1 - alphaIsTransparency: 0 - spriteTessellationDetail: -1 - textureType: 0 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - spritePackingTag: - pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/readme.md b/testproject/Legacy/MultiprocessRuntime/readme.md deleted file mode 100644 index 6efdcba540..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/readme.md +++ /dev/null @@ -1,279 +0,0 @@ -# Multiprocess testing - -## Why -Multiprocess testing can be used for different use cases like -- integration tests (Netcode + actual transport or multi-scene testing for example) -- performance testing. -- Anything requiring a more realistic environment for testing that involves having a full client and server, communicating on a real network interface using real transports in separate Unity processes. - -The tests you write and test locally will be deployed dynamically to bokken instances. The tests shouldn't have to worry about what hardware it runs on, this should be abstracted away by "workers" and "coordinator". - -## How to write a multiprocess test -There's a few steps to write a multiprocess test - -1. Your test class needs to inherit from `BaseMultiprocessTests` -2. Each test method needs the `MultiprocessContextBasedTest` attribute -3. Each test method needs to run `InitializeContextSteps();` -4. Each context based step can use -```cs -yield return new ExecuteStepInContext(StepExecutionContext.Clients, stepToExecute: nbObjectsBytes => { - // Something here -}); -``` -A test method would look like -```cs - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator MyTest() - { - InitializeContextSteps(); // the only call that should be made outside of context based tests - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Debug.Log("server stuff"); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Debug.Log("client stuff"); - Assert.That(1, Is.EqualTo(1)); - throw new Exception("asdf"); // this client side exception will be communicated to the coordinator, making the test fail - }); - } -``` -Your test code shouldn't execute outside of these steps (as that test method can be executed multiple times, once for step registration and once for the actual test run for example) - -Another way to write a multiprocess test without context based steps is to use TestCoordinator directly. -```cs - private static void ExecuteSimpleCoordinatorTest() - { - TestCoordinator.Instance.WriteTestResultsServerRpc(float.PositiveInfinity); - } - - [UnityTest] - public IEnumerator CheckTestCoordinator() - { - // Call the client side method - TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteSimpleCoordinatorTest); - - var resultCount = 0; - for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients - { - yield return new WaitUntil(TestCoordinator.ResultIsSet()); - - var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); - Assert.Greater(result, 0f); - resultCount++; - } - - Assert.That(resultCount, Is.EqualTo(WorkerCount)); - } -``` - -Here's a complete set of examples using the API - -```cs -using System; -using System.Collections; -using System.Collections.Generic; -using NUnit.Framework; -using Unity.PerformanceTesting; -using UnityEngine; -using UnityEngine.Profiling; -using UnityEngine.TestTools; -using static ExecuteStepInContext; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class DemoProcessTest : BaseMultiprocessTests - { - protected override int WorkerCount { get; } = 2; // spawns 2 clients connecting to the test runner - protected override bool m_IsPerformanceTest { get; } = false; // specifies whether this should execute from editor or not - - [UnityTest, MultiprocessContextBasedTest] // attribute necessary for context based step execution - public IEnumerator MyTest() - { - InitializeContextSteps(); // necessary to initialize context based steps - - // These steps execute sequentially. - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Debug.Log("server stuff"); - }); - // for example, the test runner will yield on the same step until clients all report they are done with this step. Once all clients report they are done, the test can continue to the same step. - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Debug.Log("client stuff"); - Assert.That(1, Is.EqualTo(1)); - throw new Exception("asdf"); // this client side exception will be communicated to the coordinator, making the test fail - }); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - // To write results to the test runner, call this method: - TestCoordinator.Instance.WriteTestResultsServerRpc(123); - TestCoordinator.Instance.WriteTestResultsServerRpc(123); - TestCoordinator.Instance.WriteTestResultsServerRpc(123); // could be replaced by json string instead for ease of use? - }); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - // consumes first result sent above from any client - TestCoordinator.ConsumeCurrentResult(); - // consumes all results from all clients - foreach (var (clientID, result) in TestCoordinator.ConsumeCurrentResult()) - { - Assert.That(result, Is.EqualTo(123)); - } - // consumes results for individual clients - foreach (var clientID in TestCoordinator.AllClientIdsExceptMine) - { - TestCoordinator.ConsumeCurrentResult(clientID); - } - }); - - int someValue = 456; // one caveat to executeStepInContext is contrary to instinct, this is not shared between server and client execution. - // to send that value to clients, "paramToPass" needs to be used - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - var valueComingFromServer = BitConverter.ToInt32(bytes, 0); - }, paramToPass: BitConverter.GetBytes(456)); // could be replaced by JSON string instead for ease of use? - // useful for taking in [Values] method parameters as these are only known by the server - - // when you have client steps that take more than one frame, you can subscribe to the OnUpdate callback on CallbackComponent - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - void Update(float _) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= Update; - TestCoordinator.Instance.ClientFinishedServerRpc(); // since finishOnInvoke is false, we need to do this manually - } - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += Update; - }, waitMultipleUpdates: true); // this keeps waiting "are you done? are you done? are you done?" and relies on the clients calling the "ClientFinishedServerRpc" - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - int cpt = 0; - void Update(float _) - { - TestCoordinator.Instance.WriteTestResultsServerRpc(Time.time); - } - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += Update; - }, additionalIsFinishedWaiter: () => // this keeps waiting "are you done? are you done? are you done?" until this lambda returns true - { - foreach (var (clientId, latest) in TestCoordinator.ConsumeCurrentResult()) - { - return latest >= 10; - } - return false; - }); - } - - [UnityTest, Performance] // already existing performance framework https://docs.unity3d.com/Packages/com.unity.test-framework.performance@2.8/manual/index.html - public IEnumerator PerfTest() - { - var totalAllocSampleGroup = new SampleGroup("GC Alloc", SampleUnit.Kilobyte); - var allocStat = Profiler.GetTotalAllocatedMemoryLong(); - Measure.Custom(totalAllocSampleGroup, allocStat / 1024); // this will record in Unity's shared Performance DB. - // Dashboards will be able to display these stats overtime - yield return null; - } - } -} - - -``` - - -## How to run a test -**Local**: Test players need to be built first to test locally. - -**Automated**: Integration with CI should do this automatically. - -![](readme-ressources/Building-Player.jpg) - -Then run the tests from Unity's test runner. - -Note that performance tests should be run from external processes (not from editor). This way the server code will run in a build, just as much as client code, for more realistic test results. - -![](readme-ressources/Multiprocess.jpg) - -## How it's done -### Multiple processes orchestration - -Test code and host code execute in the same process: When writing play mode tests, Unity will start a unity environment for that test, including game loop, scene, object hierarchy, all that fun stuff. This means the test itself has access to everything other unity scripts would have access to. At test startup, the test will ask unity to switch scene to MultiprocessTestScene containing a GameObject already placed in that scene called TestCoordinator . The test will callStartHost that will listen for connections, still all in the same process. -Once that's done, that same test will then spawn new client processes. These clients will also start with that MultiprocessTestScene loaded at startup, also containing a TestCoordinator . These client side TestCoordinators will detect they are clients (using command line arg) and instead of calling StartHost will call StartClient . This is where new code to specify the IP to connect to would stand (the lines I sent you). -A good way I could have clarified that code is to separate TestCoordinator into TestCoordinatorClient and TestCoordinatorServer thinking of it. Right now TestCoordinator code does both. -Once the connection is established (tests yield wait for connection in Setup code), then the tests can start sending RPCs to each other. -The test (that's server side) will call multiple TriggerActionIdClientRpc . This will trigger these actions on all clients. The clients execute their test code, then answer back with ClientFinishedServerRpc. -Once all the tests are done exchanging commands, a final RPC CloseRemoteClientRpc is called to tell the clients they are done. -If that RPC fails to send for some reason, clients also have a keep alive that tells them to self destroy when it expires. -If you look at the “how it's done” section in the multiprocess readme.md testproject/Assets/Tests/Runtime/MultiprocessRuntime/readme.md there's a few drawings to explain that flow. - -So: -Editor -- Run tests -- Run host code -- Launches builds -Separate build -- Runs client RPCs that execute test code, not the tests themselves - - -Now just to bake your noodles a bit more, Unity will take an additional step before launching these steps. The above is true if you launch these tests from the editor. If you launch these tests in a separate player, Unity in the background will create a separate process for that player. That player will then connect using a plain tcp connection to the editor to report back on its test results. This is important to know if you want to have the test/host part launch on different platforms like mobile. - -So the above would become -Editor -- Launches test player on platform -Test player -- Run tests -- Run host code -- Launches builds -Separate build -- Runs client RPCs … - -With the bokken integration, we'll need to be careful about ressource contention at Unity, these tests could be heavy on ressources. -Tests when launched locally will simply create new OS processes for each worker players. - - - -![](readme-ressources/OrchestrationOverview.jpg) -*Note that this diagram is still WIP for the CI part* -### Bokken orchestration -Bokken Orchestration can be performed with the support of the following tool: -[Multiplayer Multiprocess Test Tools](https://github.cds.internal.unity3d.com/unity/multiplayer-multiprocess-test-tools) -[Documentation](https://backstage.corp.unity3d.com/catalog/default/component/multiplayer-multiprocess-test-tools/docs/) - -### CI -todo -#### Performance report dashboards -todo -### Client-server test coordination -A Test Coordinator is in charge of managing communication between the nodes, executing remote test code. The test coordinator is also in charge of process cleanup, if for example the server crashes, so we don't have zombie clients laying around. -The test coordinator in client mode will automatically try to connect to a server on Start(). -### Context based step execution -Test methods are executed twice. Once in "registration" mode, to have all the steps register themselves using a unique ID. This ID is deterministic between client and server processes, so that when a server calls a step during actual test execution, the clients have the same ID associated with the same lambda. -During test execution, the main node's step will call an RPC on clients to trigger their pre-registered lambda. The main node's step will then yield until it receives a "client done" RPC from all clients. The main node's test will then be able to continue execution to the next step. - -## The MultiprocessTestPlayer -The MultiprocessTestPlayer, which is built by the sub-menu items under "Netcode" -> "Multiprocess Test", supports many workflows and configurations. - -### Command Line -Currently the MultiprocessTestPlayer can be configured via the command line on all platforms that support parsing of command line arguments in the C# layer. -For example, this means that command line configuration is not available on Android. - -#### Setting the transport address -Default Values on Client: 127.0.0.1, 3076 - -In order to set the transport address for either the server/host or the client, the options of "-ip" and "-p" can be used. For example: - - -ip 127.0.0.1 -p 3076 - -These options can be passed when starting the client, for example, in order to let it know where the host is to connect to. - -#### Setting the transport -Default Values: UNET - -The default transport is UNET but this can be switched to UTP by using - - -transport utp - - -# Future considerations -- Integrate with local MultiInstance tests? -- Have ExecuteStepInContext a game facing feature for sequencing client-server actions? diff --git a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef b/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef deleted file mode 100644 index 13baf50a69..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "TestProject.MultiprocessTests", - "rootNamespace": "", - "references": [ - "Unity.Netcode.Runtime", - "TestProject", - "Unity.Netcode.Editor", - "Unity.Netcode.Components", - "ScriptsForAutomatedTesting", - "Unity.PerformanceTesting", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner" - ], - "includePlatforms": [ - "Editor", - "macOSStandalone", - "LinuxStandalone64", - "WindowsStandalone32", - "WindowsStandalone64" - ], - "excludePlatforms": [], - "allowUnsafeCode": true, - "overrideReferences": false, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "autoReferenced": true, - "defineConstraints": [ - "UNITY_INCLUDE_TESTS" - ], - "noEngineReferences": false, - "versionDefines": [ - ] -} diff --git a/testproject/Packages/manifest.json b/testproject/Packages/manifest.json index f7832aaad0..b6bccda91a 100644 --- a/testproject/Packages/manifest.json +++ b/testproject/Packages/manifest.json @@ -1,23 +1,23 @@ { "disableProjectUpdate": false, "dependencies": { - "com.unity.addressables": "2.7.4", - "com.unity.ai.navigation": "2.0.9", - "com.unity.collab-proxy": "2.10.1", - "com.unity.ide.rider": "3.0.38", - "com.unity.ide.visualstudio": "2.0.25", - "com.unity.mathematics": "1.3.3", - "com.unity.multiplayer.tools": "2.2.6", + "com.unity.addressables": "2.11.1", + "com.unity.ai.navigation": "2.0.14", + "com.unity.collab-proxy": "2.12.4", + "com.unity.ide.rider": "3.0.40", + "com.unity.ide.visualstudio": "2.0.26", + "com.unity.mathematics": "1.4.0", + "com.unity.multiplayer.tools": "2.2.9", "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", "com.unity.package-validation-suite": "0.49.0-preview", - "com.unity.services.authentication": "3.5.2", - "com.unity.services.multiplayer": "1.2.0", - "com.unity.test-framework": "1.6.0", - "com.unity.test-framework.performance": "3.2.0", - "com.unity.timeline": "1.8.9", - "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.11", - "com.unity.ugui": "2.0.0", + "com.unity.services.authentication": "3.7.3", + "com.unity.services.multiplayer": "2.2.4", + "com.unity.test-framework": "1.8.0", + "com.unity.test-framework.performance": "6.6.0", + "com.unity.timeline": "6.6.0", + "com.unity.ugui": "2.6.0", "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -31,10 +31,13 @@ "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.physicscore2d": "1.0.0", "com.unity.modules.screencapture": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tetgen": "1.0.0", "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.timelinefoundation": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.modules.umbra": "1.0.0", @@ -44,9 +47,9 @@ "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", - "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" }, diff --git a/testproject/Packages/packages-lock.json b/testproject/Packages/packages-lock.json deleted file mode 100644 index 675de94bf2..0000000000 --- a/testproject/Packages/packages-lock.json +++ /dev/null @@ -1,570 +0,0 @@ -{ - "dependencies": { - "com.unity.addressables": { - "version": "2.7.4", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.profiling.core": "1.0.2", - "com.unity.test-framework": "1.4.5", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.scriptablebuildpipeline": "2.4.3", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ai.navigation": { - "version": "2.0.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.ai": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.burst": { - "version": "1.8.25", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.collab-proxy": { - "version": "2.10.1", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.collections": { - "version": "2.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.23", - "com.unity.mathematics": "1.3.2", - "com.unity.test-framework": "1.4.6", - "com.unity.nuget.mono-cecil": "1.11.5", - "com.unity.test-framework.performance": "3.0.3" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ext.nunit": { - "version": "2.0.5", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.ide.rider": { - "version": "3.0.38", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ext.nunit": "1.0.6" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ide.visualstudio": { - "version": "2.0.25", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.31" - }, - "url": "https://packages.unity.com" - }, - "com.unity.mathematics": { - "version": "1.3.3", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.multiplayer.tools": { - "version": "2.2.6", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.18", - "com.unity.collections": "2.5.1", - "com.unity.mathematics": "1.3.2", - "com.unity.profiling.core": "1.0.2", - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.modules.uielements": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.netcode.gameobjects": { - "version": "file:../../com.unity.netcode.gameobjects", - "depth": 0, - "source": "local", - "dependencies": { - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.6.0" - } - }, - "com.unity.nuget.mono-cecil": { - "version": "1.11.5", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.nuget.newtonsoft-json": { - "version": "3.2.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.package-validation-suite": { - "version": "0.49.0-preview", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.nuget.mono-cecil": "0.1.6-preview.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.profiling.core": { - "version": "1.0.2", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.scriptablebuildpipeline": { - "version": "2.4.3", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.4.5", - "com.unity.modules.assetbundle": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.authentication": { - "version": "3.5.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.15.1", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.core": { - "version": "1.15.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.modules.androidjni": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment": { - "version": "1.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.15.1", - "com.unity.services.deployment.api": "1.1.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment.api": { - "version": "1.1.2", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.services.multiplayer": { - "version": "1.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.transport": "2.5.0", - "com.unity.collections": "2.2.1", - "com.unity.services.qos": "1.3.0", - "com.unity.services.core": "1.15.1", - "com.unity.services.wire": "1.4.0", - "com.unity.services.deployment": "1.6.2", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "3.5.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.qos": { - "version": "1.3.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.collections": "1.2.4", - "com.unity.services.core": "1.12.4", - "com.unity.nuget.newtonsoft-json": "3.0.2", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "2.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.wire": { - "version": "1.4.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.12.5", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.services.authentication": "2.7.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot": { - "version": "2.0.10", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot.linux-x86_64": { - "version": "2.0.9", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10" - }, - "url": "https://packages.unity.com" - }, - "com.unity.test-framework": { - "version": "1.6.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ext.nunit": "2.0.3", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.test-framework.performance": { - "version": "3.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.33", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.timeline": { - "version": "1.8.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.toolchain.win-x86_64-linux-x86_64": { - "version": "2.0.11", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10", - "com.unity.sysroot.linux-x86_64": "2.0.9" - }, - "url": "https://packages.unity.com" - }, - "com.unity.transport": { - "version": "2.6.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.24", - "com.unity.collections": "2.2.1", - "com.unity.mathematics": "1.3.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ugui": { - "version": "2.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0" - } - }, - "com.unity.modules.accessibility": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.ai": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.androidjni": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.animation": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.assetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.audio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.cloth": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.director": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.animation": "1.0.0" - } - }, - "com.unity.modules.hierarchycore": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imageconversion": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imgui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.jsonserialize": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.particlesystem": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics2d": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.screencapture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.subsystems": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.terrain": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.terrainphysics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.terrain": "1.0.0" - } - }, - "com.unity.modules.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics2d": "1.0.0" - } - }, - "com.unity.modules.ui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.uielements": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.hierarchycore": "1.0.0", - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.umbra": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unityanalytics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.unitywebrequest": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unitywebrequestassetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestaudio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.audio": "1.0.0" - } - }, - "com.unity.modules.unitywebrequesttexture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestwww": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.vehicles": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.video": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.vr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.xr": "1.0.0" - } - }, - "com.unity.modules.wind": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.xr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.subsystems": "1.0.0" - } - } - } -} diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index b2632cd9f5..63b5a1481e 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -44,9 +44,6 @@ EditorBuildSettings: - enabled: 1 path: Assets/Tests/Manual/NetworkSceneManagerCallbacks/SceneWeAreSwitchingFrom.unity guid: 073bd2111475c0643be45b7abe6a97ad - - enabled: 1 - path: Assets/Scenes/MultiprocessTestScene.unity - guid: 76743cb7b342c49279327834918a9c6e - enabled: 1 path: Assets/Scenes/EmptyScene.unity guid: a2545a872c007404fbb6b0393ab74974 @@ -133,6 +130,9 @@ EditorBuildSettings: guid: 17b92153f7381d34fa48c4d5c0393d13 - enabled: 1 path: Assets/Tests/Manual/IntegrationTestScenes/EmptyScene3.unity + guid: bfb4addb64cf6455a88668eba2f759ba + - enabled: 1 + path: Assets/Tests/Manual/IntegrationTestScenes/MoveADynamicObjectInAwake.unity guid: abd4c8b51c445d54faa16c67ac973f1b - enabled: 1 path: Assets/Samples/SpawnObject/SimpleSpawn.unity @@ -158,7 +158,9 @@ EditorBuildSettings: - enabled: 1 path: Assets/Tests/Manual/NetworkAnimatorTests/AnimationBidirectionalTriggers/NetworkAnimatorDualTriggerCheer.unity guid: e12df855278120245a8a936a6a52b5bd + - enabled: 1 + path: Assets/Tests/Manual/IntegrationTestScenes/InSceneNetworkObjectMovesToDDOL.unity + guid: 4c20c17b4f92e634f8796b0460851d49 m_configObjects: com.unity.addressableassets: {fileID: 11400000, guid: 5a3d5c53c25349c48912726ae850f3b0, type: 2} - m_UseUCBPForAssetBundles: 0 diff --git a/testproject/ProjectSettings/EditorSettings.asset b/testproject/ProjectSettings/EditorSettings.asset index f92054474a..3ddd8f044f 100644 --- a/testproject/ProjectSettings/EditorSettings.asset +++ b/testproject/ProjectSettings/EditorSettings.asset @@ -23,7 +23,7 @@ EditorSettings: m_EnableTextureStreamingInEditMode: 1 m_EnableTextureStreamingInPlayMode: 1 m_AsyncShaderCompilation: 1 - m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptionsEnabled: 1 m_EnterPlayModeOptions: 3 m_ShowLightmapResolutionOverlay: 1 m_UseLegacyProbeSampleCount: 0 diff --git a/testproject/ProjectSettings/PackageManagerSettings.asset b/testproject/ProjectSettings/PackageManagerSettings.asset index b01b2f8da9..6e57db2799 100644 --- a/testproject/ProjectSettings/PackageManagerSettings.asset +++ b/testproject/ProjectSettings/PackageManagerSettings.asset @@ -12,32 +12,33 @@ MonoBehaviour: m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: - m_EnablePreviewPackages: 1 - m_EnablePackageDependencies: 1 + m_EnablePreReleasePackages: 0 m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 1 - m_Registries: - - m_Id: main + oneTimePackageErrorsPopUpShown: 0 + m_MainRegistry: + m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_IsUnityRegistry: 1 m_Capabilities: 7 + m_ConfigSource: 0 + m_Compliance: + m_Status: 0 + m_Violations: [] + m_ScopedRegistries: [] m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: - m_ErrorMessage: - m_Original: - m_Id: - m_Name: - m_Url: - m_Scopes: [] - m_IsDefault: 0 - m_Capabilities: 0 m_Modified: 0 - m_Name: - m_Url: - m_Scopes: - - - m_SelectedScopeIndex: 0 + m_ErrorMessage: + m_UserModificationsEntityId: + m_rawData: 568105584918791443 + m_OriginalEntityId: + m_rawData: 568105584918791444 + m_LoadAssets: 0 diff --git a/testproject/ProjectSettings/PhysicsCoreProjectSettings2D.asset b/testproject/ProjectSettings/PhysicsCoreProjectSettings2D.asset new file mode 100644 index 0000000000..049c7454b5 --- /dev/null +++ b/testproject/ProjectSettings/PhysicsCoreProjectSettings2D.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!176606843 &1 +PhysicsCoreProjectSettings2D: + m_ObjectHideFlags: 0 + m_PhysicsCoreSettings: {fileID: 0} diff --git a/testproject/ProjectSettings/ProjectSettings.asset b/testproject/ProjectSettings/ProjectSettings.asset index 3bb16ba357..7919f1cd96 100644 --- a/testproject/ProjectSettings/ProjectSettings.asset +++ b/testproject/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 28 + serializedVersion: 30 productGUID: bba99b16607b94720b7d04f7f1a82989 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -41,7 +41,6 @@ PlayerSettings: height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1280 defaultScreenHeight: 720 defaultScreenWidthWeb: 960 @@ -66,10 +65,15 @@ PlayerSettings: useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 + adjustIOSFPSUsingThermalState: 1 + thermalStateSeriousIOSFPS: 30 + thermalStateCriticalIOSFPS: 15 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 + androidRequestedVisibleInsets: 0 + androidSystemBarsBehavior: 2 androidDisplayOptions: 1 androidBlitType: 0 androidResizeableActivity: 0 @@ -84,6 +88,7 @@ PlayerSettings: defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 + callOnDisableOnAssetBundleUnload: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 @@ -114,6 +119,7 @@ PlayerSettings: xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 @@ -147,12 +153,10 @@ PlayerSettings: preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 allowHDRDisplaySupport: 0 @@ -174,9 +178,10 @@ PlayerSettings: tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 23 + AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 aotOptions: nimt-trampolines=1024 stripEngineCode: 1 iPhoneStrippingLevel: 0 @@ -191,13 +196,14 @@ PlayerSettings: VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSSimulatorArchitecture: 0 - iOSTargetOSVersionString: 13.0 + iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 13.0 + tvOSTargetOSVersionString: 15.0 VisionOSSdkVersion: 0 VisionOSTargetOSVersionString: 1.0 + xcodeProjectType: 0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -451,12 +457,6 @@ PlayerSettings: - m_BuildTarget: WindowsStandaloneSupport m_APIs: 0200000012000000 m_Automatic: 0 - m_BuildTargetVRSettings: - - m_BuildTarget: Standalone - m_Enabled: 0 - m_Devices: - - Oculus - - OpenVR m_DefaultShaderChunkSizeInMB: 16 m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 @@ -484,7 +484,7 @@ PlayerSettings: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: - macOSTargetOSVersion: 11.0 + macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 @@ -630,6 +630,8 @@ PlayerSettings: switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 switchUpgradedPlayerSettingsToNMETA: 0 + switchCaStoreSource: 0 + switchCaStoreFilePath: ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -739,14 +741,14 @@ PlayerSettings: webWasm2023: 0 webEnableSubmoduleStrippingCompatibility: 0 scriptingDefineSymbols: - Standalone: UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT + Standalone: UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT;NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL;NETCODE_EXPERIMENTAL_SINGLE_WORLD_HOST;OUT_OF_BAND_RPC additionalCompilerArguments: - Standalone: - - -warnaserror + Standalone: [] platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {} il2cppCodeGeneration: {} + il2cppLTOMode: {} il2cppStacktraceInformation: {} managedStrippingLevel: EmbeddedLinux: 1 @@ -794,8 +796,7 @@ PlayerSettings: metroDefaultTileSize: 1 metroTileForegroundText: 2 metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} - metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, - a: 1} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 syncCapabilities: 0 platformCapabilities: {} @@ -831,7 +832,6 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} cloudServicesEnabled: Analytics: 0 Build: 0 @@ -862,6 +862,7 @@ PlayerSettings: captureStartupLogs: {} activeInputHandler: 0 windowsGamepadBackendHint: 0 + enableDirectStorage: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -877,3 +878,4 @@ PlayerSettings: androidVulkanAllowFilterList: [] androidVulkanDeviceFilterListAsset: {fileID: 0} d3d12DeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/testproject/ProjectSettings/ProjectVersion.txt b/testproject/ProjectSettings/ProjectVersion.txt index c31f18a127..02866a2258 100644 --- a/testproject/ProjectSettings/ProjectVersion.txt +++ b/testproject/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.2.12f1 -m_EditorVersionWithRevision: 6000.2.12f1 (e89d5df0e333) +m_EditorVersion: 6000.6.0b5 +m_EditorVersionWithRevision: 6000.6.0b5 (b5238eaafb35)