mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
Weaver SyncVar processing moved into NetworkBehaviourSyncVarProcessor
This commit is contained in:
parent
181e1231d7
commit
4f5f76e42f
@ -71,6 +71,7 @@
|
|||||||
<Compile Include="NetworkBehaviourCommandProcessor.cs" />
|
<Compile Include="NetworkBehaviourCommandProcessor.cs" />
|
||||||
<Compile Include="NetworkBehaviourRpcProcessor.cs" />
|
<Compile Include="NetworkBehaviourRpcProcessor.cs" />
|
||||||
<Compile Include="NetworkBehaviourSyncEventProcessor.cs" />
|
<Compile Include="NetworkBehaviourSyncEventProcessor.cs" />
|
||||||
|
<Compile Include="NetworkBehaviourSyncVarProcessor.cs" />
|
||||||
<Compile Include="NetworkBehaviourTargetRpcProcessor.cs" />
|
<Compile Include="NetworkBehaviourTargetRpcProcessor.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="SyncListStructProcessor.cs" />
|
<Compile Include="SyncListStructProcessor.cs" />
|
||||||
|
@ -25,7 +25,6 @@ class NetworkBehaviourProcessor
|
|||||||
readonly List<MethodDefinition> m_RpcCallFuncs = new List<MethodDefinition>();
|
readonly List<MethodDefinition> m_RpcCallFuncs = new List<MethodDefinition>();
|
||||||
readonly List<MethodDefinition> m_TargetRpcCallFuncs = new List<MethodDefinition>();
|
readonly List<MethodDefinition> m_TargetRpcCallFuncs = new List<MethodDefinition>();
|
||||||
|
|
||||||
const int k_SyncVarLimit = 64; // ulong = 64 bytes
|
|
||||||
readonly TypeDefinition m_td;
|
readonly TypeDefinition m_td;
|
||||||
|
|
||||||
public NetworkBehaviourProcessor(TypeDefinition td)
|
public NetworkBehaviourProcessor(TypeDefinition td)
|
||||||
@ -44,7 +43,7 @@ public void Process()
|
|||||||
}
|
}
|
||||||
Weaver.DLog(m_td, "Process Start");
|
Weaver.DLog(m_td, "Process Start");
|
||||||
ProcessVersion();
|
ProcessVersion();
|
||||||
ProcessSyncVars();
|
NetworkBehaviourSyncVarProcessor.ProcessSyncVars(m_td, m_SyncVars, m_SyncObjects, m_SyncVarNetIds);
|
||||||
Weaver.ResetRecursionCount();
|
Weaver.ResetRecursionCount();
|
||||||
|
|
||||||
ProcessMethods();
|
ProcessMethods();
|
||||||
@ -483,50 +482,6 @@ public static int GetChannelId(CustomAttribute ca)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns false for error, not for no-hook-exists
|
|
||||||
bool CheckForHookFunction(FieldDefinition syncVar, out MethodDefinition foundMethod)
|
|
||||||
{
|
|
||||||
foundMethod = null;
|
|
||||||
foreach (var ca in syncVar.CustomAttributes)
|
|
||||||
{
|
|
||||||
if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
|
|
||||||
{
|
|
||||||
foreach (CustomAttributeNamedArgument customField in ca.Fields)
|
|
||||||
{
|
|
||||||
if (customField.Name == "hook")
|
|
||||||
{
|
|
||||||
string hookFunctionName = customField.Argument.Value as string;
|
|
||||||
|
|
||||||
foreach (var m in m_td.Methods)
|
|
||||||
{
|
|
||||||
if (m.Name == hookFunctionName)
|
|
||||||
{
|
|
||||||
if (m.Parameters.Count == 1)
|
|
||||||
{
|
|
||||||
if (m.Parameters[0].ParameterType != syncVar.FieldType)
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar Hook function " + hookFunctionName + " has wrong type signature for " + m_td.Name);
|
|
||||||
Weaver.fail = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foundMethod = m;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Log.Error("SyncVar Hook function " + hookFunctionName + " must have one argument " + m_td.Name);
|
|
||||||
Weaver.fail = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.Error("SyncVar Hook function " + hookFunctionName + " not found for " + m_td.Name);
|
|
||||||
Weaver.fail = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GenerateNetworkIntervalSetting(float interval)
|
void GenerateNetworkIntervalSetting(float interval)
|
||||||
{
|
{
|
||||||
MethodDefinition meth = new MethodDefinition("GetNetworkSendInterval", MethodAttributes.Public |
|
MethodDefinition meth = new MethodDefinition("GetNetworkSendInterval", MethodAttributes.Public |
|
||||||
@ -740,7 +695,7 @@ void GenerateDeSerialization()
|
|||||||
|
|
||||||
// check for Hook function
|
// check for Hook function
|
||||||
MethodDefinition foundMethod;
|
MethodDefinition foundMethod;
|
||||||
if (!CheckForHookFunction(syncVar, out foundMethod))
|
if (!NetworkBehaviourSyncVarProcessor.CheckForHookFunction(m_td, syncVar, out foundMethod))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1075,269 +1030,6 @@ void ProcessEvents()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodDefinition ProcessSyncVarGet(FieldDefinition fd, string originalName)
|
|
||||||
{
|
|
||||||
//Create the get method
|
|
||||||
MethodDefinition get = new MethodDefinition(
|
|
||||||
"get_Network" + originalName, MethodAttributes.Public |
|
|
||||||
MethodAttributes.SpecialName |
|
|
||||||
MethodAttributes.HideBySig,
|
|
||||||
fd.FieldType);
|
|
||||||
|
|
||||||
ILProcessor getWorker = get.Body.GetILProcessor();
|
|
||||||
|
|
||||||
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
getWorker.Append(getWorker.Create(OpCodes.Ldfld, fd));
|
|
||||||
getWorker.Append(getWorker.Create(OpCodes.Ret));
|
|
||||||
|
|
||||||
get.Body.Variables.Add(new VariableDefinition(fd.FieldType));
|
|
||||||
get.Body.InitLocals = true;
|
|
||||||
get.SemanticsAttributes = MethodSemanticsAttributes.Getter;
|
|
||||||
|
|
||||||
return get;
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodDefinition ProcessSyncVarSet(FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId)
|
|
||||||
{
|
|
||||||
//Create the set method
|
|
||||||
MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public |
|
|
||||||
MethodAttributes.SpecialName |
|
|
||||||
MethodAttributes.HideBySig,
|
|
||||||
Weaver.voidType);
|
|
||||||
|
|
||||||
ILProcessor setWorker = set.Body.GetILProcessor();
|
|
||||||
|
|
||||||
// this
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
|
|
||||||
// new value to set
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_1));
|
|
||||||
|
|
||||||
// reference to field to set
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldflda, fd));
|
|
||||||
|
|
||||||
// dirty bit
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); // 8 byte integer aka long
|
|
||||||
|
|
||||||
MethodDefinition hookFunctionMethod;
|
|
||||||
CheckForHookFunction(fd, out hookFunctionMethod);
|
|
||||||
|
|
||||||
if (hookFunctionMethod != null)
|
|
||||||
{
|
|
||||||
//if (NetworkServer.localClientActive && !syncVarHookGuard)
|
|
||||||
Instruction label = setWorker.Create(OpCodes.Nop);
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.NetworkServerGetLocalClientActive));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Brfalse, label));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.getSyncVarHookGuard));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Brtrue, label));
|
|
||||||
|
|
||||||
// syncVarHookGuard = true;
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_1));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard));
|
|
||||||
|
|
||||||
// call hook
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_1));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, hookFunctionMethod));
|
|
||||||
|
|
||||||
// syncVarHookGuard = false;
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard));
|
|
||||||
|
|
||||||
setWorker.Append(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd.FieldType.FullName == Weaver.gameObjectType.FullName)
|
|
||||||
{
|
|
||||||
// reference to netId Field to set
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId));
|
|
||||||
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarGameObjectReference));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// make generic version of SetSyncVar with field type
|
|
||||||
GenericInstanceMethod gm = new GenericInstanceMethod(Weaver.setSyncVarReference);
|
|
||||||
gm.GenericArguments.Add(fd.FieldType);
|
|
||||||
|
|
||||||
// invoke SetSyncVar
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Call, gm));
|
|
||||||
}
|
|
||||||
|
|
||||||
setWorker.Append(setWorker.Create(OpCodes.Ret));
|
|
||||||
|
|
||||||
set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType));
|
|
||||||
set.SemanticsAttributes = MethodSemanticsAttributes.Setter;
|
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessSyncVar(FieldDefinition fd, long dirtyBit)
|
|
||||||
{
|
|
||||||
string originalName = fd.Name;
|
|
||||||
|
|
||||||
Weaver.lists.replacedFields.Add(fd);
|
|
||||||
Weaver.DLog(m_td, "Sync Var " + fd.Name + " " + fd.FieldType + " " + Weaver.gameObjectType);
|
|
||||||
|
|
||||||
// GameObject SyncVars have a new field for netId
|
|
||||||
FieldDefinition netFieldId = null;
|
|
||||||
if (fd.FieldType.FullName == Weaver.gameObjectType.FullName)
|
|
||||||
{
|
|
||||||
netFieldId = new FieldDefinition("___" + fd.Name + "NetId",
|
|
||||||
FieldAttributes.Private,
|
|
||||||
Weaver.uint32Type);
|
|
||||||
|
|
||||||
m_SyncVarNetIds.Add(netFieldId);
|
|
||||||
Weaver.lists.netIdFields.Add(netFieldId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var get = ProcessSyncVarGet(fd, originalName);
|
|
||||||
var set = ProcessSyncVarSet(fd, originalName, dirtyBit, netFieldId);
|
|
||||||
|
|
||||||
//NOTE: is property even needed? Could just use a setter function?
|
|
||||||
//create the property
|
|
||||||
PropertyDefinition propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType)
|
|
||||||
{
|
|
||||||
GetMethod = get, SetMethod = set
|
|
||||||
};
|
|
||||||
|
|
||||||
//add the methods and property to the type.
|
|
||||||
m_td.Methods.Add(get);
|
|
||||||
m_td.Methods.Add(set);
|
|
||||||
m_td.Properties.Add(propertyDefinition);
|
|
||||||
Weaver.lists.replacementProperties.Add(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessSyncVars()
|
|
||||||
{
|
|
||||||
int numSyncVars = 0;
|
|
||||||
|
|
||||||
// the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties.
|
|
||||||
// start assigning syncvars at the place the base class stopped, if any
|
|
||||||
int dirtyBitCounter = Weaver.GetSyncVarStart(m_td.BaseType.FullName);
|
|
||||||
|
|
||||||
m_SyncVarNetIds.Clear();
|
|
||||||
|
|
||||||
// find syncvars
|
|
||||||
foreach (FieldDefinition fd in m_td.Fields)
|
|
||||||
{
|
|
||||||
foreach (var ca in fd.CustomAttributes)
|
|
||||||
{
|
|
||||||
if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
|
|
||||||
{
|
|
||||||
var resolvedField = fd.FieldType.Resolve();
|
|
||||||
|
|
||||||
if (Weaver.IsDerivedFrom(resolvedField, Weaver.NetworkBehaviourType))
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] cannot be derived from NetworkBehaviour.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Weaver.IsDerivedFrom(resolvedField, Weaver.ScriptableObjectType))
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] cannot be derived from ScriptableObject.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((fd.Attributes & FieldAttributes.Static) != 0)
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] cannot be static.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resolvedField.HasGenericParameters)
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] cannot have generic parameters.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resolvedField.IsInterface)
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] cannot be an interface.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fieldModuleName = resolvedField.Module.Name;
|
|
||||||
if (fieldModuleName != Weaver.scriptDef.MainModule.Name &&
|
|
||||||
fieldModuleName != Weaver.m_UnityAssemblyDefinition.MainModule.Name &&
|
|
||||||
fieldModuleName != Weaver.m_UNetAssemblyDefinition.MainModule.Name &&
|
|
||||||
fieldModuleName != Weaver.corLib.Name &&
|
|
||||||
fieldModuleName != "System.Runtime.dll" && // this is only for Metro, built-in types are not in corlib on metro
|
|
||||||
fieldModuleName != "netstandard.dll" // handle built-in types when weaving new C#7 compiler assemblies
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] from " + resolvedField.Module.ToString() + " cannot be a different module.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd.FieldType.IsArray)
|
|
||||||
{
|
|
||||||
Log.Error("SyncVar [" + fd.FullName + "] cannot be an array. Use a SyncList instead.");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Helpers.ImplementsSyncObject(fd.FieldType))
|
|
||||||
{
|
|
||||||
Log.Warning(string.Format("Script class [{0}] has [SyncVar] attribute on SyncList field {1}, SyncLists should not be marked with SyncVar.", m_td.FullName, fd.Name));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_SyncVars.Add(fd);
|
|
||||||
|
|
||||||
ProcessSyncVar(fd, 1L << dirtyBitCounter);
|
|
||||||
dirtyBitCounter += 1;
|
|
||||||
numSyncVars += 1;
|
|
||||||
|
|
||||||
if (dirtyBitCounter == k_SyncVarLimit)
|
|
||||||
{
|
|
||||||
Log.Error("Script class [" + m_td.FullName + "] has too many SyncVars (" + k_SyncVarLimit + "). (This could include base classes)");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd.FieldType.FullName.Contains("Mirror.SyncListStruct"))
|
|
||||||
{
|
|
||||||
Log.Error("SyncListStruct member variable [" + fd.FullName + "] must use a dervied class, like \"class MySyncList : SyncListStruct<MyStruct> {}\".");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Weaver.ImplementsInterface(fd.FieldType.Resolve(), Weaver.SyncObjectType))
|
|
||||||
{
|
|
||||||
if (fd.IsStatic)
|
|
||||||
{
|
|
||||||
Log.Error("SyncList [" + m_td.FullName + ":" + fd.FullName + "] cannot be a static");
|
|
||||||
Weaver.fail = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_SyncObjects.Add(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (FieldDefinition fd in m_SyncVarNetIds)
|
|
||||||
{
|
|
||||||
m_td.Fields.Add(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
Weaver.SetNumSyncVars(m_td.FullName, numSyncVars);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasMethod(string name)
|
bool HasMethod(string name)
|
||||||
{
|
{
|
||||||
foreach (var method in m_td.Methods)
|
foreach (var method in m_td.Methods)
|
||||||
|
319
Mirror/Weaver/NetworkBehaviourSyncVarProcessor.cs
Normal file
319
Mirror/Weaver/NetworkBehaviourSyncVarProcessor.cs
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
// all the [SyncVar] code from NetworkBehaviourProcessor in one place
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
|
namespace Mirror.Weaver
|
||||||
|
{
|
||||||
|
public class NetworkBehaviourSyncVarProcessor
|
||||||
|
{
|
||||||
|
const int k_SyncVarLimit = 64; // ulong = 64 bytes
|
||||||
|
|
||||||
|
// returns false for error, not for no-hook-exists
|
||||||
|
public static bool CheckForHookFunction(TypeDefinition td, FieldDefinition syncVar, out MethodDefinition foundMethod)
|
||||||
|
{
|
||||||
|
foundMethod = null;
|
||||||
|
foreach (var ca in syncVar.CustomAttributes)
|
||||||
|
{
|
||||||
|
if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
|
||||||
|
{
|
||||||
|
foreach (CustomAttributeNamedArgument customField in ca.Fields)
|
||||||
|
{
|
||||||
|
if (customField.Name == "hook")
|
||||||
|
{
|
||||||
|
string hookFunctionName = customField.Argument.Value as string;
|
||||||
|
|
||||||
|
foreach (var m in td.Methods)
|
||||||
|
{
|
||||||
|
if (m.Name == hookFunctionName)
|
||||||
|
{
|
||||||
|
if (m.Parameters.Count == 1)
|
||||||
|
{
|
||||||
|
if (m.Parameters[0].ParameterType != syncVar.FieldType)
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar Hook function " + hookFunctionName + " has wrong type signature for " + td.Name);
|
||||||
|
Weaver.fail = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foundMethod = m;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log.Error("SyncVar Hook function " + hookFunctionName + " must have one argument " + td.Name);
|
||||||
|
Weaver.fail = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.Error("SyncVar Hook function " + hookFunctionName + " not found for " + td.Name);
|
||||||
|
Weaver.fail = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodDefinition ProcessSyncVarGet(FieldDefinition fd, string originalName)
|
||||||
|
{
|
||||||
|
//Create the get method
|
||||||
|
MethodDefinition get = new MethodDefinition(
|
||||||
|
"get_Network" + originalName, MethodAttributes.Public |
|
||||||
|
MethodAttributes.SpecialName |
|
||||||
|
MethodAttributes.HideBySig,
|
||||||
|
fd.FieldType);
|
||||||
|
|
||||||
|
ILProcessor getWorker = get.Body.GetILProcessor();
|
||||||
|
|
||||||
|
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
getWorker.Append(getWorker.Create(OpCodes.Ldfld, fd));
|
||||||
|
getWorker.Append(getWorker.Create(OpCodes.Ret));
|
||||||
|
|
||||||
|
get.Body.Variables.Add(new VariableDefinition(fd.FieldType));
|
||||||
|
get.Body.InitLocals = true;
|
||||||
|
get.SemanticsAttributes = MethodSemanticsAttributes.Getter;
|
||||||
|
|
||||||
|
return get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodDefinition ProcessSyncVarSet(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId)
|
||||||
|
{
|
||||||
|
//Create the set method
|
||||||
|
MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public |
|
||||||
|
MethodAttributes.SpecialName |
|
||||||
|
MethodAttributes.HideBySig,
|
||||||
|
Weaver.voidType);
|
||||||
|
|
||||||
|
ILProcessor setWorker = set.Body.GetILProcessor();
|
||||||
|
|
||||||
|
// this
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
|
||||||
|
// new value to set
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_1));
|
||||||
|
|
||||||
|
// reference to field to set
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldflda, fd));
|
||||||
|
|
||||||
|
// dirty bit
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); // 8 byte integer aka long
|
||||||
|
|
||||||
|
MethodDefinition hookFunctionMethod;
|
||||||
|
CheckForHookFunction(td, fd, out hookFunctionMethod);
|
||||||
|
|
||||||
|
if (hookFunctionMethod != null)
|
||||||
|
{
|
||||||
|
//if (NetworkServer.localClientActive && !syncVarHookGuard)
|
||||||
|
Instruction label = setWorker.Create(OpCodes.Nop);
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.NetworkServerGetLocalClientActive));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Brfalse, label));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.getSyncVarHookGuard));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Brtrue, label));
|
||||||
|
|
||||||
|
// syncVarHookGuard = true;
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_1));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard));
|
||||||
|
|
||||||
|
// call hook
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_1));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, hookFunctionMethod));
|
||||||
|
|
||||||
|
// syncVarHookGuard = false;
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard));
|
||||||
|
|
||||||
|
setWorker.Append(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd.FieldType.FullName == Weaver.gameObjectType.FullName)
|
||||||
|
{
|
||||||
|
// reference to netId Field to set
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId));
|
||||||
|
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarGameObjectReference));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make generic version of SetSyncVar with field type
|
||||||
|
GenericInstanceMethod gm = new GenericInstanceMethod(Weaver.setSyncVarReference);
|
||||||
|
gm.GenericArguments.Add(fd.FieldType);
|
||||||
|
|
||||||
|
// invoke SetSyncVar
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Call, gm));
|
||||||
|
}
|
||||||
|
|
||||||
|
setWorker.Append(setWorker.Create(OpCodes.Ret));
|
||||||
|
|
||||||
|
set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType));
|
||||||
|
set.SemanticsAttributes = MethodSemanticsAttributes.Setter;
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, List<FieldDefinition> syncVarNetIds, long dirtyBit)
|
||||||
|
{
|
||||||
|
string originalName = fd.Name;
|
||||||
|
|
||||||
|
Weaver.lists.replacedFields.Add(fd);
|
||||||
|
Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType + " " + Weaver.gameObjectType);
|
||||||
|
|
||||||
|
// GameObject SyncVars have a new field for netId
|
||||||
|
FieldDefinition netFieldId = null;
|
||||||
|
if (fd.FieldType.FullName == Weaver.gameObjectType.FullName)
|
||||||
|
{
|
||||||
|
netFieldId = new FieldDefinition("___" + fd.Name + "NetId",
|
||||||
|
FieldAttributes.Private,
|
||||||
|
Weaver.uint32Type);
|
||||||
|
|
||||||
|
syncVarNetIds.Add(netFieldId);
|
||||||
|
Weaver.lists.netIdFields.Add(netFieldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var get = ProcessSyncVarGet(fd, originalName);
|
||||||
|
var set = ProcessSyncVarSet(td, fd, originalName, dirtyBit, netFieldId);
|
||||||
|
|
||||||
|
//NOTE: is property even needed? Could just use a setter function?
|
||||||
|
//create the property
|
||||||
|
PropertyDefinition propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType)
|
||||||
|
{
|
||||||
|
GetMethod = get, SetMethod = set
|
||||||
|
};
|
||||||
|
|
||||||
|
//add the methods and property to the type.
|
||||||
|
td.Methods.Add(get);
|
||||||
|
td.Methods.Add(set);
|
||||||
|
td.Properties.Add(propertyDefinition);
|
||||||
|
Weaver.lists.replacementProperties.Add(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ProcessSyncVars(TypeDefinition td, List<FieldDefinition> syncVars, List<FieldDefinition> syncObjects, List<FieldDefinition> syncVarNetIds)
|
||||||
|
{
|
||||||
|
int numSyncVars = 0;
|
||||||
|
|
||||||
|
// the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties.
|
||||||
|
// start assigning syncvars at the place the base class stopped, if any
|
||||||
|
int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName);
|
||||||
|
|
||||||
|
syncVarNetIds.Clear();
|
||||||
|
|
||||||
|
// find syncvars
|
||||||
|
foreach (FieldDefinition fd in td.Fields)
|
||||||
|
{
|
||||||
|
foreach (var ca in fd.CustomAttributes)
|
||||||
|
{
|
||||||
|
if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
|
||||||
|
{
|
||||||
|
var resolvedField = fd.FieldType.Resolve();
|
||||||
|
|
||||||
|
if (Weaver.IsDerivedFrom(resolvedField, Weaver.NetworkBehaviourType))
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] cannot be derived from NetworkBehaviour.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Weaver.IsDerivedFrom(resolvedField, Weaver.ScriptableObjectType))
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] cannot be derived from ScriptableObject.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fd.Attributes & FieldAttributes.Static) != 0)
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] cannot be static.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedField.HasGenericParameters)
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] cannot have generic parameters.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedField.IsInterface)
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] cannot be an interface.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldModuleName = resolvedField.Module.Name;
|
||||||
|
if (fieldModuleName != Weaver.scriptDef.MainModule.Name &&
|
||||||
|
fieldModuleName != Weaver.m_UnityAssemblyDefinition.MainModule.Name &&
|
||||||
|
fieldModuleName != Weaver.m_UNetAssemblyDefinition.MainModule.Name &&
|
||||||
|
fieldModuleName != Weaver.corLib.Name &&
|
||||||
|
fieldModuleName != "System.Runtime.dll" && // this is only for Metro, built-in types are not in corlib on metro
|
||||||
|
fieldModuleName != "netstandard.dll" // handle built-in types when weaving new C#7 compiler assemblies
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] from " + resolvedField.Module.ToString() + " cannot be a different module.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd.FieldType.IsArray)
|
||||||
|
{
|
||||||
|
Log.Error("SyncVar [" + fd.FullName + "] cannot be an array. Use a SyncList instead.");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Helpers.ImplementsSyncObject(fd.FieldType))
|
||||||
|
{
|
||||||
|
Log.Warning(string.Format("Script class [{0}] has [SyncVar] attribute on SyncList field {1}, SyncLists should not be marked with SyncVar.", td.FullName, fd.Name));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncVars.Add(fd);
|
||||||
|
|
||||||
|
ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter);
|
||||||
|
dirtyBitCounter += 1;
|
||||||
|
numSyncVars += 1;
|
||||||
|
|
||||||
|
if (dirtyBitCounter == k_SyncVarLimit)
|
||||||
|
{
|
||||||
|
Log.Error("Script class [" + td.FullName + "] has too many SyncVars (" + k_SyncVarLimit + "). (This could include base classes)");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd.FieldType.FullName.Contains("Mirror.SyncListStruct"))
|
||||||
|
{
|
||||||
|
Log.Error("SyncListStruct member variable [" + fd.FullName + "] must use a dervied class, like \"class MySyncList : SyncListStruct<MyStruct> {}\".");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Weaver.ImplementsInterface(fd.FieldType.Resolve(), Weaver.SyncObjectType))
|
||||||
|
{
|
||||||
|
if (fd.IsStatic)
|
||||||
|
{
|
||||||
|
Log.Error("SyncList [" + td.FullName + ":" + fd.FullName + "] cannot be a static");
|
||||||
|
Weaver.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncObjects.Add(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (FieldDefinition fd in syncVarNetIds)
|
||||||
|
{
|
||||||
|
td.Fields.Add(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Weaver.SetNumSyncVars(td.FullName, numSyncVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user