mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
feat: Allow generic NetworkBehaviour<T> subclasses (#3073)
* feat: Allow generic NetworkBehaviour subclasses
It's only generic SyncVars (via attribute) and rpcs/cmds we don't want to deal with and that aren't supported.
Even generic SyncVar<T> works
* Generate IL2CPP compatible base calls
see cf91e1d547
* Make SyncVar field/hook references generic too
Fixes bad IL
* Update Extensions.cs
* Update Assets/Mirror/Editor/Weaver/Extensions.cs
Co-authored-by: vis2k <info@noobtuts.com>
This commit is contained in:
parent
7670271bf1
commit
d67dc74bbd
@ -140,6 +140,18 @@ public static MethodReference MakeHostInstanceGeneric(this MethodReference self,
|
|||||||
return module.ImportReference(reference);
|
return module.ImportReference(reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// needed for NetworkBehaviour<T> support
|
||||||
|
// https://github.com/vis2k/Mirror/pull/3073/
|
||||||
|
public static FieldReference MakeHostInstanceGeneric(this FieldReference self)
|
||||||
|
{
|
||||||
|
var declaringType = new GenericInstanceType(self.DeclaringType);
|
||||||
|
foreach (var parameter in self.DeclaringType.GenericParameters)
|
||||||
|
{
|
||||||
|
declaringType.GenericArguments.Add(parameter);
|
||||||
|
}
|
||||||
|
return new FieldReference(self.Name, self.FieldType, declaringType);
|
||||||
|
}
|
||||||
|
|
||||||
// Given a field of a generic class such as Writer<T>.write,
|
// Given a field of a generic class such as Writer<T>.write,
|
||||||
// and a generic instance such as ArraySegment`int
|
// and a generic instance such as ArraySegment`int
|
||||||
// Creates a reference to the specialized method ArraySegment`int`.get_Count
|
// Creates a reference to the specialized method ArraySegment`int`.get_Count
|
||||||
@ -251,5 +263,75 @@ public static AssemblyNameReference FindReference(this ModuleDefinition module,
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Takes generic arguments from child class and applies them to parent reference, if possible
|
||||||
|
// eg makes `Base<T>` in Child<int> : Base<int> have `int` instead of `T`
|
||||||
|
// Originally by James-Frowen under MIT
|
||||||
|
// https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45
|
||||||
|
public static TypeReference ApplyGenericParameters(this TypeReference parentReference,
|
||||||
|
TypeReference childReference)
|
||||||
|
{
|
||||||
|
// If the parent is not generic, we got nothing to apply
|
||||||
|
if (!parentReference.IsGenericInstance)
|
||||||
|
return parentReference;
|
||||||
|
|
||||||
|
GenericInstanceType parentGeneric = (GenericInstanceType)parentReference;
|
||||||
|
// make new type so we can replace the args on it
|
||||||
|
// resolve it so we have non-generic instance (eg just instance with <T> instead of <int>)
|
||||||
|
// if we don't cecil will make it double generic (eg INVALID IL)
|
||||||
|
GenericInstanceType generic = new GenericInstanceType(parentReference.Resolve());
|
||||||
|
foreach (TypeReference arg in parentGeneric.GenericArguments)
|
||||||
|
generic.GenericArguments.Add(arg);
|
||||||
|
|
||||||
|
for (int i = 0; i < generic.GenericArguments.Count; i++)
|
||||||
|
{
|
||||||
|
// if arg is not generic
|
||||||
|
// eg List<int> would be int so not generic.
|
||||||
|
// But List<T> would be T so is generic
|
||||||
|
if (!generic.GenericArguments[i].IsGenericParameter)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// get the generic name, eg T
|
||||||
|
string name = generic.GenericArguments[i].Name;
|
||||||
|
// find what type T is, eg turn it into `int` if `List<int>`
|
||||||
|
TypeReference arg = FindMatchingGenericArgument(childReference, name);
|
||||||
|
|
||||||
|
// import just to be safe
|
||||||
|
TypeReference imported = parentReference.Module.ImportReference(arg);
|
||||||
|
// set arg on generic, parent ref will be Base<int> instead of just Base<T>
|
||||||
|
generic.GenericArguments[i] = imported;
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the type reference for a generic parameter with the provided name in the child reference
|
||||||
|
// Originally by James-Frowen under MIT
|
||||||
|
// https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45
|
||||||
|
static TypeReference FindMatchingGenericArgument(TypeReference childReference, string paramName)
|
||||||
|
{
|
||||||
|
TypeDefinition def = childReference.Resolve();
|
||||||
|
// child class must be generic if we are in this part of the code
|
||||||
|
// eg Child<T> : Base<T> <--- child must have generic if Base has T
|
||||||
|
// vs Child : Base<int> <--- wont be here if Base has int (we check if T exists before calling this)
|
||||||
|
if (!def.HasGenericParameters)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Base class had generic parameters, but could not find them in child class");
|
||||||
|
|
||||||
|
// go through parameters in child class, and find the generic that matches the name
|
||||||
|
for (int i = 0; i < def.GenericParameters.Count; i++)
|
||||||
|
{
|
||||||
|
GenericParameter param = def.GenericParameters[i];
|
||||||
|
if (param.Name == paramName)
|
||||||
|
{
|
||||||
|
GenericInstanceType generic = (GenericInstanceType)childReference;
|
||||||
|
// return generic arg with same index
|
||||||
|
return generic.GenericArguments[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should never happen, if it does it means that this code is bugged
|
||||||
|
throw new InvalidOperationException("Did not find matching generic");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,14 +68,6 @@ public bool Process(ref bool WeavingFailed)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (netBehaviourSubclass.HasGenericParameters)
|
|
||||||
{
|
|
||||||
Log.Error($"{netBehaviourSubclass.Name} cannot have generic parameters", netBehaviourSubclass);
|
|
||||||
WeavingFailed = true;
|
|
||||||
// originally Process returned true in every case, except if already processed.
|
|
||||||
// maybe return false here in the future.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
MarkAsProcessed(netBehaviourSubclass);
|
MarkAsProcessed(netBehaviourSubclass);
|
||||||
|
|
||||||
// deconstruct tuple and set fields
|
// deconstruct tuple and set fields
|
||||||
@ -437,8 +429,13 @@ void GenerateSerialization(ref bool WeavingFailed)
|
|||||||
worker.Emit(OpCodes.Ldarg_2);
|
worker.Emit(OpCodes.Ldarg_2);
|
||||||
worker.Emit(OpCodes.Brfalse, initialStateLabel);
|
worker.Emit(OpCodes.Brfalse, initialStateLabel);
|
||||||
|
|
||||||
foreach (FieldDefinition syncVar in syncVars)
|
foreach (FieldDefinition syncVarDef in syncVars)
|
||||||
{
|
{
|
||||||
|
FieldReference syncVar = syncVarDef;
|
||||||
|
if (netBehaviourSubclass.HasGenericParameters)
|
||||||
|
{
|
||||||
|
syncVar = syncVarDef.MakeHostInstanceGeneric();
|
||||||
|
}
|
||||||
// Generates a writer call for each sync variable
|
// Generates a writer call for each sync variable
|
||||||
// writer
|
// writer
|
||||||
worker.Emit(OpCodes.Ldarg_1);
|
worker.Emit(OpCodes.Ldarg_1);
|
||||||
@ -481,8 +478,14 @@ void GenerateSerialization(ref bool WeavingFailed)
|
|||||||
|
|
||||||
// start at number of syncvars in parent
|
// start at number of syncvars in parent
|
||||||
int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName);
|
int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName);
|
||||||
foreach (FieldDefinition syncVar in syncVars)
|
foreach (FieldDefinition syncVarDef in syncVars)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
FieldReference syncVar = syncVarDef;
|
||||||
|
if (netBehaviourSubclass.HasGenericParameters)
|
||||||
|
{
|
||||||
|
syncVar = syncVarDef.MakeHostInstanceGeneric();
|
||||||
|
}
|
||||||
Instruction varLabel = worker.Create(OpCodes.Nop);
|
Instruction varLabel = worker.Create(OpCodes.Nop);
|
||||||
|
|
||||||
// Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL)
|
// Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL)
|
||||||
@ -539,7 +542,15 @@ void DeserializeField(FieldDefinition syncVar, ILProcessor worker, ref bool Weav
|
|||||||
|
|
||||||
// push 'ref T this.field'
|
// push 'ref T this.field'
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, syncVar);
|
// if the netbehaviour class is generic, we need to make the field reference generic as well for correct IL
|
||||||
|
if (netBehaviourSubclass.HasGenericParameters)
|
||||||
|
{
|
||||||
|
worker.Emit(OpCodes.Ldflda, syncVar.MakeHostInstanceGeneric());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
worker.Emit(OpCodes.Ldflda, syncVar);
|
||||||
|
}
|
||||||
|
|
||||||
// hook? then push 'new Action<T,T>(Hook)' onto stack
|
// hook? then push 'new Action<T,T>(Hook)' onto stack
|
||||||
MethodDefinition hookMethod = syncVarAttributeProcessor.GetHookMethod(netBehaviourSubclass, syncVar, ref WeavingFailed);
|
MethodDefinition hookMethod = syncVarAttributeProcessor.GetHookMethod(netBehaviourSubclass, syncVar, ref WeavingFailed);
|
||||||
@ -821,6 +832,14 @@ bool ValidateParameters(MethodReference method, RemoteCallType callType, ref boo
|
|||||||
// validate parameters for a remote function call like Rpc/Cmd
|
// validate parameters for a remote function call like Rpc/Cmd
|
||||||
bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam, ref bool WeavingFailed)
|
bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam, ref bool WeavingFailed)
|
||||||
{
|
{
|
||||||
|
// need to check this before any type lookups since those will fail since generic types don't resolve
|
||||||
|
if (param.ParameterType.IsGenericParameter)
|
||||||
|
{
|
||||||
|
Log.Error($"{method.Name} cannot have generic parameters", method);
|
||||||
|
WeavingFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool isNetworkConnection = param.ParameterType.Is<NetworkConnection>();
|
bool isNetworkConnection = param.ParameterType.Is<NetworkConnection>();
|
||||||
bool isSenderConnection = IsSenderConnection(param, callType);
|
bool isSenderConnection = IsSenderConnection(param, callType);
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ public static List<FieldDefinition> FindSyncObjectsFields(Writers writers, Reade
|
|||||||
|
|
||||||
foreach (FieldDefinition fd in td.Fields)
|
foreach (FieldDefinition fd in td.Fields)
|
||||||
{
|
{
|
||||||
|
if (fd.FieldType.IsGenericParameter)
|
||||||
|
{
|
||||||
|
// can't call .Resolve on generic ones
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (fd.FieldType.Resolve().IsDerivedFrom<SyncObject>())
|
if (fd.FieldType.Resolve().IsDerivedFrom<SyncObject>())
|
||||||
{
|
{
|
||||||
if (fd.IsStatic)
|
if (fd.IsStatic)
|
||||||
|
@ -69,17 +69,28 @@ public void GenerateNewActionFromHookMethod(FieldDefinition syncVar, ILProcessor
|
|||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MethodReference hookMethodReference;
|
||||||
|
// if the network behaviour class is generic, we need to make the method reference generic for correct IL
|
||||||
|
if (hookMethod.DeclaringType.HasGenericParameters)
|
||||||
|
{
|
||||||
|
hookMethodReference = hookMethod.MakeHostInstanceGeneric(hookMethod.Module, hookMethod.DeclaringType.MakeGenericInstanceType(hookMethod.DeclaringType.GenericParameters.ToArray()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hookMethodReference = hookMethod;
|
||||||
|
}
|
||||||
|
|
||||||
// we support regular and virtual hook functions.
|
// we support regular and virtual hook functions.
|
||||||
if (hookMethod.IsVirtual)
|
if (hookMethod.IsVirtual)
|
||||||
{
|
{
|
||||||
// for virtual / overwritten hooks, we need different IL.
|
// for virtual / overwritten hooks, we need different IL.
|
||||||
// this is from simply testing Action = VirtualHook; in C#.
|
// this is from simply testing Action = VirtualHook; in C#.
|
||||||
worker.Emit(OpCodes.Dup);
|
worker.Emit(OpCodes.Dup);
|
||||||
worker.Emit(OpCodes.Ldvirtftn, hookMethod);
|
worker.Emit(OpCodes.Ldvirtftn, hookMethodReference);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
worker.Emit(OpCodes.Ldftn, hookMethod);
|
worker.Emit(OpCodes.Ldftn, hookMethodReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
// call 'new Action<T,T>()' constructor to convert the function to an action
|
// call 'new Action<T,T>()' constructor to convert the function to an action
|
||||||
@ -143,6 +154,29 @@ public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string origina
|
|||||||
|
|
||||||
ILProcessor worker = get.Body.GetILProcessor();
|
ILProcessor worker = get.Body.GetILProcessor();
|
||||||
|
|
||||||
|
FieldReference fr;
|
||||||
|
if (fd.DeclaringType.HasGenericParameters)
|
||||||
|
{
|
||||||
|
fr = fd.MakeHostInstanceGeneric();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fr = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldReference netIdFieldReference = null;
|
||||||
|
if (netFieldId != null)
|
||||||
|
{
|
||||||
|
if (netFieldId.DeclaringType.HasGenericParameters)
|
||||||
|
{
|
||||||
|
netIdFieldReference = netFieldId.MakeHostInstanceGeneric();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
netIdFieldReference = netFieldId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// [SyncVar] GameObject?
|
// [SyncVar] GameObject?
|
||||||
if (fd.FieldType.Is<UnityEngine.GameObject>())
|
if (fd.FieldType.Is<UnityEngine.GameObject>())
|
||||||
{
|
{
|
||||||
@ -150,9 +184,9 @@ public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string origina
|
|||||||
// this.
|
// this.
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldfld, netFieldId);
|
worker.Emit(OpCodes.Ldfld, netIdFieldReference);
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, fd);
|
worker.Emit(OpCodes.Ldflda, fr);
|
||||||
worker.Emit(OpCodes.Call, weaverTypes.getSyncVarGameObjectReference);
|
worker.Emit(OpCodes.Call, weaverTypes.getSyncVarGameObjectReference);
|
||||||
worker.Emit(OpCodes.Ret);
|
worker.Emit(OpCodes.Ret);
|
||||||
}
|
}
|
||||||
@ -163,9 +197,9 @@ public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string origina
|
|||||||
// this.
|
// this.
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldfld, netFieldId);
|
worker.Emit(OpCodes.Ldfld, netIdFieldReference);
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, fd);
|
worker.Emit(OpCodes.Ldflda, fr);
|
||||||
worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference);
|
worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference);
|
||||||
worker.Emit(OpCodes.Ret);
|
worker.Emit(OpCodes.Ret);
|
||||||
}
|
}
|
||||||
@ -175,9 +209,9 @@ public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string origina
|
|||||||
// this.
|
// this.
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldfld, netFieldId);
|
worker.Emit(OpCodes.Ldfld, netIdFieldReference);
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, fd);
|
worker.Emit(OpCodes.Ldflda, fr);
|
||||||
MethodReference getFunc = weaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType);
|
MethodReference getFunc = weaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType);
|
||||||
worker.Emit(OpCodes.Call, getFunc);
|
worker.Emit(OpCodes.Call, getFunc);
|
||||||
worker.Emit(OpCodes.Ret);
|
worker.Emit(OpCodes.Ret);
|
||||||
@ -186,7 +220,7 @@ public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string origina
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldfld, fd);
|
worker.Emit(OpCodes.Ldfld, fr);
|
||||||
worker.Emit(OpCodes.Ret);
|
worker.Emit(OpCodes.Ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +249,28 @@ public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition
|
|||||||
weaverTypes.Import(typeof(void)));
|
weaverTypes.Import(typeof(void)));
|
||||||
|
|
||||||
ILProcessor worker = set.Body.GetILProcessor();
|
ILProcessor worker = set.Body.GetILProcessor();
|
||||||
|
FieldReference fr;
|
||||||
|
if (fd.DeclaringType.HasGenericParameters)
|
||||||
|
{
|
||||||
|
fr = fd.MakeHostInstanceGeneric();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fr = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldReference netIdFieldReference = null;
|
||||||
|
if (netFieldId != null)
|
||||||
|
{
|
||||||
|
if (netFieldId.DeclaringType.HasGenericParameters)
|
||||||
|
{
|
||||||
|
netIdFieldReference = netFieldId.MakeHostInstanceGeneric();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
netIdFieldReference = netFieldId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (!SyncVarEqual(value, ref playerData))
|
// if (!SyncVarEqual(value, ref playerData))
|
||||||
Instruction endOfMethod = worker.Create(OpCodes.Nop);
|
Instruction endOfMethod = worker.Create(OpCodes.Nop);
|
||||||
@ -241,7 +297,7 @@ public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition
|
|||||||
|
|
||||||
// push 'ref T this.field'
|
// push 'ref T this.field'
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, fd);
|
worker.Emit(OpCodes.Ldflda, fr);
|
||||||
|
|
||||||
// push the dirty bit for this SyncVar
|
// push the dirty bit for this SyncVar
|
||||||
worker.Emit(OpCodes.Ldc_I8, dirtyBit);
|
worker.Emit(OpCodes.Ldc_I8, dirtyBit);
|
||||||
@ -265,14 +321,14 @@ public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition
|
|||||||
{
|
{
|
||||||
// GameObject setter needs one more parameter: netId field ref
|
// GameObject setter needs one more parameter: netId field ref
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, netFieldId);
|
worker.Emit(OpCodes.Ldflda, netIdFieldReference);
|
||||||
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_GameObject);
|
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_GameObject);
|
||||||
}
|
}
|
||||||
else if (fd.FieldType.Is<NetworkIdentity>())
|
else if (fd.FieldType.Is<NetworkIdentity>())
|
||||||
{
|
{
|
||||||
// NetworkIdentity setter needs one more parameter: netId field ref
|
// NetworkIdentity setter needs one more parameter: netId field ref
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, netFieldId);
|
worker.Emit(OpCodes.Ldflda, netIdFieldReference);
|
||||||
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_NetworkIdentity);
|
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_NetworkIdentity);
|
||||||
}
|
}
|
||||||
// TODO this only uses the persistent netId for types DERIVED FROM NB.
|
// TODO this only uses the persistent netId for types DERIVED FROM NB.
|
||||||
@ -283,7 +339,7 @@ public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition
|
|||||||
// NetworkIdentity setter needs one more parameter: netId field ref
|
// NetworkIdentity setter needs one more parameter: netId field ref
|
||||||
// (actually its a NetworkBehaviourSyncVar type)
|
// (actually its a NetworkBehaviourSyncVar type)
|
||||||
worker.Emit(OpCodes.Ldarg_0);
|
worker.Emit(OpCodes.Ldarg_0);
|
||||||
worker.Emit(OpCodes.Ldflda, netFieldId);
|
worker.Emit(OpCodes.Ldflda, netIdFieldReference);
|
||||||
// make generic version of GeneratedSyncVarSetter_NetworkBehaviour<T>
|
// make generic version of GeneratedSyncVarSetter_NetworkBehaviour<T>
|
||||||
MethodReference getFunc = weaverTypes.generatedSyncVarSetter_NetworkBehaviour_T.MakeGeneric(assembly.MainModule, fd.FieldType);
|
MethodReference getFunc = weaverTypes.generatedSyncVarSetter_NetworkBehaviour_T.MakeGeneric(assembly.MainModule, fd.FieldType);
|
||||||
worker.Emit(OpCodes.Call, getFunc);
|
worker.Emit(OpCodes.Call, getFunc);
|
||||||
@ -315,16 +371,18 @@ public void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary<Fie
|
|||||||
if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>())
|
if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>())
|
||||||
{
|
{
|
||||||
netIdField = new FieldDefinition($"___{fd.Name}NetId",
|
netIdField = new FieldDefinition($"___{fd.Name}NetId",
|
||||||
FieldAttributes.Private,
|
FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed
|
||||||
weaverTypes.Import<NetworkBehaviour.NetworkBehaviourSyncVar>());
|
weaverTypes.Import<NetworkBehaviour.NetworkBehaviourSyncVar>());
|
||||||
|
netIdField.DeclaringType = td;
|
||||||
|
|
||||||
syncVarNetIds[fd] = netIdField;
|
syncVarNetIds[fd] = netIdField;
|
||||||
}
|
}
|
||||||
else if (fd.FieldType.IsNetworkIdentityField())
|
else if (fd.FieldType.IsNetworkIdentityField())
|
||||||
{
|
{
|
||||||
netIdField = new FieldDefinition($"___{fd.Name}NetId",
|
netIdField = new FieldDefinition($"___{fd.Name}NetId",
|
||||||
FieldAttributes.Private,
|
FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed
|
||||||
weaverTypes.Import<uint>());
|
weaverTypes.Import<uint>());
|
||||||
|
netIdField.DeclaringType = td;
|
||||||
|
|
||||||
syncVarNetIds[fd] = netIdField;
|
syncVarNetIds[fd] = netIdField;
|
||||||
}
|
}
|
||||||
@ -377,6 +435,13 @@ public void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary<Fie
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fd.FieldType.IsGenericParameter)
|
||||||
|
{
|
||||||
|
Log.Error($"{fd.Name} has generic type. Generic SyncVars are not supported", fd);
|
||||||
|
WeavingFailed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (fd.FieldType.IsArray)
|
if (fd.FieldType.IsArray)
|
||||||
{
|
{
|
||||||
Log.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd);
|
Log.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd);
|
||||||
|
@ -48,16 +48,21 @@ public static MethodReference TryResolveMethodInParents(TypeReference tr, Assemb
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
foreach (MethodDefinition methodRef in tr.Resolve().Methods)
|
foreach (MethodDefinition methodDef in tr.Resolve().Methods)
|
||||||
{
|
{
|
||||||
if (methodRef.Name == name)
|
if (methodDef.Name == name)
|
||||||
{
|
{
|
||||||
|
MethodReference methodRef = methodDef;
|
||||||
|
if (tr.IsGenericInstance)
|
||||||
|
{
|
||||||
|
methodRef = methodRef.MakeHostInstanceGeneric(tr.Module, (GenericInstanceType)tr);
|
||||||
|
}
|
||||||
return assembly.MainModule.ImportReference(methodRef);
|
return assembly.MainModule.ImportReference(methodRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Could not find the method in this class, try the parent
|
// Could not find the method in this class, try the parent
|
||||||
return TryResolveMethodInParents(tr.Resolve().BaseType, assembly, name);
|
return TryResolveMethodInParents(tr.Resolve().BaseType.ApplyGenericParameters(tr), assembly, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable)
|
public static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable)
|
||||||
|
@ -5,10 +5,17 @@ namespace Mirror.Weaver.Tests
|
|||||||
public class WeaverNetworkBehaviourTests : WeaverTestsBuildFromTestName
|
public class WeaverNetworkBehaviourTests : WeaverTestsBuildFromTestName
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void NetworkBehaviourGeneric()
|
public void NetworkBehaviourGenericSyncVar()
|
||||||
{
|
{
|
||||||
HasError("NetworkBehaviourGeneric`1 cannot have generic parameters",
|
HasError("genericSyncVarNotAllowed has generic type. Generic SyncVars are not supported",
|
||||||
"WeaverNetworkBehaviourTests.NetworkBehaviourGeneric.NetworkBehaviourGeneric`1");
|
"T WeaverNetworkBehaviourTests.NetworkBehaviourGeneric.NetworkBehaviourGeneric`1::genericSyncVarNotAllowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NetworkBehaviourGenericRpc()
|
||||||
|
{
|
||||||
|
HasError("RpcGeneric cannot have generic parameters",
|
||||||
|
"System.Void WeaverNetworkBehaviourTests.NetworkBehaviourGeneric.NetworkBehaviourGeneric`1::RpcGeneric(T)");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace WeaverNetworkBehaviourTests.NetworkBehaviourGeneric
|
||||||
|
{
|
||||||
|
class NetworkBehaviourGeneric<T> : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public T param;
|
||||||
|
public readonly SyncVar<T> syncVar = new SyncVar<T>(default);
|
||||||
|
public readonly SyncList<T> syncList = new SyncList<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GenericImplInt : NetworkBehaviourGeneric<int>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b279c2f37eaa4de09c1f15a5b80029b4
|
||||||
|
timeCreated: 1643560588
|
@ -4,6 +4,7 @@ namespace WeaverNetworkBehaviourTests.NetworkBehaviourGeneric
|
|||||||
{
|
{
|
||||||
class NetworkBehaviourGeneric<T> : NetworkBehaviour
|
class NetworkBehaviourGeneric<T> : NetworkBehaviour
|
||||||
{
|
{
|
||||||
T genericsNotAllowed;
|
[ClientRpc]
|
||||||
|
void RpcGeneric(T param) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace WeaverNetworkBehaviourTests.NetworkBehaviourGeneric
|
||||||
|
{
|
||||||
|
class NetworkBehaviourGeneric<T> : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[SyncVar]
|
||||||
|
T genericSyncVarNotAllowed;
|
||||||
|
|
||||||
|
T genericTypeIsFine;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user