From 0f10c72744864ac55d2e1aa96ba8d7713c77d9e7 Mon Sep 17 00:00:00 2001 From: Paul Pacheco Date: Sun, 9 Feb 2020 13:16:13 -0600 Subject: [PATCH] feat: supports scriptable objects (#1471) * feat: supports scriptable objects Now you can pass scriptable objects in commands, rpcs and syncvars For example: ```cs class Weapon: ScriptableObject { public string name; public string description; public int damage; ... } class Player : NetworkBehaviour { [SyncVar] Weapon equipped; ... } ``` Scriptable objects will be created in the client using ScriptableObject.CreateInstance. If users want something else they can provide a custom serializer (that has not changed) * fix: remove scriptableobject error Tests The test that checks that scrscriptableobjects give error is no longer valid --- Assets/Mirror/Editor/Weaver/Readers.cs | 16 ++++--- Assets/Mirror/Editor/Weaver/Resolvers.cs | 14 ++++++ Assets/Mirror/Editor/Weaver/Weaver.cs | 6 +++ Assets/Mirror/Editor/Weaver/Writers.cs | 5 --- .../Editor/ScriptableObjectWriterTest.cs | 44 +++++++++++++++++++ .../Editor/ScriptableObjectWriterTest.cs.meta | 11 +++++ Assets/Mirror/Tests/Editor/WeaverTest.cs | 7 --- .../SyncVarsDerivedScriptableObject.cs | 31 ------------- 8 files changed, 85 insertions(+), 49 deletions(-) create mode 100644 Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs create mode 100644 Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta delete mode 100644 Assets/Mirror/Tests/Editor/WeaverTests~/SyncVarsDerivedScriptableObject.cs diff --git a/Assets/Mirror/Editor/Weaver/Readers.cs b/Assets/Mirror/Editor/Weaver/Readers.cs index 46d9476ccb3..42fdc7bfce5 100644 --- a/Assets/Mirror/Editor/Weaver/Readers.cs +++ b/Assets/Mirror/Editor/Weaver/Readers.cs @@ -46,11 +46,6 @@ public static MethodReference GetReadFunc(TypeReference variable, int recursionC Weaver.Error($"{variable} is not a supported type"); return null; } - if (td.IsDerivedFrom(Weaver.ScriptableObjectType)) - { - Weaver.Error($"Cannot generate reader for scriptable object {variable}. Use a supported type or provide a custom reader"); - return null; - } if (td.IsDerivedFrom(Weaver.ComponentType)) { Weaver.Error($"Cannot generate reader for component type {variable}. Use a supported type or provide a custom reader"); @@ -331,13 +326,22 @@ static MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable ILProcessor worker = readerFunc.Body.GetILProcessor(); + TypeDefinition td = variable.Resolve(); + if (variable.IsValueType) { // structs are created with Initobj worker.Append(worker.Create(OpCodes.Ldloca, 0)); worker.Append(worker.Create(OpCodes.Initobj, variable)); } - else + else if (td.IsDerivedFrom(Weaver.ScriptableObjectType)) + { + GenericInstanceMethod genericInstanceMethod = new GenericInstanceMethod(Weaver.ScriptableObjectCreateInstanceMethod); + genericInstanceMethod.GenericArguments.Add(variable); + worker.Append(worker.Create(OpCodes.Call, genericInstanceMethod)); + worker.Append(worker.Create(OpCodes.Stloc_0)); + } + else { // classes are created with their constructor diff --git a/Assets/Mirror/Editor/Weaver/Resolvers.cs b/Assets/Mirror/Editor/Weaver/Resolvers.cs index 434aed428fd..67148d46c23 100644 --- a/Assets/Mirror/Editor/Weaver/Resolvers.cs +++ b/Assets/Mirror/Editor/Weaver/Resolvers.cs @@ -114,6 +114,20 @@ public static GenericInstanceMethod ResolveMethodGeneric(TypeReference t, Assemb return null; } + public static MethodReference ResolveMethod(TypeReference t, AssemblyDefinition scriptDef, System.Func predicate) + { + foreach (MethodDefinition methodRef in t.Resolve().Methods) + { + if (predicate(methodRef)) + { + return scriptDef.MainModule.ImportReference(methodRef); + } + } + + Weaver.Error($"Method not found"); + return null; + } + public static FieldReference ResolveField(TypeReference tr, AssemblyDefinition scriptDef, string name) { foreach (FieldDefinition fd in tr.Resolve().Fields) diff --git a/Assets/Mirror/Editor/Weaver/Weaver.cs b/Assets/Mirror/Editor/Weaver/Weaver.cs index bf4369b9342..92e76cc4a23 100644 --- a/Assets/Mirror/Editor/Weaver/Weaver.cs +++ b/Assets/Mirror/Editor/Weaver/Weaver.cs @@ -53,6 +53,8 @@ class Weaver public static TypeReference SyncSetType; public static TypeReference SyncDictionaryType; + public static MethodReference ScriptableObjectCreateInstanceMethod; + public static MethodReference NetworkBehaviourDirtyBitsReference; public static MethodReference GetPooledWriterReference; public static MethodReference RecycleWriterReference; @@ -270,6 +272,10 @@ static void SetupTargetTypes() MonoBehaviourType = UnityAssembly.MainModule.GetType("UnityEngine.MonoBehaviour"); ScriptableObjectType = UnityAssembly.MainModule.GetType("UnityEngine.ScriptableObject"); + ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod( + ScriptableObjectType, CurrentAssembly, + md => md.Name == "CreateInstance" && md.HasGenericParameters); + NetworkConnectionType = NetAssembly.MainModule.GetType("Mirror.NetworkConnection"); NetworkConnectionType = CurrentAssembly.MainModule.ImportReference(NetworkConnectionType); diff --git a/Assets/Mirror/Editor/Weaver/Writers.cs b/Assets/Mirror/Editor/Weaver/Writers.cs index 77bc482aa0d..5e51c0281b2 100644 --- a/Assets/Mirror/Editor/Weaver/Writers.cs +++ b/Assets/Mirror/Editor/Weaver/Writers.cs @@ -53,11 +53,6 @@ public static MethodReference GetWriteFunc(TypeReference variable, int recursion Weaver.Error($"{variable} is not a supported type. Use a supported type or provide a custom writer"); return null; } - if (td.IsDerivedFrom(Weaver.ScriptableObjectType)) - { - Weaver.Error($"Cannot generate writer for scriptable object {variable}. Use a supported type or provide a custom writer"); - return null; - } if (td.IsDerivedFrom(Weaver.ComponentType)) { Weaver.Error($"Cannot generate writer for component type {variable}. Use a supported type or provide a custom writer"); diff --git a/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs new file mode 100644 index 00000000000..a1f162c495c --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs @@ -0,0 +1,44 @@ +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Mirror.Tests +{ + internal class MyScriptableObject : ScriptableObject + { + public int someData; + } + + [TestFixture] + public class ScriptableObjectWriterTest + { + + // ArraySegment is a special case, optimized for no copy and no allocation + // other types are generated by the weaver + + + class ScriptableObjectMessage : MessageBase + { + public MyScriptableObject scriptableObject; + } + + [Test] + public void TestWriteScriptableObject() + { + ScriptableObjectMessage message = new ScriptableObjectMessage + { + scriptableObject = ScriptableObject.CreateInstance() + }; + + message.scriptableObject.someData = 10; + + byte[] data = MessagePacker.Pack(message); + + ScriptableObjectMessage unpacked = MessagePacker.Unpack(data); + + Assert.That(unpacked.scriptableObject, Is.Not.Null); + Assert.That(unpacked.scriptableObject.someData, Is.EqualTo(10)); + } + + } +} diff --git a/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta new file mode 100644 index 00000000000..03cd254bd0e --- /dev/null +++ b/Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6c10e2d494114e9190f56d13a894c82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/WeaverTest.cs b/Assets/Mirror/Tests/Editor/WeaverTest.cs index bfa7ef74799..c6319a01350 100644 --- a/Assets/Mirror/Tests/Editor/WeaverTest.cs +++ b/Assets/Mirror/Tests/Editor/WeaverTest.cs @@ -167,13 +167,6 @@ public void SyncVarsDerivedNetworkBehaviour() Assert.That(weaverErrors, Contains.Item("Mirror.Weaver error: Cannot generate writer for component type MirrorTest.MirrorTestPlayer/MySyncVar. Use a supported type or provide a custom writer")); } - [Test] - public void SyncVarsDerivedScriptableObject() - { - Assert.That(CompilationFinishedHook.WeaveFailed, Is.True); - Assert.That(weaverErrors, Contains.Item("Mirror.Weaver error: Cannot generate writer for scriptable object MirrorTest.MirrorTestPlayer/MySyncVar. Use a supported type or provide a custom writer")); - } - [Test] public void SyncVarsStatic() { diff --git a/Assets/Mirror/Tests/Editor/WeaverTests~/SyncVarsDerivedScriptableObject.cs b/Assets/Mirror/Tests/Editor/WeaverTests~/SyncVarsDerivedScriptableObject.cs deleted file mode 100644 index 358793383c5..00000000000 --- a/Assets/Mirror/Tests/Editor/WeaverTests~/SyncVarsDerivedScriptableObject.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UnityEngine; -using Mirror; - -namespace MirrorTest -{ - class MirrorTestPlayer : NetworkBehaviour - { - [SyncVar(hook = nameof(OnChangeHealth))] - int health; - - class MySyncVar : ScriptableObject - { - int abc = 123; - } - [SyncVar] - MySyncVar invalidVar = new MySyncVar(); - - public void TakeDamage(int amount) - { - if (!isServer) - return; - - health -= amount; - } - - void OnChangeHealth(int oldHealth, int newHealth) - { - // do things with your health bar - } - } -}