update weaver

This commit is contained in:
JohnCorby 2023-04-26 15:06:17 -07:00
parent 2ca7046480
commit 613a2704b7
11 changed files with 182 additions and 91 deletions

View File

@ -12,8 +12,18 @@ namespace Mirror.Weaver
? td.GetElementType().FullName == type.FullName ? td.GetElementType().FullName == type.FullName
: td.FullName == type.FullName; : td.FullName == type.FullName;
// check if 'td' is exactly of type T.
// it does not check if any base type is of <T>, only the specific type.
// for example:
// NetworkConnection Is NetworkConnection: true
// NetworkConnectionToClient Is NetworkConnection: false
public static bool Is<T>(this TypeReference td) => Is(td, typeof(T)); public static bool Is<T>(this TypeReference td) => Is(td, typeof(T));
// check if 'tr' is derived from T.
// it does not check if 'tr' is exactly T.
// for example:
// NetworkConnection IsDerivedFrom<NetworkConnection>: false
// NetworkConnectionToClient IsDerivedFrom<NetworkConnection>: true
public static bool IsDerivedFrom<T>(this TypeReference tr) => IsDerivedFrom(tr, typeof(T)); public static bool IsDerivedFrom<T>(this TypeReference tr) => IsDerivedFrom(tr, typeof(T));
public static bool IsDerivedFrom(this TypeReference tr, Type baseClass) public static bool IsDerivedFrom(this TypeReference tr, Type baseClass)
@ -79,7 +89,10 @@ namespace Mirror.Weaver
public static bool IsNetworkIdentityField(this TypeReference tr) => public static bool IsNetworkIdentityField(this TypeReference tr) =>
tr.Is<UnityEngine.GameObject>() || tr.Is<UnityEngine.GameObject>() ||
tr.Is<NetworkIdentity>() || tr.Is<NetworkIdentity>() ||
tr.IsDerivedFrom<NetworkBehaviour>(); // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
tr.IsDerivedFrom<NetworkBehaviour>() ||
tr.Is<NetworkBehaviour>();
public static bool CanBeResolved(this TypeReference parent) public static bool CanBeResolved(this TypeReference parent)
{ {
@ -266,7 +279,7 @@ namespace Mirror.Weaver
// Takes generic arguments from child class and applies them to parent reference, if possible // 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` // eg makes `Base<T>` in Child<int> : Base<int> have `int` instead of `T`
// Originally by James-Frowen under MIT // Originally by James-Frowen under MIT
// https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45 // https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45
public static TypeReference ApplyGenericParameters(this TypeReference parentReference, public static TypeReference ApplyGenericParameters(this TypeReference parentReference,
TypeReference childReference) TypeReference childReference)
@ -306,7 +319,7 @@ namespace Mirror.Weaver
} }
// Finds the type reference for a generic parameter with the provided name in the child reference // Finds the type reference for a generic parameter with the provided name in the child reference
// Originally by James-Frowen under MIT // Originally by James-Frowen under MIT
// https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45 // https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45
static TypeReference FindMatchingGenericArgument(TypeReference childReference, string paramName) static TypeReference FindMatchingGenericArgument(TypeReference childReference, string paramName)
{ {

View File

@ -10,10 +10,11 @@ namespace Mirror.Weaver
// generates code like: // generates code like:
public void CmdThrust(float thrusting, int spin) public void CmdThrust(float thrusting, int spin)
{ {
NetworkWriter networkWriter = new NetworkWriter(); NetworkWriterPooled writer = NetworkWriterPool.Get();
networkWriter.Write(thrusting); writer.Write(thrusting);
networkWriter.WritePackedUInt32((uint)spin); writer.WritePackedUInt32((uint)spin);
base.SendCommandInternal(cmdName, networkWriter, channel); base.SendCommandInternal(cmdName, cmdHash, writer, channel);
NetworkWriterPool.Return(writer);
} }
public void CallCmdThrust(float thrusting, int spin) public void CallCmdThrust(float thrusting, int spin)
@ -38,7 +39,7 @@ namespace Mirror.Weaver
NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes);
// NetworkWriter writer = new NetworkWriter(); // NetworkWriter writer = new NetworkWriter();
NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes);
// write all the arguments that the user passed to the Cmd call // write all the arguments that the user passed to the Cmd call
if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed)) if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed))
@ -52,6 +53,11 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
worker.Emit(OpCodes.Ldstr, md.FullName); worker.Emit(OpCodes.Ldstr, md.FullName);
// pass the function hash so we don't have to compute it at runtime
// otherwise each GetStableHash call requires O(N) complexity.
// noticeable for long function names:
// https://github.com/MirrorNetworking/Mirror/issues/3375
worker.Emit(OpCodes.Ldc_I4, md.FullName.GetStableHashCode());
// writer // writer
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ldc_I4, channel); worker.Emit(OpCodes.Ldc_I4, channel);
@ -59,7 +65,7 @@ namespace Mirror.Weaver
worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal); worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal);
NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
return cmd; return cmd;

View File

@ -137,21 +137,21 @@ namespace Mirror.Weaver
public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes) public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes)
{ {
worker.Body.InitLocals = true; worker.Body.InitLocals = true;
worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import<PooledNetworkWriter>())); worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import<NetworkWriterPooled>()));
} }
public static void WriteCreateWriter(ILProcessor worker, WeaverTypes weaverTypes) public static void WriteGetWriter(ILProcessor worker, WeaverTypes weaverTypes)
{ {
// create writer // create writer
worker.Emit(OpCodes.Call, weaverTypes.GetPooledWriterReference); worker.Emit(OpCodes.Call, weaverTypes.GetWriterReference);
worker.Emit(OpCodes.Stloc_0); worker.Emit(OpCodes.Stloc_0);
} }
public static void WriteRecycleWriter(ILProcessor worker, WeaverTypes weaverTypes) public static void WriteReturnWriter(ILProcessor worker, WeaverTypes weaverTypes)
{ {
// NetworkWriterPool.Recycle(writer); // NetworkWriterPool.Recycle(writer);
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Call, weaverTypes.RecycleWriterReference); worker.Emit(OpCodes.Call, weaverTypes.ReturnWriterReference);
} }
public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed)
@ -397,7 +397,7 @@ namespace Mirror.Weaver
MethodDefinition serialize = new MethodDefinition(SerializeMethodName, MethodDefinition serialize = new MethodDefinition(SerializeMethodName,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
weaverTypes.Import<bool>()); weaverTypes.Import(typeof(void)));
serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import<NetworkWriter>())); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import<NetworkWriter>()));
serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import<bool>())); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import<bool>()));
@ -405,10 +405,7 @@ namespace Mirror.Weaver
serialize.Body.InitLocals = true; serialize.Body.InitLocals = true;
// loc_0, this local variable is to determine if any variable was dirty // base.SerializeSyncVars(writer, forceAll);
VariableDefinition dirtyLocal = new VariableDefinition(weaverTypes.Import<bool>());
serialize.Body.Variables.Add(dirtyLocal);
MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName);
if (baseSerialize != null) if (baseSerialize != null)
{ {
@ -419,16 +416,20 @@ namespace Mirror.Weaver
// forceAll // forceAll
worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Ldarg_2);
worker.Emit(OpCodes.Call, baseSerialize); worker.Emit(OpCodes.Call, baseSerialize);
// set dirtyLocal to result of base.OnSerialize()
worker.Emit(OpCodes.Stloc_0);
} }
// Generates: if (forceAll); // Generates:
// if (forceAll)
// {
// writer.WriteInt(health);
// ...
// }
Instruction initialStateLabel = worker.Create(OpCodes.Nop); Instruction initialStateLabel = worker.Create(OpCodes.Nop);
// forceAll // forceAll
worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Ldarg_2); // load 'forceAll' flag
worker.Emit(OpCodes.Brfalse, initialStateLabel); worker.Emit(OpCodes.Brfalse, initialStateLabel); // start the 'if forceAll' branch
// generates write.Write(syncVar) for each SyncVar in forceAll case
foreach (FieldDefinition syncVarDef in syncVars) foreach (FieldDefinition syncVarDef in syncVars)
{ {
FieldReference syncVar = syncVarDef; FieldReference syncVar = syncVarDef;
@ -442,7 +443,21 @@ namespace Mirror.Weaver
// this // this
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
worker.Emit(OpCodes.Ldfld, syncVar); worker.Emit(OpCodes.Ldfld, syncVar);
MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); MethodReference writeFunc;
// For NBs we always need to use the default NetworkBehaviour write func
// since the reader counter part uses that exact layout which is not easy to change
// without introducing more edge cases
// effectively this disallows custom NB-type writers/readers on SyncVars
// see: https://github.com/MirrorNetworking/Mirror/issues/2680
if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>())
{
writeFunc = writers.GetWriteFunc(weaverTypes.Import<NetworkBehaviour>(), ref WeavingFailed);
}
else
{
writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed);
}
if (writeFunc != null) if (writeFunc != null)
{ {
worker.Emit(OpCodes.Call, writeFunc); worker.Emit(OpCodes.Call, writeFunc);
@ -455,15 +470,14 @@ namespace Mirror.Weaver
} }
} }
// always return true if forceAll // if (forceAll) then always return at the end of the 'if' case
// Generates: return true
worker.Emit(OpCodes.Ldc_I4_1);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
// Generates: end if (forceAll); // end the 'if' case for "if (forceAll)"
worker.Append(initialStateLabel); worker.Append(initialStateLabel);
////////////////////////////////////////////////////////////////////
// write dirty bits before the data fields // write dirty bits before the data fields
// Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ());
// writer // writer
@ -480,7 +494,6 @@ namespace Mirror.Weaver
int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName);
foreach (FieldDefinition syncVarDef in syncVars) foreach (FieldDefinition syncVarDef in syncVars)
{ {
FieldReference syncVar = syncVarDef; FieldReference syncVar = syncVarDef;
if (netBehaviourSubclass.HasGenericParameters) if (netBehaviourSubclass.HasGenericParameters)
{ {
@ -504,7 +517,21 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
worker.Emit(OpCodes.Ldfld, syncVar); worker.Emit(OpCodes.Ldfld, syncVar);
MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); MethodReference writeFunc;
// For NBs we always need to use the default NetworkBehaviour write func
// since the reader counter part uses that exact layout which is not easy to change
// without introducing more edge cases
// effectively this disallows custom NB-type writers/readers on SyncVars
// see: https://github.com/MirrorNetworking/Mirror/issues/2680
if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>())
{
writeFunc = writers.GetWriteFunc(weaverTypes.Import<NetworkBehaviour>(), ref WeavingFailed);
}
else
{
writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed);
}
if (writeFunc != null) if (writeFunc != null)
{ {
worker.Emit(OpCodes.Call, writeFunc); worker.Emit(OpCodes.Call, writeFunc);
@ -516,11 +543,6 @@ namespace Mirror.Weaver
return; return;
} }
// something was dirty
worker.Emit(OpCodes.Ldc_I4_1);
// set dirtyLocal to true
worker.Emit(OpCodes.Stloc_0);
worker.Append(varLabel); worker.Append(varLabel);
dirtyBit += 1; dirtyBit += 1;
} }
@ -529,8 +551,7 @@ namespace Mirror.Weaver
//worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}");
//worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference);
// generate: return dirtyLocal // generate: return
worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
netBehaviourSubclass.Methods.Add(serialize); netBehaviourSubclass.Methods.Add(serialize);
} }
@ -589,10 +610,9 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldflda, netIdField); worker.Emit(OpCodes.Ldflda, netIdField);
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_NetworkIdentity); worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_NetworkIdentity);
} }
// TODO this only uses the persistent netId for types DERIVED FROM NB. // handle both NetworkBehaviour and inheritors.
// not if the type is just 'NetworkBehaviour'. // fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
// this is what original implementation did too. fix it after. else if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>() || syncVar.FieldType.Is<NetworkBehaviour>())
else if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>())
{ {
// reader // reader
worker.Emit(OpCodes.Ldarg_1); worker.Emit(OpCodes.Ldarg_1);

View File

@ -68,7 +68,7 @@ namespace Mirror.Weaver
//worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}"); //worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}");
//worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference);
NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes);
// write all the arguments that the user passed to the Rpc call // write all the arguments that the user passed to the Rpc call
if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed)) if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed))
@ -82,6 +82,11 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
worker.Emit(OpCodes.Ldstr, md.FullName); worker.Emit(OpCodes.Ldstr, md.FullName);
// pass the function hash so we don't have to compute it at runtime
// otherwise each GetStableHash call requires O(N) complexity.
// noticeable for long function names:
// https://github.com/MirrorNetworking/Mirror/issues/3375
worker.Emit(OpCodes.Ldc_I4, md.FullName.GetStableHashCode());
// writer // writer
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ldc_I4, channel); worker.Emit(OpCodes.Ldc_I4, channel);
@ -89,7 +94,7 @@ namespace Mirror.Weaver
worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal); worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal);
NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);

View File

@ -16,7 +16,7 @@ namespace Mirror.Weaver
foreach (FieldDefinition fd in td.Fields) foreach (FieldDefinition fd in td.Fields)
{ {
if (fd.FieldType.IsGenericParameter) if (fd.FieldType.IsGenericParameter || fd.ContainsGenericParameter)
{ {
// can't call .Resolve on generic ones // can't call .Resolve on generic ones
continue; continue;

View File

@ -203,7 +203,9 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference); worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
} }
else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>() || fd.FieldType.Is<NetworkBehaviour>())
{ {
// return this.GetSyncVarNetworkBehaviour<T>(ref field, uint netId); // return this.GetSyncVarNetworkBehaviour<T>(ref field, uint netId);
// this. // this.
@ -331,10 +333,9 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldflda, netIdFieldReference); 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. // handle both NetworkBehaviour and inheritors.
// not if the type is just 'NetworkBehaviour'. // fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
// this is what original implementation did too. fix it after. else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>() || fd.FieldType.Is<NetworkBehaviour>())
else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>())
{ {
// 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)
@ -368,11 +369,13 @@ namespace Mirror.Weaver
// GameObject/NetworkIdentity SyncVars have a new field for netId // GameObject/NetworkIdentity SyncVars have a new field for netId
FieldDefinition netIdField = null; FieldDefinition netIdField = null;
// NetworkBehaviour has different field type than other NetworkIdentityFields // NetworkBehaviour has different field type than other NetworkIdentityFields
if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>() || fd.FieldType.Is<NetworkBehaviour>())
{ {
netIdField = new FieldDefinition($"___{fd.Name}NetId", netIdField = new FieldDefinition($"___{fd.Name}NetId",
FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed
weaverTypes.Import<NetworkBehaviour.NetworkBehaviourSyncVar>()); weaverTypes.Import<NetworkBehaviourSyncVar>());
netIdField.DeclaringType = td; netIdField.DeclaringType = td;
syncVarNetIds[fd] = netIdField; syncVarNetIds[fd] = netIdField;
@ -475,7 +478,11 @@ namespace Mirror.Weaver
{ {
td.Fields.Add(fd); td.Fields.Add(fd);
} }
syncVarAccessLists.SetNumSyncVars(td.FullName, syncVars.Count);
// include parent class syncvars
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3457
int parentSyncVarCount = syncVarAccessLists.GetSyncVarStart(td.BaseType.FullName);
syncVarAccessLists.SetNumSyncVars(td.FullName, parentSyncVarCount + syncVars.Count);
return (syncVars, syncVarNetIds); return (syncVars, syncVarNetIds);
} }

View File

@ -9,8 +9,16 @@ namespace Mirror.Weaver
// helper functions to check if the method has a NetworkConnection parameter // helper functions to check if the method has a NetworkConnection parameter
public static bool HasNetworkConnectionParameter(MethodDefinition md) public static bool HasNetworkConnectionParameter(MethodDefinition md)
{ {
return md.Parameters.Count > 0 && if (md.Parameters.Count > 0)
md.Parameters[0].ParameterType.Is<NetworkConnection>(); {
// we need to allow both NetworkConnection, and inheriting types.
// NetworkBehaviour.SendTargetRpc takes a NetworkConnection parameter.
// fixes https://github.com/vis2k/Mirror/issues/3290
TypeReference type = md.Parameters[0].ParameterType;
return type.Is<NetworkConnection>() ||
type.IsDerivedFrom<NetworkConnection>();
}
return false;
} }
public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed)
@ -34,16 +42,25 @@ namespace Mirror.Weaver
// NetworkConnection parameter is optional // NetworkConnection parameter is optional
if (HasNetworkConnectionParameter(md)) if (HasNetworkConnectionParameter(md))
{ {
// on server, the NetworkConnection parameter is a connection to client. // TargetRpcs are sent from server to client.
// when the rpc is invoked on the client, it still has the same // on server, we currently support two types:
// function signature. we pass in the connection to server, // TargetRpc(NetworkConnection)
// which is cleaner than just passing null) // TargetRpc(NetworkConnectionToClient)
//NetworkClient.readyconnection // however, it's always a connection to client.
// in the future, only NetworkConnectionToClient will be supported.
// explicit typing helps catch issues at compile time.
// //
// TODO // on client, InvokeTargetRpc calls the original code.
// a) .connectionToServer = best solution. no doubt. // we need to fill in the NetworkConnection parameter.
// b) NetworkClient.connection for now. add TODO to not use static later. // NetworkClient.connection is always a connection to server.
worker.Emit(OpCodes.Call, weaverTypes.NetworkClientConnectionReference); //
// we used to pass NetworkClient.connection as the TargetRpc parameter.
// which caused: https://github.com/MirrorNetworking/Mirror/issues/3455
// when the parameter is defined as a NetworkConnectionToClient.
//
// a client's connection never fits into a NetworkConnectionToClient.
// we need to always pass null here.
worker.Emit(OpCodes.Ldnull);
} }
// process reader parameters and skip first one if first one is NetworkConnection // process reader parameters and skip first one if first one is NetworkConnection
@ -100,7 +117,7 @@ namespace Mirror.Weaver
NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes);
NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes);
// write all the arguments that the user passed to the TargetRpc call // write all the arguments that the user passed to the TargetRpc call
// (skip first one if first one is NetworkConnection) // (skip first one if first one is NetworkConnection)
@ -122,12 +139,17 @@ namespace Mirror.Weaver
} }
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
worker.Emit(OpCodes.Ldstr, md.FullName); worker.Emit(OpCodes.Ldstr, md.FullName);
// pass the function hash so we don't have to compute it at runtime
// otherwise each GetStableHash call requires O(N) complexity.
// noticeable for long function names:
// https://github.com/MirrorNetworking/Mirror/issues/3375
worker.Emit(OpCodes.Ldc_I4, md.FullName.GetStableHashCode());
// writer // writer
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0)); worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0));
worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal); worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal);
NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);

View File

@ -19,6 +19,7 @@ namespace Mirror.Weaver
AssemblyDefinition assembly; AssemblyDefinition assembly;
WeaverTypes weaverTypes; WeaverTypes weaverTypes;
TypeDefinition GeneratedCodeClass; TypeDefinition GeneratedCodeClass;
// CHANGED
internal Logger Log; internal Logger Log;
Dictionary<TypeReference, MethodReference> readFuncs = Dictionary<TypeReference, MethodReference> readFuncs =
@ -114,7 +115,9 @@ namespace Mirror.Weaver
return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed); return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed);
} }
else if (variableReference.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
else if (variableReference.IsDerivedFrom<NetworkBehaviour>() || variableReference.Is<NetworkBehaviour>())
{ {
return GetNetworkBehaviourReader(variableReference); return GetNetworkBehaviourReader(variableReference);
} }
@ -138,6 +141,7 @@ namespace Mirror.Weaver
WeavingFailed = true; WeavingFailed = true;
return null; return null;
} }
// CHANGED
/* /*
if (variableDefinition.HasGenericParameters) if (variableDefinition.HasGenericParameters)
{ {
@ -270,6 +274,7 @@ namespace Mirror.Weaver
GenerateNullCheck(worker, ref WeavingFailed); GenerateNullCheck(worker, ref WeavingFailed);
CreateNew(variable, worker, td, ref WeavingFailed); CreateNew(variable, worker, td, ref WeavingFailed);
// CHANGED
this.ReadAllFieldsGeneric(variable, worker, ref WeavingFailed); this.ReadAllFieldsGeneric(variable, worker, ref WeavingFailed);
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);

View File

@ -25,6 +25,12 @@ namespace Mirror.Weaver
AssemblyDefinition CurrentAssembly; AssemblyDefinition CurrentAssembly;
Writers writers; Writers writers;
Readers readers; Readers readers;
// in case of weaver errors, we don't stop immediately.
// we log all errors and then eventually return false if
// weaving has failed.
// this way the user can fix multiple errors at once, instead of having
// to fix -> recompile -> fix -> recompile for one error at a time.
bool WeavingFailed; bool WeavingFailed;
// logger functions can be set from the outside. // logger functions can be set from the outside.
@ -200,6 +206,7 @@ namespace Mirror.Weaver
ModuleDefinition moduleDefinition = CurrentAssembly.MainModule; ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
Console.WriteLine($"Script Module: {moduleDefinition.Name}"); Console.WriteLine($"Script Module: {moduleDefinition.Name}");
// CHANGED
QSBReaderWriterProcessor.Process(moduleDefinition, writers, readers, ref WeavingFailed); QSBReaderWriterProcessor.Process(moduleDefinition, writers, readers, ref WeavingFailed);
modified |= WeaveModule(moduleDefinition); modified |= WeaveModule(moduleDefinition);

View File

@ -11,8 +11,8 @@ namespace Mirror.Weaver
public MethodReference ScriptableObjectCreateInstanceMethod; public MethodReference ScriptableObjectCreateInstanceMethod;
public MethodReference NetworkBehaviourDirtyBitsReference; public MethodReference NetworkBehaviourDirtyBitsReference;
public MethodReference GetPooledWriterReference; public MethodReference GetWriterReference;
public MethodReference RecycleWriterReference; public MethodReference ReturnWriterReference;
public MethodReference NetworkClientConnectionReference; public MethodReference NetworkClientConnectionReference;
@ -77,28 +77,14 @@ namespace Mirror.Weaver
TypeReference NetworkServerType = Import(typeof(NetworkServer)); TypeReference NetworkServerType = Import(typeof(NetworkServer));
NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed); NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed);
TypeReference NetworkClientType = Import(typeof(NetworkClient)); TypeReference NetworkClientType = Import(typeof(NetworkClient));
NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed); NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed);
NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed);
TypeReference RemoteCallDelegateType = Import<RemoteCalls.RemoteCallDelegate>();
RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed);
TypeReference NetworkBehaviourType = Import<NetworkBehaviour>(); TypeReference NetworkBehaviourType = Import<NetworkBehaviour>();
TypeReference RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls));
TypeReference ScriptableObjectType = Import<ScriptableObject>();
ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod(
ScriptableObjectType, assembly, Log,
md => md.Name == "CreateInstance" && md.HasGenericParameters,
ref WeavingFailed);
NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits"); NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits");
TypeReference NetworkWriterPoolType = Import(typeof(NetworkWriterPool));
GetPooledWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "GetWriter", ref WeavingFailed);
RecycleWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Recycle", ref WeavingFailed);
NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed);
generatedSyncVarSetter = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter", ref WeavingFailed); generatedSyncVarSetter = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter", ref WeavingFailed);
generatedSyncVarSetter_GameObject = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_GameObject", ref WeavingFailed); generatedSyncVarSetter_GameObject = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_GameObject", ref WeavingFailed);
@ -114,9 +100,25 @@ namespace Mirror.Weaver
getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed); getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed);
getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed); getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed);
sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed);
sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed);
sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed);
InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed);
TypeReference RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls));
registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed); registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed);
registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed); registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed);
TypeReference RemoteCallDelegateType = Import<RemoteCalls.RemoteCallDelegate>();
RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed);
TypeReference ScriptableObjectType = Import<ScriptableObject>();
ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod(
ScriptableObjectType, assembly, Log,
md => md.Name == "CreateInstance" && md.HasGenericParameters,
ref WeavingFailed);
TypeReference unityDebug = Import(typeof(UnityEngine.Debug)); TypeReference unityDebug = Import(typeof(UnityEngine.Debug));
// these have multiple methods with same name, so need to check parameters too // these have multiple methods with same name, so need to check parameters too
logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md =>
@ -133,11 +135,10 @@ namespace Mirror.Weaver
TypeReference typeType = Import(typeof(Type)); TypeReference typeType = Import(typeof(Type));
getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed); getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed);
sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed);
sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed);
sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed);
InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed); TypeReference NetworkWriterPoolType = Import(typeof(NetworkWriterPool));
GetWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Get", ref WeavingFailed);
ReturnWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Return", ref WeavingFailed);
TypeReference readerExtensions = Import(typeof(NetworkReaderExtensions)); TypeReference readerExtensions = Import(typeof(NetworkReaderExtensions));
readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md => readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md =>
@ -147,6 +148,7 @@ namespace Mirror.Weaver
}), }),
ref WeavingFailed); ref WeavingFailed);
// CHANGED
/* /*
// [InitializeOnLoadMethod] // [InitializeOnLoadMethod]
// 'UnityEditor' is not available in builds. // 'UnityEditor' is not available in builds.

View File

@ -116,7 +116,9 @@ namespace Mirror.Weaver
return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed); return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed);
} }
if (variableReference.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
if (variableReference.IsDerivedFrom<NetworkBehaviour>() || variableReference.Is<NetworkBehaviour>())
{ {
return GetNetworkBehaviourWriter(variableReference); return GetNetworkBehaviourWriter(variableReference);
} }
@ -139,6 +141,7 @@ namespace Mirror.Weaver
{ {
throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
} }
// CHANGED
/* /*
if (variableDefinition.HasGenericParameters) if (variableDefinition.HasGenericParameters)
{ {
@ -219,6 +222,7 @@ namespace Mirror.Weaver
if (!variable.Resolve().IsValueType) if (!variable.Resolve().IsValueType)
WriteNullCheck(worker, ref WeavingFailed); WriteNullCheck(worker, ref WeavingFailed);
// CHANGED
if (!this.WriteAllFieldsGeneric(variable, worker, ref WeavingFailed)) if (!this.WriteAllFieldsGeneric(variable, worker, ref WeavingFailed))
return null; return null;