diff --git a/.github/workflows/Semantic.yml b/.github/workflows/Semantic.yml new file mode 100644 index 000000000..cfed4c3a5 --- /dev/null +++ b/.github/workflows/Semantic.yml @@ -0,0 +1,48 @@ +name: Semantic Release + +on: + workflow_call: + +jobs: + SemanticRelease: + name: Semantic Release + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Delete Tests + run: | + Remove-Item -Recurse -Force Assets\Mirror\Tests + Remove-Item -Recurse -Force Assets\Mirror\Tests.meta + + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '3.1.100' + + # Installs nuget package from https://www.nuget.org/packages/unity-packer + - name: Install unity-packer + run: dotnet tool install -g unity-packer + + - name: Package + run: unity-packer pack Mirror.unitypackage Assets/Mirror Assets/Mirror Assets/ScriptTemplates Assets/ScriptTemplates LICENSE Assets/Mirror/LICENSE + + - uses: actions/upload-artifact@v3 + with: + name: Mirror.unitypackage + path: Mirror.unitypackage + + - name: Release + uses: cycjimmy/semantic-release-action@v3 + with: + extra_plugins: | + @semantic-release/exec + @semantic-release/changelog + @semantic-release/git + branch: master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/SonarQube.yml b/.github/workflows/SonarQube.yml new file mode 100644 index 000000000..4977158f0 --- /dev/null +++ b/.github/workflows/SonarQube.yml @@ -0,0 +1,36 @@ +name: SonarQube Analysis + +on: + workflow_call: + +jobs: + SonarQube: + name: SonarQube + runs-on: ubuntu-latest + + # available list of containers here: + # https://hub.docker.com/r/unityci/editor/tags?page=1&ordering=last_updated&name=ubuntu-2020.1.17f1-base + container: unityci/editor:ubuntu-2021.3.16f1-base-1.0.1 + + steps: + - name: SonarQube analysis + uses: MirrorNetworking/unity-runner@2.0.0 + with: + entrypoint: /sonar-scanner.sh + projectKey: vis2k_Mirror + projectName: Mirror + sonarOrganisation: vis2k + beginArguments: >- + /d:sonar.verbose="true" + /d:sonar.cs.nunit.reportsPaths=Tests/editmode-results.xml,Tests/playimode-results.xml + /d:sonar.cs.opencover.reportsPaths=Tests/workspace-opencov/EditMode/TestCoverageResults_0000.xml,Tests/workspace-opencov/PlayMode/TestCoverageResults_0000.xml + /d:sonar.coverage.exclusions=Assets/Mirror/Transports/**,Assets/Mirror/Examples/**,Assets/Mirror/Tests/** + /d:sonar.exclusions=Assets/Mirror/Runtime/Transport/SimpleWebTransport/** + # files ignored in code coverage: + # Assets/Mirror/Transports/** - Transports have their own tests and Sonar coverage + # Assets/Mirror/Examples/** - Examples don't need test coverage + # Assets/Mirror/Tests/** - Tests don't need test coverage + env: + FrameworkPathOverride: /opt/Unity/Editor/Data/MonoBleedingEdge/ + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/activation.yml b/.github/workflows/activation.yml index 0234d96d1..3fa84065e 100644 --- a/.github/workflows/activation.yml +++ b/.github/workflows/activation.yml @@ -1,19 +1,27 @@ -name: Acquire activation file -if: false() +name: Acquire Activation File + on: workflow_dispatch: {} + jobs: activation: - name: Request manual activation file 🔑 + name: Request Manual Activation File 🔑 runs-on: ubuntu-latest + steps: - # Request manual activation file - - name: Request manual activation file + - name: Request Manual Activation File id: getManualLicenseFile uses: game-ci/unity-request-activation-file@v2 - # Upload artifact (Unity_v20XX.X.XXXX.alf) - - name: Expose as artifact - uses: actions/upload-artifact@v2 + with: + unityVersion: 2019.4.40f1 + + - name: Upload License Request + uses: actions/upload-artifact@v3 with: name: ${{ steps.getManualLicenseFile.outputs.filePath }} path: ${{ steps.getManualLicenseFile.outputs.filePath }} + + - name: Next Steps + run: | + echo "Upload the alf file to https://license.unity3d.com/manual to get a ulf license file." + echo "Unzip and open the ulf license in Notepad and paste into secret called UNITY_LICENSE." diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 60e5ca399..c2fd0d236 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,156 +1,81 @@ -name: CI -#on: -# pull_request: {} -# push: { branches: [master] } +name: Run Unity Tests -on: +on: + pull_request: {} push: + branches: + - master paths-ignore: - - 'doc/**' + - 'Packages/**' + - 'ProjectSettings/**' + - '.gitattributes' + - '.gitignore' + - '.editorconfig' - '*.md' + - '*.yml' jobs: - - requestActivationFile: + RunUnityTests: + name: Run Unity Tests runs-on: ubuntu-latest - if: false + strategy: + matrix: + unityVersion: + - 2019.4.40f1 + - 2020.3.43f1 + - 2021.3.16f1 + - 2022.2.1f1 + steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - name: Request manual activation file - uses: MirrorNetworking/unity-runner@2.0.0 - id: getManualLicenseFile - with: - entrypoint: /request_activation.sh + # Do Not Enable Caching --- Library needs to be recompiled every time because Weaver + # Leaving this here for posterity to ensure we never turn this on. + #- name: Cache Library + # id: cache-library + # uses: actions/cache@v3 + # with: + # path: Library + # key: Library-${{ matrix.unityVersion }} - - name: Expose as artifact - uses: actions/upload-artifact@v1 - with: - name: Manual Activation File - path: ${{ steps.getManualLicenseFile.outputs.filePath }} - - CI: - name: Test - runs-on: ubuntu-latest - env: - UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} - if: true - steps: - - # Checkout repository (required to test local actions) - - name: Checkout repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Add Coverage Plugin - uses: canastro/copy-action@0.0.2 - with: - source: Packages/manifest-coverage.json - target: Packages/manifest.json - - - name: Activate license - uses: MirrorNetworking/unity-runner@2.0.0 - with: - entrypoint: /activate.sh - - - name: Generate Solution - uses: MirrorNetworking/unity-runner@2.0.0 - with: - # Arguments to pass to unity - args: -buildTarget StandaloneWindows64 -customBuildName Mirror -customBuildPath ./build/StandaloneWindows64 -projectPath . -executeMethod UnityEditor.SyncVS.SyncSolution -quit - - # Configure test runner - name: Run editor Tests - uses: MirrorNetworking/unity-runner@2.0.0 - with: - args: -runTests -testPlatform editmode -testResults Tests/editmode-results.xml -enableCodeCoverage -coverageResultsPath Tests + continue-on-error: true + uses: game-ci/unity-test-runner@main - - name: Run play Tests - uses: MirrorNetworking/unity-runner@2.0.0 + # We can use the same license for all Unity versions + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + + # testMode is set to editMode (editor tests only) until we fix playmode tests (or eliminate them) + # `-stackTraceLogType None` speeds up the job and vastly shrinks the log output + # If a test actually fails, we'll diagnose it locally anyway, so the stacktrace doesn't really help with: - args: -runTests -testPlatform playmode -testResults Tests/playmode-results.xml -enableCodeCoverage -coverageResultsPath Tests - - # Upload artifacts + projectPath: ./ + testMode: editmode + unityVersion: ${{ matrix.unityVersion }} + githubToken: ${{ secrets.GITHUB_TOKEN }} + customParameters: -stackTraceLogType None + - name: Archive test results - uses: actions/upload-artifact@v1 - if: always() + uses: actions/upload-artifact@v3 with: - name: Test results (editor mode) - path: Tests + name: Test Results ${{ matrix.unityVersion }} + path: artifacts - name: Publish test results - uses: MirrorNetworking/nunit-reporter@v1.0.9 - if: always() + uses: MirrorNetworking/nunit-reporter@master with: - path: "Tests/*.xml" + reportTitle: Test Report ${{ matrix.unityVersion }} + path: "artifacts/*.xml" access-token: ${{ secrets.GITHUB_TOKEN }} - - - name: SonarQube analysis - if: always() - uses: MirrorNetworking/unity-runner@2.0.0 - with: - entrypoint: /sonar-scanner.sh - projectKey: vis2k_Mirror - projectName: Mirror - sonarOrganisation: vis2k - beginArguments: >- - /d:sonar.verbose="true" - /d:sonar.cs.nunit.reportsPaths=Tests/editmode-results.xml,Tests/playimode-results.xml - /d:sonar.cs.opencover.reportsPaths=Tests/workspace-opencov/EditMode/TestCoverageResults_0000.xml,Tests/workspace-opencov/PlayMode/TestCoverageResults_0000.xml - /d:sonar.coverage.exclusions=Assets/Mirror/Runtime/Transport/Telepathy/**/*.cs,Assets/Mirror/Runtime/Transport/SimpleWebTransport/**,Assets/Mirror/Runtime/Transport/KCP/kcp2k/**,Assets/Mirror/Cloud/**/*.cs,Assets/Mirror/Examples/**/*.cs,Assets/Mirror/Tests/**/*.cs - /d:sonar.exclusions=Assets/Mirror/Runtime/Transport/SimpleWebTransport/** - # files ignored in code coverage: - # Assets/Mirror/Runtime/Transport/Telepathy/** - has its own test in the Telepathy repo - # Assets/Mirror/Runtime/Transport/SimpleWebTransport/** - fully exclude this because it has sonar running on its repo - # Assets/Mirror/Runtime/Transport/KCP/kcp2k/** - has its own test in the kcp2k repo - # Assets/Mirror/Cloud/** - has its own tests in private cloud repo - # Assets/Mirror/Examples/** - examples dont need test coverage - # Assets/Mirror/Tests/** - tests dont need test coverage - env: - FrameworkPathOverride: /opt/Unity/Editor/Data/MonoBleedingEdge/ - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + #SonarQube: + # name: SonarQube Analysis + # needs: RunUnityTests + # uses: ./.github/workflows/SonarQube.yml Release: - runs-on: windows-latest - needs: CI - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Delete Tests - run: | - Remove-Item -Recurse -Force Assets\Mirror\Tests - Remove-Item -Recurse -Force Assets\Mirror\Tests.meta - - - name: Setup dotnet - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.100' - - - name: Install unity-packer - run: dotnet tool install -g unity-packer - - - name: Package - run: | - unity-packer pack Mirror.unitypackage Assets/Mirror Assets/Mirror Assets/ScriptTemplates Assets/ScriptTemplates LICENSE Assets/Mirror/LICENSE - - - uses: actions/upload-artifact@v1 - with: - name: Mirror.unitypackage - path: Mirror.unitypackage - - - name: Release - uses: cycjimmy/semantic-release-action@v2 - with: - extra_plugins: | - @semantic-release/exec - @semantic-release/changelog - @semantic-release/git - branch: master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + name: Semantic Release + needs: RunUnityTests + uses: ./.github/workflows/Semantic.yml diff --git a/Assets/Mirror/Authenticators/BasicAuthenticator.cs b/Assets/Mirror/Authenticators/BasicAuthenticator.cs index f8f18650d..3c077c79c 100644 --- a/Assets/Mirror/Authenticators/BasicAuthenticator.cs +++ b/Assets/Mirror/Authenticators/BasicAuthenticator.cs @@ -50,7 +50,7 @@ public override void OnStartServer() /// /// Called on server from StopServer to reset the Authenticator - /// Server message handlers should be registered in this method. + /// Server message handlers should be unregistered in this method. /// public override void OnStopServer() { diff --git a/Assets/Mirror/Components/NetworkTransformReliable/NetworkTransformReliable.cs b/Assets/Mirror/Components/NetworkTransformReliable/NetworkTransformReliable.cs index 0e983db4a..4c260f549 100644 --- a/Assets/Mirror/Components/NetworkTransformReliable/NetworkTransformReliable.cs +++ b/Assets/Mirror/Components/NetworkTransformReliable/NetworkTransformReliable.cs @@ -235,7 +235,7 @@ public override void OnSerialize(NetworkWriter writer, bool initialState) // save serialized as 'last' for next delta compression if (syncPosition) Compression.ScaleToLong(snapshot.position, positionPrecision, out lastSerializedPosition); - if (syncScale) Compression.ScaleToLong(snapshot.position, scalePrecision, out lastSerializedScale); + if (syncScale) Compression.ScaleToLong(snapshot.scale, scalePrecision, out lastSerializedScale); // set 'last' last = snapshot; diff --git a/Assets/Mirror/Core/NetworkClient.cs b/Assets/Mirror/Core/NetworkClient.cs index 1c293cc1d..f5972e8c5 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -1675,12 +1675,12 @@ public static void Shutdown() // GUI ///////////////////////////////////////////////////////////////// // called from NetworkManager to display timeline interpolation status. // useful to indicate catchup / slowdown / dynamic adjustment etc. - internal static void OnGUI() + public static void OnGUI() { // only if in world if (!ready) return; - GUILayout.BeginArea(new Rect(10, 5, 400, 50)); + GUILayout.BeginArea(new Rect(10, 5, 500, 50)); GUILayout.BeginHorizontal("Box"); GUILayout.Label("Snapshot Interp.:"); @@ -1691,6 +1691,7 @@ internal static void OnGUI() GUILayout.Box($"timeline: {localTimeline:F2}"); GUILayout.Box($"buffer: {snapshots.Count}"); GUILayout.Box($"timescale: {localTimescale:F2}"); + GUILayout.Box($"BTM: {bufferTimeMultiplier:F2}"); GUILayout.EndHorizontal(); GUILayout.EndArea(); diff --git a/Assets/Mirror/Core/NetworkManagerHUD.cs b/Assets/Mirror/Core/NetworkManagerHUD.cs index 3e665f129..0a267fb4e 100644 --- a/Assets/Mirror/Core/NetworkManagerHUD.cs +++ b/Assets/Mirror/Core/NetworkManagerHUD.cs @@ -23,7 +23,7 @@ void Awake() void OnGUI() { - GUILayout.BeginArea(new Rect(10 + offsetX, 40 + offsetY, 215, 9999)); + GUILayout.BeginArea(new Rect(10 + offsetX, 40 + offsetY, 250, 9999)); if (!NetworkClient.isConnected && !NetworkServer.active) { StartButtons(); diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index c694659ff..c2f9a9359 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -180,7 +180,6 @@ public static void Shutdown() // Reset all statics here.... dontListen = false; - active = false; isLoadingScene = false; lastSendTime = 0; @@ -191,8 +190,13 @@ public static void Shutdown() handlers.Clear(); newObservers.Clear(); - // this calls spawned.Clear() + // destroy all spawned objects, _then_ set inactive. + // make sure .active is still true before calling this. + // otherwise modifying SyncLists in OnStopServer would throw + // because .IsWritable() check checks if NetworkServer.active. + // https://github.com/MirrorNetworking/Mirror/issues/3344 CleanupSpawned(); + active = false; // sets nextNetworkId to 1 // sets clientAuthorityCallback to null @@ -860,7 +864,11 @@ public static void DisconnectAll() // cleanup connections.Clear(); localConnection = null; - active = false; + // this used to set active=false. + // however, then Shutdown can't properly destroy objects: + // https://github.com/MirrorNetworking/Mirror/issues/3344 + // "DisconnectAll" should only disconnect all, not set inactive. + // active = false; } // add/remove/replace player /////////////////////////////////////////// diff --git a/Assets/Mirror/Core/SyncList.cs b/Assets/Mirror/Core/SyncList.cs index f9fabd53f..8b70eed22 100644 --- a/Assets/Mirror/Core/SyncList.cs +++ b/Assets/Mirror/Core/SyncList.cs @@ -243,7 +243,7 @@ public override void OnDeserializeDelta(NetworkReader reader) // ClientToServer needs to set dirty in server OnDeserialize. // no access check: server OnDeserialize can always // write, even for ClientToServer (for broadcasting). - AddOperation(Operation.OP_SET, i, oldItem, newItem, false); + AddOperation(Operation.OP_SET, index, oldItem, newItem, false); } break; }