Skip to content

Commit

Permalink
feat: You can use synclists directly (#366)
Browse files Browse the repository at this point in the history
you had to write a class that extended synclist to make the weaver happy.
Now the weaver does not need to generate code for synclists anymore,
so you can use synclists as \<insert your deity\> intended.

## before
```cs
class MyBehavior: NetworkBehaviour {
    // nonsense to make the weaver happy
    class SyncListMyType : SyncList<MyType> {}

    public readonly SyncListMyType mylist = new SyncListMyType();
}
```
## after
```cs
class MyBehavior: NetworkBehaviour {
    public readonly SyncList<MyType> mylist = new SyncList<MyType>();
}
```

BREAKING CHANGE: SyncList and other syncobjects no longer have override methods to serialize and deserialize data
  • Loading branch information
paulpach committed Sep 30, 2020
1 parent 103a131 commit ca18d11
Show file tree
Hide file tree
Showing 48 changed files with 84 additions and 1,133 deletions.
56 changes: 0 additions & 56 deletions Assets/Mirror/Editor/Weaver/Processors/GenericArgumentResolver.cs

This file was deleted.

This file was deleted.

28 changes: 0 additions & 28 deletions Assets/Mirror/Editor/Weaver/Processors/SyncDictionaryProcessor.cs

This file was deleted.

This file was deleted.

29 changes: 0 additions & 29 deletions Assets/Mirror/Editor/Weaver/Processors/SyncListProcessor.cs

This file was deleted.

11 changes: 0 additions & 11 deletions Assets/Mirror/Editor/Weaver/Processors/SyncListProcessor.cs.meta

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static void GenerateSyncObjectInitializer(ILProcessor worker, FieldDefini
GenerateSyncObjectRegistration(worker, fd);
}

// generates 'syncListInt = new SyncListInt()' if user didn't do that yet
// generates 'SyncList<int> = new SyncList<int>()' if user didn't do that yet
static void GenerateSyncObjectInstanceInitializer(ILProcessor worker, FieldDefinition fd)
{
// check the ctor's instructions for an Stfld op-code for this specific sync list field.
Expand All @@ -27,14 +27,14 @@ static void GenerateSyncObjectInstanceInitializer(ILProcessor worker, FieldDefin
if (field.DeclaringType == fd.DeclaringType && field.Name == fd.Name)
{
// Already initialized by the user in the field definition, e.g:
// public SyncListInt Foo = new SyncListInt();
// public SyncList<int> Foo = new SyncList<int>();
return;
}
}
}

// Not initialized by the user in the field definition, e.g:
// public SyncListInt Foo;
// public SyncList<int> Foo;

TypeDefinition fieldType = fd.FieldType.Resolve();
// find ctor with no parameters
Expand Down
120 changes: 0 additions & 120 deletions Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Mirror.Weaver
{
Expand All @@ -27,129 +25,11 @@ public static List<FieldDefinition> FindSyncObjectsFields(TypeDefinition td)
continue;
}

if (fd.FieldType.Resolve().HasGenericParameters)
{
Weaver.Error($"Cannot use generic SyncObject {fd.Name} directly in NetworkBehaviour. Create a class and inherit from the generic SyncObject instead", fd);
continue;
}

syncObjects.Add(fd);
}
}


return syncObjects;
}

/// <summary>
/// Generates the serialization and deserialization methods for a specified generic argument
/// </summary>
/// <param name="td">The type of the class that needs serialization methods</param>
/// <param name="itemType">generic argument to serialize</param>
/// <param name="mirrorBaseType">the base SyncObject td inherits from</param>
/// <param name="serializeMethod">The name of the serialize method</param>
/// <param name="deserializeMethod">The name of the deserialize method</param>
public static void GenerateSerialization(TypeDefinition td, TypeReference itemType, Type mirrorBaseType, string serializeMethod, string deserializeMethod)
{
Weaver.DLog(td, "SyncObjectProcessor Start item:" + itemType.FullName);

bool success = GenerateSerialization(serializeMethod, td, itemType, mirrorBaseType);
if (Weaver.WeavingFailed)
{
return;
}

success |= GenerateDeserialization(deserializeMethod, td, itemType, mirrorBaseType);

if (success)
Weaver.DLog(td, "SyncObjectProcessor Done");
}

// serialization of individual element
static bool GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType, Type mirrorBaseType)
{
Weaver.DLog(td, " GenerateSerialization");
bool existing = td.HasMethodInBaseType(methodName, mirrorBaseType);
if (existing)
return true;


// this check needs to happen inside GenerateSerialization because
// we need to check if user has made custom function above
if (itemType.IsGenericInstance)
{
Weaver.Error($"Can not create Serialize or Deserialize for generic element in {td.Name}. Override virtual methods with custom Serialize and Deserialize to use {itemType} in SyncList", td);
return false;
}

var serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Public |
MethodAttributes.HideBySig,
WeaverTypes.Import(typeof(void)));

serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, WeaverTypes.Import<NetworkWriter>()));
serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType));
ILProcessor worker = serializeFunc.Body.GetILProcessor();

MethodReference writeFunc = Writers.GetWriteFunc(itemType);
if (writeFunc != null)
{
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Ldarg_2));
worker.Append(worker.Create(OpCodes.Call, writeFunc));
}
else
{
Weaver.Error($"{td.Name} has sync object generic type {itemType.Name}. Use a type supported by mirror instead", td);
return false;
}
worker.Append(worker.Create(OpCodes.Ret));

td.Methods.Add(serializeFunc);
return true;
}

static bool GenerateDeserialization(string methodName, TypeDefinition td, TypeReference itemType, Type mirrorBaseType)
{
Weaver.DLog(td, " GenerateDeserialization");
bool existing = td.HasMethodInBaseType(methodName, mirrorBaseType);
if (existing)
return true;

// this check needs to happen inside GenerateDeserialization because
// we need to check if user has made custom function above
if (itemType.IsGenericInstance)
{
Weaver.Error($"Can not create Serialize or Deserialize for generic element in {td.Name}. Override virtual methods with custom Serialize and Deserialize to use {itemType.Name} in SyncList", td);
return false;
}

var deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Public |
MethodAttributes.HideBySig,
itemType);

deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, WeaverTypes.Import<NetworkReader>()));

ILProcessor worker = deserializeFunction.Body.GetILProcessor();

MethodReference readerFunc = Readers.GetReadFunc(itemType);
if (readerFunc != null)
{
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Call, readerFunc));
worker.Append(worker.Create(OpCodes.Ret));
}
else
{
Weaver.Error($"{td.Name} has sync object generic type {itemType.Name}. Use a type supported by mirror instead", td);
return false;
}

td.Methods.Add(deserializeFunction);
return true;
}
}
}
54 changes: 0 additions & 54 deletions Assets/Mirror/Editor/Weaver/Weaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,67 +126,13 @@ static bool WeaveNetworkBehavior(TypeDefinition td)
return modified;
}

static bool WeaveSyncObject(TypeDefinition td)
{
bool modified = false;

// ignore generic classes
// we can not process generic classes
// we give error if a generic syncObject is used in NetworkBehaviour
if (td.HasGenericParameters)
return false;

// ignore abstract classes
// we dont need to process abstract classes because classes that
// inherit from them will be processed instead

// We cant early return with non classes or Abstract classes
// because we still need to check for embeded types
if (td.IsClass || !td.IsAbstract)
{
if (td.IsDerivedFrom(typeof(SyncList<>)))
{
SyncListProcessor.Process(td, typeof(SyncList<>));
modified = true;
}
else if (td.IsDerivedFrom(typeof(SyncSet<>)))
{
SyncListProcessor.Process(td, typeof(SyncSet<>));
modified = true;
}
else if (td.IsDerivedFrom(typeof(SyncDictionary<,>)))
{
SyncDictionaryProcessor.Process(td);
modified = true;
}
}

// check for embedded types
foreach (TypeDefinition embedded in td.NestedTypes)
{
modified |= WeaveSyncObject(embedded);
}

return modified;
}

static bool WeaveModule(ModuleDefinition moduleDefinition)
{
try
{
bool modified = false;

// We need to do 2 passes, because SyncListStructs might be referenced from other modules, so we must make sure we generate them first.
var watch = System.Diagnostics.Stopwatch.StartNew();
foreach (TypeDefinition td in moduleDefinition.Types)
{
if (td.IsClass && td.BaseType.CanBeResolved())
{
modified |= WeaveSyncObject(td);
}
}
watch.Stop();
Console.WriteLine("Weave sync objects took " + watch.ElapsedMilliseconds + " milliseconds");

watch.Start();
foreach (TypeDefinition td in moduleDefinition.Types)
Expand Down

0 comments on commit ca18d11

Please sign in to comment.