-
-
Notifications
You must be signed in to change notification settings - Fork 65
/
SyncObjectProcessor.cs
155 lines (131 loc) · 6.48 KB
/
SyncObjectProcessor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;
using System.Collections.Generic;
using Mono.CecilX;
using Mono.CecilX.Cil;
namespace Mirror.Weaver
{
public static class SyncObjectProcessor
{
/// <summary>
/// Finds SyncObjects fields in a type
/// <para>Type should be a NetworkBehaviour</para>
/// </summary>
/// <param name="td"></param>
/// <returns></returns>
public static List<FieldDefinition> FindSyncObjectsFields(TypeDefinition td)
{
List<FieldDefinition> syncObjects = new List<FieldDefinition>();
foreach (FieldDefinition fd in td.Fields)
{
if (fd.FieldType.Resolve().ImplementsInterface<SyncObject>())
{
if (fd.IsStatic)
{
Weaver.Error($"{fd.Name} cannot be static", fd);
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;
}
MethodDefinition 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;
}
MethodDefinition 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;
}
}
}