Skip to content

Commit

Permalink
feat: custom reader/writer via extension methods (#1047)
Browse files Browse the repository at this point in the history
* Remove hardcoded writers from weaver

* Remove hardcoded readers from weaver

* Remove unused stuff

* cleanups

* Cleanup some comments

* remove unused import

* Update Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs

* Improve comments a bit

* small cleanups

* Remove redundant private keyword

* Remove unnecesary blank space

* Remove unused field

* Measure how long it takes to find the readers and writers

* Find all extension methods for readers and writers

* Get rid of debugging log

* Don't comment obvious stuff

* Don't swallow exception

* only scan static classes for extension methods

* Add unit test for custom readers and writers

* Update Assets/Mirror/Editor/Weaver/Weaver.cs

* Update Assets/Mirror/Runtime/NetworkWriterExt.cs

* Update ReaderWriterProcessor.cs

* Renamed to avoid abreviations

* Move the extensions inside the same .cs

* Update NetworkReader.cs

* Update NetworkWriter.cs
  • Loading branch information
paulpach authored and miwarnec committed Sep 2, 2019
1 parent e0b66db commit b45afad
Show file tree
Hide file tree
Showing 10 changed files with 622 additions and 505 deletions.
10 changes: 10 additions & 0 deletions Assets/Mirror/Editor/Weaver/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,15 @@ public static MethodReference MakeHostInstanceGeneric(this MethodReference self,
return Weaver.CurrentAssembly.MainModule.ImportReference(reference);
}

public static CustomAttribute GetCustomAttribute(this MethodDefinition method, string attributeName)
{
foreach (CustomAttribute ca in method.CustomAttributes)
{
if (ca.AttributeType.FullName == attributeName)
return ca;
}
return null;
}

}
}
89 changes: 89 additions & 0 deletions Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using Mono.CecilX;
using UnityEditor.Compilation;
using System.Linq;
using System.Collections.Generic;

namespace Mirror.Weaver
{
public static class ReaderWriterProcessor
{
// find all readers and writers and register them
public static void ProcessReadersAndWriters(AssemblyDefinition CurrentAssembly)
{
Readers.Init();
Writers.Init();

foreach (Assembly unityAsm in CompilationPipeline.GetAssemblies())
{
if (unityAsm.name != CurrentAssembly.Name.Name)
{
using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver())
using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(unityAsm.outputPath, new ReaderParameters { ReadWrite = false, ReadSymbols = true, AssemblyResolver = asmResolver }))
{
ProcessAssemblyClasses(CurrentAssembly, assembly);
}
}
}

ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly);
}

static void ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyDefinition assembly)
{
foreach (TypeDefinition klass in assembly.MainModule.Types)
{
// extension methods only live in static classes
// static classes are represented as sealed and abstract
if (klass.IsAbstract && klass.IsSealed)
{
LoadWriters(CurrentAssembly, klass);
LoadReaders(CurrentAssembly, klass);
}
}
}

static void LoadWriters(AssemblyDefinition currentAssembly, TypeDefinition klass)
{
// register all the writers in this class. Skip the ones with wrong signature
foreach (MethodDefinition method in klass.Methods)
{
if (method.Parameters.Count != 2)
continue;

if (method.Parameters[0].ParameterType.FullName != "Mirror.NetworkWriter")
continue;

if (method.ReturnType.FullName != "System.Void")
continue;

if (method.GetCustomAttribute("System.Runtime.CompilerServices.ExtensionAttribute") == null)
continue;

TypeReference dataType = method.Parameters[1].ParameterType;
Writers.Register(dataType, currentAssembly.MainModule.ImportReference(method));
}
}

static void LoadReaders(AssemblyDefinition currentAssembly, TypeDefinition klass)
{
// register all the reader in this class. Skip the ones with wrong signature
foreach (MethodDefinition method in klass.Methods)
{
if (method.Parameters.Count != 1)
continue;

if (method.Parameters[0].ParameterType.FullName != "Mirror.NetworkReader")
continue;

if (method.ReturnType.FullName == "System.Void")
continue;

if (method.GetCustomAttribute("System.Runtime.CompilerServices.ExtensionAttribute") == null)
continue;

Readers.Register(method.ReturnType, currentAssembly.MainModule.ImportReference(method));
}
}
}
}

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

50 changes: 8 additions & 42 deletions Assets/Mirror/Editor/Weaver/Readers.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Mono.CecilX;
using Mono.CecilX.Cil;
Expand All @@ -11,56 +12,21 @@ public static class Readers
const int MaxRecursionCount = 128;
static Dictionary<string, MethodReference> readFuncs;

public static void Init(AssemblyDefinition CurrentAssembly)
public static void Init()
{
TypeReference networkReaderType = Weaver.NetworkReaderType;

readFuncs = new Dictionary<string, MethodReference>
{
{ Weaver.singleType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadSingle") },
{ Weaver.doubleType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadDouble") },
{ Weaver.boolType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadBoolean") },
{ Weaver.stringType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadString") },
{ Weaver.int64Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadPackedInt64") },
{ Weaver.uint64Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadPackedUInt64") },
{ Weaver.int32Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadPackedInt32") },
{ Weaver.uint32Type.FullName,Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadPackedUInt32") },
{ Weaver.int16Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadInt16") },
{ Weaver.uint16Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadUInt16") },
{ Weaver.byteType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadByte") },
{ Weaver.sbyteType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadSByte") },
{ Weaver.charType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadChar") },
{ Weaver.decimalType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadDecimal") },
{ Weaver.vector2Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadVector2") },
{ Weaver.vector3Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadVector3") },
{ Weaver.vector4Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadVector4") },
{ Weaver.vector2IntType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadVector2Int") },
{ Weaver.vector3IntType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadVector3Int") },
{ Weaver.colorType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadColor") },
{ Weaver.color32Type.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadColor32") },
{ Weaver.quaternionType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadQuaternion") },
{ Weaver.rectType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadRect") },
{ Weaver.planeType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadPlane") },
{ Weaver.rayType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadRay") },
{ Weaver.matrixType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadMatrix4x4") },
{ Weaver.guidType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadGuid") },
{ Weaver.gameObjectType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadGameObject") },
{ Weaver.NetworkIdentityType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadNetworkIdentity") },
{ Weaver.transformType.FullName, Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadTransform") },
{ "System.Byte[]", Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadBytesAndSize") },
{ "System.ArraySegment`1<System.Byte>", Resolvers.ResolveMethod(networkReaderType, CurrentAssembly, "ReadBytesAndSizeSegment") }
};
readFuncs = new Dictionary<string, MethodReference>();
}

internal static void Register(TypeReference dataType, MethodReference methodReference)
{
readFuncs[dataType.FullName] = methodReference;
}

public static MethodReference GetReadFunc(TypeReference variable, int recursionCount = 0)
{
if (readFuncs.TryGetValue(variable.FullName, out MethodReference foundFunc))
{
if (foundFunc.ReturnType.IsArray == variable.IsArray)
{
return foundFunc;
}
return foundFunc;
}

TypeDefinition td = variable.Resolve();
Expand Down
60 changes: 4 additions & 56 deletions Assets/Mirror/Editor/Weaver/Weaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ class Weaver

public static TypeReference NetworkWriterType;

public static MethodReference getComponentReference;
public static MethodReference getNetIdReference;
public static TypeReference NetworkIdentityType;
public static TypeReference IEnumeratorType;

Expand Down Expand Up @@ -103,38 +101,15 @@ class Weaver
public static TypeReference voidType;
public static TypeReference singleType;
public static TypeReference doubleType;
public static TypeReference decimalType;
public static TypeReference boolType;
public static TypeReference stringType;
public static TypeReference int64Type;
public static TypeReference uint64Type;
public static TypeReference int32Type;
public static TypeReference uint32Type;
public static TypeReference int16Type;
public static TypeReference uint16Type;
public static TypeReference byteType;
public static TypeReference sbyteType;
public static TypeReference charType;
public static TypeReference objectType;
public static TypeReference valueTypeType;
public static TypeReference vector2Type;
public static TypeReference vector3Type;
public static TypeReference vector4Type;
public static TypeReference vector2IntType;
public static TypeReference vector3IntType;
public static TypeReference colorType;
public static TypeReference color32Type;
public static TypeReference quaternionType;
public static TypeReference rectType;
public static TypeReference rayType;
public static TypeReference planeType;
public static TypeReference matrixType;
public static TypeReference guidType;
public static TypeReference typeType;
public static TypeReference gameObjectType;
public static TypeReference transformType;
public static TypeReference unityObjectType;
public static MethodReference gameObjectInequality;

public static MethodReference setSyncVarReference;
public static MethodReference setSyncVarHookGuard;
Expand Down Expand Up @@ -216,21 +191,8 @@ static bool ProcessNetworkBehaviourType(TypeDefinition td)

static void SetupUnityTypes()
{
vector2Type = UnityAssembly.MainModule.GetType("UnityEngine.Vector2");
vector3Type = UnityAssembly.MainModule.GetType("UnityEngine.Vector3");
vector4Type = UnityAssembly.MainModule.GetType("UnityEngine.Vector4");
vector2IntType = UnityAssembly.MainModule.GetType("UnityEngine.Vector2Int");
vector3IntType = UnityAssembly.MainModule.GetType("UnityEngine.Vector3Int");
colorType = UnityAssembly.MainModule.GetType("UnityEngine.Color");
color32Type = UnityAssembly.MainModule.GetType("UnityEngine.Color32");
quaternionType = UnityAssembly.MainModule.GetType("UnityEngine.Quaternion");
rectType = UnityAssembly.MainModule.GetType("UnityEngine.Rect");
planeType = UnityAssembly.MainModule.GetType("UnityEngine.Plane");
rayType = UnityAssembly.MainModule.GetType("UnityEngine.Ray");
matrixType = UnityAssembly.MainModule.GetType("UnityEngine.Matrix4x4");
gameObjectType = UnityAssembly.MainModule.GetType("UnityEngine.GameObject");
transformType = UnityAssembly.MainModule.GetType("UnityEngine.Transform");
unityObjectType = UnityAssembly.MainModule.GetType("UnityEngine.Object");

NetworkClientType = NetAssembly.MainModule.GetType("Mirror.NetworkClient");
NetworkServerType = NetAssembly.MainModule.GetType("Mirror.NetworkServer");
Expand Down Expand Up @@ -271,23 +233,14 @@ static void SetupTargetTypes()
voidType = ImportCorLibType("System.Void");
singleType = ImportCorLibType("System.Single");
doubleType = ImportCorLibType("System.Double");
decimalType = ImportCorLibType("System.Decimal");
boolType = ImportCorLibType("System.Boolean");
stringType = ImportCorLibType("System.String");
int64Type = ImportCorLibType("System.Int64");
uint64Type = ImportCorLibType("System.UInt64");
int32Type = ImportCorLibType("System.Int32");
uint32Type = ImportCorLibType("System.UInt32");
int16Type = ImportCorLibType("System.Int16");
uint16Type = ImportCorLibType("System.UInt16");
byteType = ImportCorLibType("System.Byte");
sbyteType = ImportCorLibType("System.SByte");
charType = ImportCorLibType("System.Char");
objectType = ImportCorLibType("System.Object");
valueTypeType = ImportCorLibType("System.ValueType");
typeType = ImportCorLibType("System.Type");
IEnumeratorType = ImportCorLibType("System.Collections.IEnumerator");
guidType = ImportCorLibType("System.Guid");

ArraySegmentType = ImportCorLibType("System.ArraySegment`1");
ArraySegmentArrayReference = Resolvers.ResolveProperty(ArraySegmentType, CurrentAssembly, "Array");
Expand Down Expand Up @@ -335,13 +288,6 @@ static void SetupTargetTypes()
ClientSceneType = NetAssembly.MainModule.GetType("Mirror.ClientScene");
ReadyConnectionReference = Resolvers.ResolveMethod(ClientSceneType, CurrentAssembly, "get_readyConnection");

// get specialized GetComponent<NetworkIdentity>()
getComponentReference = Resolvers.ResolveMethodGeneric(ComponentType, CurrentAssembly, "GetComponent", NetworkIdentityType);

getNetIdReference = Resolvers.ResolveMethod(networkIdentityTmp, CurrentAssembly, "get_netId");

gameObjectInequality = Resolvers.ResolveMethod(unityObjectType, CurrentAssembly, "op_Inequality");

getBehaviourIsServer = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "get_isServer");
setSyncVarReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SetSyncVar");
setSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "setSyncVarHookGuard");
Expand Down Expand Up @@ -538,8 +484,10 @@ static bool Weave(string assName, IEnumerable<string> dependencies, string unity
}

SetupTargetTypes();
Readers.Init(CurrentAssembly);
Writers.Init(CurrentAssembly);
System.Diagnostics.Stopwatch rwstopwatch = System.Diagnostics.Stopwatch.StartNew();
ReaderWriterProcessor.ProcessReadersAndWriters(CurrentAssembly);
rwstopwatch.Stop();
Console.WriteLine("Find all reader and writers took " + rwstopwatch.ElapsedMilliseconds + " milliseconds");

ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
Console.WriteLine("Script Module: {0}", moduleDefinition.Name);
Expand Down

0 comments on commit b45afad

Please sign in to comment.