diff --git a/Assets/Mirror/Core/SyncDictionary.cs b/Assets/Mirror/Core/SyncDictionary.cs index 34be71a59..37bec21cb 100644 --- a/Assets/Mirror/Core/SyncDictionary.cs +++ b/Assets/Mirror/Core/SyncDictionary.cs @@ -6,6 +6,20 @@ namespace Mirror { public class SyncIDictionary : SyncObject, IDictionary, IReadOnlyDictionary { + /// This is called after the item is added with TKey + public Action OnAdd; + + /// This is called after the item is changed with TKey. TValue is the OLD item + public Action OnSet; + + /// This is called after the item is removed with TKey. TValue is the OLD item + public Action OnRemove; + + /// This is called before the data is cleared + public Action OnClear; + + // Deprecated 2024-03-22 + [Obsolete("Use individual Actions, which pass OLD values where appropriate, instead.")] public Action Callback; protected readonly IDictionary objects; @@ -83,7 +97,25 @@ void AddOperation(Operation op, TKey key, TValue item, TValue oldItem, bool chec OnDirty?.Invoke(); } + switch (op) + { + case Operation.OP_ADD: + OnAdd?.Invoke(key); + break; + case Operation.OP_SET: + OnSet?.Invoke(key, oldItem); + break; + case Operation.OP_REMOVE: + OnRemove?.Invoke(key, oldItem); + break; + case Operation.OP_CLEAR: + OnClear?.Invoke(); + break; + } + +#pragma warning disable CS0618 // Type or member is obsolete Callback?.Invoke(op, key, item); +#pragma warning restore CS0618 // Type or member is obsolete } public override void OnSerializeAll(NetworkWriter writer) diff --git a/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs b/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs index e81f7e9aa..1a55c23f9 100644 --- a/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs +++ b/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs @@ -58,7 +58,9 @@ IEnumerator AddPlayersToMatchController() public override void OnStartClient() { - matchPlayerData.Callback += UpdateWins; +#pragma warning disable CS0618 // Type or member is obsolete + matchPlayerData.Callback = UpdateWins; +#pragma warning restore CS0618 // Type or member is obsolete canvasGroup.alpha = 1f; canvasGroup.interactable = true; diff --git a/Assets/Mirror/Tests/Editor/SyncCollections/SyncDictionaryTest.cs b/Assets/Mirror/Tests/Editor/SyncCollections/SyncDictionaryTest.cs index aa7bb0e71..1a41af07d 100644 --- a/Assets/Mirror/Tests/Editor/SyncCollections/SyncDictionaryTest.cs +++ b/Assets/Mirror/Tests/Editor/SyncCollections/SyncDictionaryTest.cs @@ -80,72 +80,131 @@ public void CurlyBracesConstructor() public void TestAdd() { // Adds a new entry with index of 4 using .Add method +#pragma warning disable 618 // Type or member is obsolete + bool called = false; + clientSyncDictionary.Callback = (op, key, item) => + { + called = true; + + Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_ADD)); + Assert.That(key, Is.EqualTo(4)); + Assert.That(item, Is.EqualTo("yay")); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); + }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + clientSyncDictionary.OnAdd = (key) => + { + actionCalled = true; + Assert.That(key, Is.EqualTo(4)); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); + }; + serverSyncDictionary.Add(4, "yay"); SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); Assert.That(clientSyncDictionary.ContainsKey(4)); Assert.That(clientSyncDictionary[4], Is.EqualTo("yay")); + Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test] public void TestClear() { // Verifies that the clear method works and that the data is still present for the Callback. +#pragma warning disable 618 // Type or member is obsolete bool called = false; - clientSyncDictionary.Callback = (op, index, item) => + clientSyncDictionary.Callback = (op, key, item) => { called = true; Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_CLEAR)); Assert.That(clientSyncDictionary.Count, Is.EqualTo(3)); }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + clientSyncDictionary.OnClear = () => + { + actionCalled = true; + Assert.That(clientSyncDictionary.Count, Is.EqualTo(3)); + }; + serverSyncDictionary.Clear(); SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); Assert.That(serverSyncDictionary, Is.EquivalentTo(new SyncDictionary())); Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test] public void TestSet() { // Overwrites an existing entry +#pragma warning disable 618 // Type or member is obsolete bool called = false; - clientSyncDictionary.Callback = (op, index, item) => + clientSyncDictionary.Callback = (op, key, item) => { called = true; Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_SET)); - Assert.That(index, Is.EqualTo(1)); + Assert.That(key, Is.EqualTo(1)); Assert.That(item, Is.EqualTo("yay")); - Assert.That(clientSyncDictionary[index], Is.EqualTo("yay")); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + clientSyncDictionary.OnSet = (key, oldItem) => + { + actionCalled = true; + Assert.That(key, Is.EqualTo(1)); + Assert.That(oldItem, Is.EqualTo("World")); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); + }; + serverSyncDictionary[1] = "yay"; SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); Assert.That(clientSyncDictionary.ContainsKey(1)); Assert.That(clientSyncDictionary[1], Is.EqualTo("yay")); Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test] public void TestBareSet() { // Adds a new entry with index of 4 without using .Add method +#pragma warning disable 618 // Type or member is obsolete bool called = false; - clientSyncDictionary.Callback = (op, index, item) => + clientSyncDictionary.Callback = (op, key, item) => { called = true; Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_ADD)); - Assert.That(index, Is.EqualTo(4)); + Assert.That(key, Is.EqualTo(4)); Assert.That(item, Is.EqualTo("yay")); - Assert.That(clientSyncDictionary[index], Is.EqualTo("yay")); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + clientSyncDictionary.OnAdd = (key) => + { + actionCalled = true; + Assert.That(key, Is.EqualTo(4)); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); + }; + serverSyncDictionary[4] = "yay"; SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); Assert.That(clientSyncDictionary.ContainsKey(4)); Assert.That(clientSyncDictionary[4], Is.EqualTo("yay")); Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test] @@ -209,52 +268,90 @@ public void TestContains() [Test] public void CallbackTest() { +#pragma warning disable 618 // Type or member is obsolete bool called = false; - clientSyncDictionary.Callback = (op, index, item) => + clientSyncDictionary.Callback = (op, key, item) => { called = true; Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_ADD)); - Assert.That(index, Is.EqualTo(3)); + Assert.That(key, Is.EqualTo(3)); Assert.That(item, Is.EqualTo("yay")); - Assert.That(clientSyncDictionary[index], Is.EqualTo("yay")); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + clientSyncDictionary.OnAdd = (key) => + { + actionCalled = true; + Assert.That(key, Is.EqualTo(3)); + Assert.That(clientSyncDictionary[key], Is.EqualTo("yay")); + }; + serverSyncDictionary.Add(3, "yay"); SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test] public void ServerCallbackTest() { +#pragma warning disable 618 // Type or member is obsolete bool called = false; - serverSyncDictionary.Callback = (op, index, item) => + serverSyncDictionary.Callback = (op, key, item) => { called = true; Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_ADD)); - Assert.That(index, Is.EqualTo(3)); + Assert.That(key, Is.EqualTo(3)); Assert.That(item, Is.EqualTo("yay")); - Assert.That(serverSyncDictionary[index], Is.EqualTo("yay")); + Assert.That(serverSyncDictionary[key], Is.EqualTo("yay")); }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + serverSyncDictionary.OnAdd = (key) => + { + actionCalled = true; + Assert.That(key, Is.EqualTo(3)); + Assert.That(serverSyncDictionary[key], Is.EqualTo("yay")); + }; + serverSyncDictionary[3] = "yay"; Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test] public void CallbackRemoveTest() { +#pragma warning disable 618 // Type or member is obsolete bool called = false; clientSyncDictionary.Callback = (op, key, item) => { called = true; Assert.That(op, Is.EqualTo(SyncDictionary.Operation.OP_REMOVE)); + Assert.That(key, Is.EqualTo(1)); Assert.That(item, Is.EqualTo("World")); }; +#pragma warning restore 618 // Type or member is obsolete + + bool actionCalled = false; + clientSyncDictionary.OnRemove = (key, oldItem) => + { + actionCalled = true; + Assert.That(key, Is.EqualTo(1)); + Assert.That(oldItem, Is.EqualTo("World")); + Assert.That(!clientSyncDictionary.ContainsKey(1)); + }; + serverSyncDictionary.Remove(1); SerializeDeltaTo(serverSyncDictionary, clientSyncDictionary); Assert.That(called, Is.True); + Assert.That(actionCalled, Is.True); } [Test]