Skip to content

Commit

Permalink
feat: supports scriptable objects (#1471)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
paulpach committed Feb 9, 2020
1 parent ab6a03b commit 0f10c72
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 49 deletions.
16 changes: 10 additions & 6 deletions Assets/Mirror/Editor/Weaver/Readers.cs
Expand Up @@ -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");
Expand Down Expand Up @@ -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

Expand Down
14 changes: 14 additions & 0 deletions Assets/Mirror/Editor/Weaver/Resolvers.cs
Expand Up @@ -114,6 +114,20 @@ public static GenericInstanceMethod ResolveMethodGeneric(TypeReference t, Assemb
return null;
}

public static MethodReference ResolveMethod(TypeReference t, AssemblyDefinition scriptDef, System.Func<MethodDefinition, bool> 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)
Expand Down
6 changes: 6 additions & 0 deletions Assets/Mirror/Editor/Weaver/Weaver.cs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
5 changes: 0 additions & 5 deletions Assets/Mirror/Editor/Weaver/Writers.cs
Expand Up @@ -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");
Expand Down
44 changes: 44 additions & 0 deletions 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<byte> 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<MyScriptableObject>()
};

message.scriptableObject.someData = 10;

byte[] data = MessagePacker.Pack(message);

ScriptableObjectMessage unpacked = MessagePacker.Unpack<ScriptableObjectMessage>(data);

Assert.That(unpacked.scriptableObject, Is.Not.Null);
Assert.That(unpacked.scriptableObject.someData, Is.EqualTo(10));
}

}
}
11 changes: 11 additions & 0 deletions Assets/Mirror/Tests/Editor/ScriptableObjectWriterTest.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions Assets/Mirror/Tests/Editor/WeaverTest.cs
Expand Up @@ -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()
{
Expand Down

This file was deleted.

0 comments on commit 0f10c72

Please sign in to comment.