mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
Merge branch 'master' into Prevent-Log-Spam
This commit is contained in:
commit
35a2bbaf88
48
.github/workflows/Semantic.yml
vendored
Normal file
48
.github/workflows/Semantic.yml
vendored
Normal file
@ -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 }}
|
36
.github/workflows/SonarQube.yml
vendored
Normal file
36
.github/workflows/SonarQube.yml
vendored
Normal file
@ -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 }}
|
24
.github/workflows/activation.yml
vendored
24
.github/workflows/activation.yml
vendored
@ -1,19 +1,27 @@
|
|||||||
name: Acquire activation file
|
name: Acquire Activation File
|
||||||
if: false()
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
activation:
|
activation:
|
||||||
name: Request manual activation file 🔑
|
name: Request Manual Activation File 🔑
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Request manual activation file
|
- name: Request Manual Activation File
|
||||||
- name: Request manual activation file
|
|
||||||
id: getManualLicenseFile
|
id: getManualLicenseFile
|
||||||
uses: game-ci/unity-request-activation-file@v2
|
uses: game-ci/unity-request-activation-file@v2
|
||||||
# Upload artifact (Unity_v20XX.X.XXXX.alf)
|
with:
|
||||||
- name: Expose as artifact
|
unityVersion: 2019.4.40f1
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
|
- name: Upload License Request
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.getManualLicenseFile.outputs.filePath }}
|
name: ${{ steps.getManualLicenseFile.outputs.filePath }}
|
||||||
path: ${{ 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."
|
||||||
|
183
.github/workflows/main.yml
vendored
183
.github/workflows/main.yml
vendored
@ -1,156 +1,81 @@
|
|||||||
name: CI
|
name: Run Unity Tests
|
||||||
#on:
|
|
||||||
# pull_request: {}
|
|
||||||
# push: { branches: [master] }
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'doc/**'
|
- 'Packages/**'
|
||||||
|
- 'ProjectSettings/**'
|
||||||
|
- '.gitattributes'
|
||||||
|
- '.gitignore'
|
||||||
|
- '.editorconfig'
|
||||||
- '*.md'
|
- '*.md'
|
||||||
|
- '*.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
RunUnityTests:
|
||||||
requestActivationFile:
|
name: Run Unity Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: false
|
strategy:
|
||||||
|
matrix:
|
||||||
|
unityVersion:
|
||||||
|
- 2019.4.40f1
|
||||||
|
- 2020.3.43f1
|
||||||
|
- 2021.3.16f1
|
||||||
|
- 2022.2.1f1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Request manual activation file
|
# Do Not Enable Caching --- Library needs to be recompiled every time because Weaver
|
||||||
uses: MirrorNetworking/unity-runner@2.0.0
|
# Leaving this here for posterity to ensure we never turn this on.
|
||||||
id: getManualLicenseFile
|
#- name: Cache Library
|
||||||
with:
|
# id: cache-library
|
||||||
entrypoint: /request_activation.sh
|
# uses: actions/cache@v3
|
||||||
|
# with:
|
||||||
|
# path: Library
|
||||||
|
# key: Library-${{ matrix.unityVersion }}
|
||||||
|
|
||||||
- name: Expose as artifact
|
- name: Run editor Tests
|
||||||
uses: actions/upload-artifact@v1
|
continue-on-error: true
|
||||||
with:
|
uses: game-ci/unity-test-runner@main
|
||||||
name: Manual Activation File
|
|
||||||
path: ${{ steps.getManualLicenseFile.outputs.filePath }}
|
|
||||||
|
|
||||||
CI:
|
# We can use the same license for all Unity versions
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
if: true
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# Checkout repository (required to test local actions)
|
# testMode is set to editMode (editor tests only) until we fix playmode tests (or eliminate them)
|
||||||
- name: Checkout repository
|
# `-stackTraceLogType None` speeds up the job and vastly shrinks the log output
|
||||||
uses: actions/checkout@v2
|
# If a test actually fails, we'll diagnose it locally anyway, so the stacktrace doesn't really help
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
projectPath: ./
|
||||||
|
testMode: editmode
|
||||||
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
|
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
customParameters: -stackTraceLogType None
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- name: Run play Tests
|
|
||||||
uses: MirrorNetworking/unity-runner@2.0.0
|
|
||||||
with:
|
|
||||||
args: -runTests -testPlatform playmode -testResults Tests/playmode-results.xml -enableCodeCoverage -coverageResultsPath Tests
|
|
||||||
|
|
||||||
# Upload artifacts
|
|
||||||
- name: Archive test results
|
- name: Archive test results
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v3
|
||||||
if: always()
|
|
||||||
with:
|
with:
|
||||||
name: Test results (editor mode)
|
name: Test Results ${{ matrix.unityVersion }}
|
||||||
path: Tests
|
path: artifacts
|
||||||
|
|
||||||
- name: Publish test results
|
- name: Publish test results
|
||||||
uses: MirrorNetworking/nunit-reporter@v1.0.9
|
uses: MirrorNetworking/nunit-reporter@master
|
||||||
if: always()
|
|
||||||
with:
|
with:
|
||||||
path: "Tests/*.xml"
|
reportTitle: Test Report ${{ matrix.unityVersion }}
|
||||||
|
path: "artifacts/*.xml"
|
||||||
access-token: ${{ secrets.GITHUB_TOKEN }}
|
access-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: SonarQube analysis
|
#SonarQube:
|
||||||
if: always()
|
# name: SonarQube Analysis
|
||||||
uses: MirrorNetworking/unity-runner@2.0.0
|
# needs: RunUnityTests
|
||||||
with:
|
# uses: ./.github/workflows/SonarQube.yml
|
||||||
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 }}
|
|
||||||
|
|
||||||
Release:
|
Release:
|
||||||
runs-on: windows-latest
|
name: Semantic Release
|
||||||
needs: CI
|
needs: RunUnityTests
|
||||||
steps:
|
uses: ./.github/workflows/Semantic.yml
|
||||||
- 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 }}
|
|
||||||
|
@ -50,7 +50,7 @@ public override void OnStartServer()
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called on server from StopServer to reset the Authenticator
|
/// Called on server from StopServer to reset the Authenticator
|
||||||
/// <para>Server message handlers should be registered in this method.</para>
|
/// <para>Server message handlers should be unregistered in this method.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void OnStopServer()
|
public override void OnStopServer()
|
||||||
{
|
{
|
||||||
|
@ -235,7 +235,7 @@ public override void OnSerialize(NetworkWriter writer, bool initialState)
|
|||||||
|
|
||||||
// save serialized as 'last' for next delta compression
|
// save serialized as 'last' for next delta compression
|
||||||
if (syncPosition) Compression.ScaleToLong(snapshot.position, positionPrecision, out lastSerializedPosition);
|
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'
|
// set 'last'
|
||||||
last = snapshot;
|
last = snapshot;
|
||||||
|
@ -1675,12 +1675,12 @@ public static void Shutdown()
|
|||||||
// GUI /////////////////////////////////////////////////////////////////
|
// GUI /////////////////////////////////////////////////////////////////
|
||||||
// called from NetworkManager to display timeline interpolation status.
|
// called from NetworkManager to display timeline interpolation status.
|
||||||
// useful to indicate catchup / slowdown / dynamic adjustment etc.
|
// useful to indicate catchup / slowdown / dynamic adjustment etc.
|
||||||
internal static void OnGUI()
|
public static void OnGUI()
|
||||||
{
|
{
|
||||||
// only if in world
|
// only if in world
|
||||||
if (!ready) return;
|
if (!ready) return;
|
||||||
|
|
||||||
GUILayout.BeginArea(new Rect(10, 5, 400, 50));
|
GUILayout.BeginArea(new Rect(10, 5, 500, 50));
|
||||||
|
|
||||||
GUILayout.BeginHorizontal("Box");
|
GUILayout.BeginHorizontal("Box");
|
||||||
GUILayout.Label("Snapshot Interp.:");
|
GUILayout.Label("Snapshot Interp.:");
|
||||||
@ -1691,6 +1691,7 @@ internal static void OnGUI()
|
|||||||
GUILayout.Box($"timeline: {localTimeline:F2}");
|
GUILayout.Box($"timeline: {localTimeline:F2}");
|
||||||
GUILayout.Box($"buffer: {snapshots.Count}");
|
GUILayout.Box($"buffer: {snapshots.Count}");
|
||||||
GUILayout.Box($"timescale: {localTimescale:F2}");
|
GUILayout.Box($"timescale: {localTimescale:F2}");
|
||||||
|
GUILayout.Box($"BTM: {bufferTimeMultiplier:F2}");
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
GUILayout.EndArea();
|
GUILayout.EndArea();
|
||||||
|
@ -23,7 +23,7 @@ void Awake()
|
|||||||
|
|
||||||
void OnGUI()
|
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)
|
if (!NetworkClient.isConnected && !NetworkServer.active)
|
||||||
{
|
{
|
||||||
StartButtons();
|
StartButtons();
|
||||||
|
@ -180,7 +180,6 @@ public static void Shutdown()
|
|||||||
|
|
||||||
// Reset all statics here....
|
// Reset all statics here....
|
||||||
dontListen = false;
|
dontListen = false;
|
||||||
active = false;
|
|
||||||
isLoadingScene = false;
|
isLoadingScene = false;
|
||||||
lastSendTime = 0;
|
lastSendTime = 0;
|
||||||
|
|
||||||
@ -191,8 +190,13 @@ public static void Shutdown()
|
|||||||
handlers.Clear();
|
handlers.Clear();
|
||||||
newObservers.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();
|
CleanupSpawned();
|
||||||
|
active = false;
|
||||||
|
|
||||||
// sets nextNetworkId to 1
|
// sets nextNetworkId to 1
|
||||||
// sets clientAuthorityCallback to null
|
// sets clientAuthorityCallback to null
|
||||||
@ -860,7 +864,11 @@ public static void DisconnectAll()
|
|||||||
// cleanup
|
// cleanup
|
||||||
connections.Clear();
|
connections.Clear();
|
||||||
localConnection = null;
|
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 ///////////////////////////////////////////
|
// add/remove/replace player ///////////////////////////////////////////
|
||||||
|
@ -243,7 +243,7 @@ public override void OnDeserializeDelta(NetworkReader reader)
|
|||||||
// ClientToServer needs to set dirty in server OnDeserialize.
|
// ClientToServer needs to set dirty in server OnDeserialize.
|
||||||
// no access check: server OnDeserialize can always
|
// no access check: server OnDeserialize can always
|
||||||
// write, even for ClientToServer (for broadcasting).
|
// write, even for ClientToServer (for broadcasting).
|
||||||
AddOperation(Operation.OP_SET, i, oldItem, newItem, false);
|
AddOperation(Operation.OP_SET, index, oldItem, newItem, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user