1
Fork 0
mirror of https://github.com/Steffo99/better-tee.git synced 2024-11-22 23:34:18 +00:00
better-tee/Assets/Packages/Mirror/Editor/Weaver/Weaver.cs
2019-09-17 17:43:32 +02:00

598 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Mono.CecilX;
using Mono.CecilX.Cil;
namespace Mirror.Weaver
{
// This data is flushed each time - if we are run multiple times in the same process/domain
class WeaverLists
{
// setter functions that replace [SyncVar] member variable references. dict<field, replacement>
public Dictionary<FieldDefinition, MethodDefinition> replacementSetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
// getter functions that replace [SyncVar] member variable references. dict<field, replacement>
public Dictionary<FieldDefinition, MethodDefinition> replacementGetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
// [Command]/[ClientRpc] functions that should be replaced. dict<originalMethodFullName, replacement>
public Dictionary<string, MethodDefinition> replaceMethods = new Dictionary<string, MethodDefinition>();
// [SyncEvent] invoke functions that should be replaced. dict<originalEventName, replacement>
public Dictionary<string, MethodDefinition> replaceEvents = new Dictionary<string, MethodDefinition>();
public List<MethodDefinition> generatedReadFunctions = new List<MethodDefinition>();
public List<MethodDefinition> generatedWriteFunctions = new List<MethodDefinition>();
public TypeDefinition generateContainerClass;
// amount of SyncVars per class. dict<className, amount>
public Dictionary<string, int> numSyncVars = new Dictionary<string, int>();
}
class Weaver
{
public static WeaverLists WeaveLists { get; private set; }
public static AssemblyDefinition CurrentAssembly { get; private set; }
public static ModuleDefinition CorLibModule { get; private set; }
public static AssemblyDefinition UnityAssembly { get; private set; }
public static AssemblyDefinition NetAssembly { get; private set; }
public static bool WeavingFailed { get; private set; }
public static bool GenerateLogErrors { get; set; }
// private properties
static readonly bool DebugLogEnabled = true;
// Network types
public static TypeReference NetworkBehaviourType;
public static TypeReference NetworkBehaviourType2;
public static TypeReference MonoBehaviourType;
public static TypeReference ScriptableObjectType;
public static TypeReference NetworkConnectionType;
public static TypeReference MessageBaseType;
public static TypeReference SyncListType;
public static TypeReference SyncSetType;
public static TypeReference SyncDictionaryType;
public static MethodReference NetworkBehaviourDirtyBitsReference;
public static MethodReference GetPooledWriterReference;
public static MethodReference RecycleWriterReference;
public static TypeReference NetworkClientType;
public static TypeReference NetworkServerType;
public static TypeReference NetworkReaderType;
public static TypeReference NetworkWriterType;
public static TypeReference NetworkIdentityType;
public static TypeReference IEnumeratorType;
public static TypeReference ClientSceneType;
public static MethodReference ReadyConnectionReference;
public static TypeReference ComponentType;
public static TypeReference CmdDelegateReference;
public static MethodReference CmdDelegateConstructor;
public static MethodReference NetworkServerGetActive;
public static MethodReference NetworkServerGetLocalClientActive;
public static MethodReference NetworkClientGetActive;
public static MethodReference getBehaviourIsServer;
// custom attribute types
public static TypeReference SyncVarType;
public static TypeReference CommandType;
public static TypeReference ClientRpcType;
public static TypeReference TargetRpcType;
public static TypeReference SyncEventType;
public static TypeReference SyncObjectType;
public static MethodReference InitSyncObjectReference;
// array segment
public static TypeReference ArraySegmentType;
public static MethodReference ArraySegmentConstructorReference;
public static MethodReference ArraySegmentArrayReference;
public static MethodReference ArraySegmentOffsetReference;
public static MethodReference ArraySegmentCountReference;
// system types
public static TypeReference voidType;
public static TypeReference singleType;
public static TypeReference doubleType;
public static TypeReference boolType;
public static TypeReference int64Type;
public static TypeReference uint64Type;
public static TypeReference int32Type;
public static TypeReference uint32Type;
public static TypeReference objectType;
public static TypeReference typeType;
public static TypeReference gameObjectType;
public static TypeReference transformType;
public static MethodReference setSyncVarReference;
public static MethodReference setSyncVarHookGuard;
public static MethodReference getSyncVarHookGuard;
public static MethodReference setSyncVarGameObjectReference;
public static MethodReference getSyncVarGameObjectReference;
public static MethodReference setSyncVarNetworkIdentityReference;
public static MethodReference getSyncVarNetworkIdentityReference;
public static MethodReference registerCommandDelegateReference;
public static MethodReference registerRpcDelegateReference;
public static MethodReference registerEventDelegateReference;
public static MethodReference getTypeReference;
public static MethodReference getTypeFromHandleReference;
public static MethodReference logErrorReference;
public static MethodReference logWarningReference;
public static MethodReference sendCommandInternal;
public static MethodReference sendRpcInternal;
public static MethodReference sendTargetRpcInternal;
public static MethodReference sendEventInternal;
public static void DLog(TypeDefinition td, string fmt, params object[] args)
{
if (!DebugLogEnabled)
return;
Console.WriteLine("[" + td.Name + "] " + string.Format(fmt, args));
}
// display weaver error
// and mark process as failed
public static void Error(string message)
{
Log.Error(message);
WeavingFailed = true;
}
public static int GetSyncVarStart(string className)
{
return WeaveLists.numSyncVars.ContainsKey(className)
? WeaveLists.numSyncVars[className]
: 0;
}
public static void SetNumSyncVars(string className, int num)
{
WeaveLists.numSyncVars[className] = num;
}
internal static void ConfirmGeneratedCodeClass()
{
if (WeaveLists.generateContainerClass == null)
{
WeaveLists.generateContainerClass = new TypeDefinition("Mirror", "GeneratedNetworkCode",
TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass,
objectType);
const MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName;
MethodDefinition method = new MethodDefinition(".ctor", methodAttributes, voidType);
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, Resolvers.ResolveMethod(objectType, CurrentAssembly, ".ctor")));
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
WeaveLists.generateContainerClass.Methods.Add(method);
}
}
static bool ProcessNetworkBehaviourType(TypeDefinition td)
{
if (!NetworkBehaviourProcessor.WasProcessed(td))
{
DLog(td, "Found NetworkBehaviour " + td.FullName);
NetworkBehaviourProcessor proc = new NetworkBehaviourProcessor(td);
proc.Process();
return true;
}
return false;
}
static void SetupUnityTypes()
{
gameObjectType = UnityAssembly.MainModule.GetType("UnityEngine.GameObject");
transformType = UnityAssembly.MainModule.GetType("UnityEngine.Transform");
NetworkClientType = NetAssembly.MainModule.GetType("Mirror.NetworkClient");
NetworkServerType = NetAssembly.MainModule.GetType("Mirror.NetworkServer");
SyncVarType = NetAssembly.MainModule.GetType("Mirror.SyncVarAttribute");
CommandType = NetAssembly.MainModule.GetType("Mirror.CommandAttribute");
ClientRpcType = NetAssembly.MainModule.GetType("Mirror.ClientRpcAttribute");
TargetRpcType = NetAssembly.MainModule.GetType("Mirror.TargetRpcAttribute");
SyncEventType = NetAssembly.MainModule.GetType("Mirror.SyncEventAttribute");
SyncObjectType = NetAssembly.MainModule.GetType("Mirror.SyncObject");
}
static void SetupCorLib()
{
AssemblyNameReference name = AssemblyNameReference.Parse("mscorlib");
ReaderParameters parameters = new ReaderParameters
{
AssemblyResolver = CurrentAssembly.MainModule.AssemblyResolver
};
CorLibModule = CurrentAssembly.MainModule.AssemblyResolver.Resolve(name, parameters).MainModule;
}
static TypeReference ImportCorLibType(string fullName)
{
TypeDefinition type = CorLibModule.GetType(fullName) ?? CorLibModule.ExportedTypes.First(t => t.FullName == fullName).Resolve();
if (type != null)
{
return CurrentAssembly.MainModule.ImportReference(type);
}
Error("Failed to import mscorlib type: " + fullName + " because Resolve failed. (Might happen when trying to Resolve in NetStandard dll, see also: https://github.com/vis2k/Mirror/issues/791)");
return null;
}
static void SetupTargetTypes()
{
// system types
SetupCorLib();
voidType = ImportCorLibType("System.Void");
singleType = ImportCorLibType("System.Single");
doubleType = ImportCorLibType("System.Double");
boolType = ImportCorLibType("System.Boolean");
int64Type = ImportCorLibType("System.Int64");
uint64Type = ImportCorLibType("System.UInt64");
int32Type = ImportCorLibType("System.Int32");
uint32Type = ImportCorLibType("System.UInt32");
objectType = ImportCorLibType("System.Object");
typeType = ImportCorLibType("System.Type");
IEnumeratorType = ImportCorLibType("System.Collections.IEnumerator");
ArraySegmentType = ImportCorLibType("System.ArraySegment`1");
ArraySegmentArrayReference = Resolvers.ResolveProperty(ArraySegmentType, CurrentAssembly, "Array");
ArraySegmentCountReference = Resolvers.ResolveProperty(ArraySegmentType, CurrentAssembly, "Count");
ArraySegmentOffsetReference = Resolvers.ResolveProperty(ArraySegmentType, CurrentAssembly, "Offset");
ArraySegmentConstructorReference = Resolvers.ResolveMethod(ArraySegmentType, CurrentAssembly, ".ctor");
NetworkReaderType = NetAssembly.MainModule.GetType("Mirror.NetworkReader");
NetworkWriterType = NetAssembly.MainModule.GetType("Mirror.NetworkWriter");
NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, CurrentAssembly, "get_active");
NetworkServerGetLocalClientActive = Resolvers.ResolveMethod(NetworkServerType, CurrentAssembly, "get_localClientActive");
NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, CurrentAssembly, "get_active");
CmdDelegateReference = NetAssembly.MainModule.GetType("Mirror.NetworkBehaviour/CmdDelegate");
CmdDelegateConstructor = Resolvers.ResolveMethod(CmdDelegateReference, CurrentAssembly, ".ctor");
CurrentAssembly.MainModule.ImportReference(gameObjectType);
CurrentAssembly.MainModule.ImportReference(transformType);
TypeReference networkIdentityTmp = NetAssembly.MainModule.GetType("Mirror.NetworkIdentity");
NetworkIdentityType = CurrentAssembly.MainModule.ImportReference(networkIdentityTmp);
NetworkBehaviourType = NetAssembly.MainModule.GetType("Mirror.NetworkBehaviour");
NetworkBehaviourType2 = CurrentAssembly.MainModule.ImportReference(NetworkBehaviourType);
NetworkConnectionType = NetAssembly.MainModule.GetType("Mirror.NetworkConnection");
MonoBehaviourType = UnityAssembly.MainModule.GetType("UnityEngine.MonoBehaviour");
ScriptableObjectType = UnityAssembly.MainModule.GetType("UnityEngine.ScriptableObject");
NetworkConnectionType = NetAssembly.MainModule.GetType("Mirror.NetworkConnection");
NetworkConnectionType = CurrentAssembly.MainModule.ImportReference(NetworkConnectionType);
MessageBaseType = NetAssembly.MainModule.GetType("Mirror.MessageBase");
SyncListType = NetAssembly.MainModule.GetType("Mirror.SyncList`1");
SyncSetType = NetAssembly.MainModule.GetType("Mirror.SyncSet`1");
SyncDictionaryType = NetAssembly.MainModule.GetType("Mirror.SyncDictionary`2");
NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, CurrentAssembly, "syncVarDirtyBits");
TypeDefinition NetworkWriterPoolType = NetAssembly.MainModule.GetType("Mirror.NetworkWriterPool");
GetPooledWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, CurrentAssembly, "GetWriter");
RecycleWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, CurrentAssembly, "Recycle");
ComponentType = UnityAssembly.MainModule.GetType("UnityEngine.Component");
ClientSceneType = NetAssembly.MainModule.GetType("Mirror.ClientScene");
ReadyConnectionReference = Resolvers.ResolveMethod(ClientSceneType, CurrentAssembly, "get_readyConnection");
getBehaviourIsServer = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "get_isServer");
setSyncVarReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SetSyncVar");
setSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "setSyncVarHookGuard");
getSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "getSyncVarHookGuard");
setSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SetSyncVarGameObject");
getSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "GetSyncVarGameObject");
setSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SetSyncVarNetworkIdentity");
getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "GetSyncVarNetworkIdentity");
registerCommandDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "RegisterCommandDelegate");
registerRpcDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "RegisterRpcDelegate");
registerEventDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "RegisterEventDelegate");
getTypeReference = Resolvers.ResolveMethod(objectType, CurrentAssembly, "GetType");
getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, CurrentAssembly, "GetTypeFromHandle");
logErrorReference = Resolvers.ResolveMethod(UnityAssembly.MainModule.GetType("UnityEngine.Debug"), CurrentAssembly, "LogError");
logWarningReference = Resolvers.ResolveMethod(UnityAssembly.MainModule.GetType("UnityEngine.Debug"), CurrentAssembly, "LogWarning");
sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SendCommandInternal");
sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SendRPCInternal");
sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SendTargetRPCInternal");
sendEventInternal = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "SendEventInternal");
SyncObjectType = CurrentAssembly.MainModule.ImportReference(SyncObjectType);
InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, CurrentAssembly, "InitSyncObject");
}
public static bool IsNetworkBehaviour(TypeDefinition td)
{
return td.IsDerivedFrom(NetworkBehaviourType);
}
public static bool IsValidTypeToGenerate(TypeDefinition variable)
{
// a valid type is a simple class or struct. so we generate only code for types we dont know, and if they are not inside
// this assembly it must mean that we are trying to serialize a variable outside our scope. and this will fail.
// no need to report an error here, the caller will report a better error
string assembly = CurrentAssembly.MainModule.Name;
return variable.Module.Name == assembly;
}
static void CheckMonoBehaviour(TypeDefinition td)
{
if (td.IsDerivedFrom(MonoBehaviourType))
{
MonoBehaviourProcessor.Process(td);
}
}
static bool CheckNetworkBehaviour(TypeDefinition td)
{
if (!td.IsClass)
return false;
if (!IsNetworkBehaviour(td))
{
CheckMonoBehaviour(td);
return false;
}
// process this and base classes from parent to child order
List<TypeDefinition> behaviourClasses = new List<TypeDefinition>();
TypeDefinition parent = td;
while (parent != null)
{
if (parent.FullName == NetworkBehaviourType.FullName)
{
break;
}
try
{
behaviourClasses.Insert(0, parent);
parent = parent.BaseType.Resolve();
}
catch (AssemblyResolutionException)
{
// this can happen for plugins.
//Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
break;
}
}
bool didWork = false;
foreach (TypeDefinition behaviour in behaviourClasses)
{
didWork |= ProcessNetworkBehaviourType(behaviour);
}
return didWork;
}
static bool CheckMessageBase(TypeDefinition td)
{
if (!td.IsClass)
return false;
bool didWork = false;
// are ANY parent classes MessageBase
TypeReference parent = td.BaseType;
while (parent != null)
{
if (parent.FullName == MessageBaseType.FullName)
{
MessageClassProcessor.Process(td);
didWork = true;
break;
}
try
{
parent = parent.Resolve().BaseType;
}
catch (AssemblyResolutionException)
{
// this can happen for plugins.
//Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
break;
}
}
// check for embedded types
foreach (TypeDefinition embedded in td.NestedTypes)
{
didWork |= CheckMessageBase(embedded);
}
return didWork;
}
static bool CheckSyncList(TypeDefinition td)
{
if (!td.IsClass)
return false;
bool didWork = false;
// are ANY parent classes SyncListStruct
TypeReference parent = td.BaseType;
while (parent != null)
{
if (parent.FullName.StartsWith(SyncListType.FullName, StringComparison.Ordinal))
{
SyncListProcessor.Process(td);
didWork = true;
break;
}
if (parent.FullName.StartsWith(SyncSetType.FullName, StringComparison.Ordinal))
{
SyncListProcessor.Process(td);
didWork = true;
break;
}
if (parent.FullName.StartsWith(SyncDictionaryType.FullName, StringComparison.Ordinal))
{
SyncDictionaryProcessor.Process(td);
didWork = true;
break;
}
try
{
parent = parent.Resolve().BaseType;
}
catch (AssemblyResolutionException)
{
// this can happen for pluins.
//Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
break;
}
}
// check for embedded types
foreach (TypeDefinition embedded in td.NestedTypes)
{
didWork |= CheckSyncList(embedded);
}
return didWork;
}
static bool Weave(string assName, IEnumerable<string> dependencies, string unityEngineDLLPath, string mirrorNetDLLPath, string outputDir)
{
using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver())
using (CurrentAssembly = AssemblyDefinition.ReadAssembly(assName, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = asmResolver }))
{
asmResolver.AddSearchDirectory(Path.GetDirectoryName(assName));
asmResolver.AddSearchDirectory(Helpers.UnityEngineDLLDirectoryName());
asmResolver.AddSearchDirectory(Path.GetDirectoryName(unityEngineDLLPath));
asmResolver.AddSearchDirectory(Path.GetDirectoryName(mirrorNetDLLPath));
if (dependencies != null)
{
foreach (string path in dependencies)
{
asmResolver.AddSearchDirectory(path);
}
}
SetupTargetTypes();
System.Diagnostics.Stopwatch rwstopwatch = System.Diagnostics.Stopwatch.StartNew();
ReaderWriterProcessor.ProcessReadersAndWriters(CurrentAssembly);
rwstopwatch.Stop();
Console.WriteLine("Find all reader and writers took " + rwstopwatch.ElapsedMilliseconds + " milliseconds");
ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
Console.WriteLine("Script Module: {0}", moduleDefinition.Name);
// Process each NetworkBehaviour
bool didWork = false;
// We need to do 2 passes, because SyncListStructs might be referenced from other modules, so we must make sure we generate them first.
for (int pass = 0; pass < 2; pass++)
{
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
foreach (TypeDefinition td in moduleDefinition.Types)
{
if (td.IsClass && td.BaseType.CanBeResolved())
{
try
{
if (pass == 0)
{
didWork |= CheckSyncList(td);
}
else
{
didWork |= CheckNetworkBehaviour(td);
didWork |= CheckMessageBase(td);
}
}
catch (Exception ex)
{
Error(ex.ToString());
throw ex;
}
}
if (WeavingFailed)
{
return false;
}
}
watch.Stop();
Console.WriteLine("Pass: " + pass + " took " + watch.ElapsedMilliseconds + " milliseconds");
}
if (didWork)
{
// this must be done for ALL code, not just NetworkBehaviours
try
{
PropertySiteProcessor.ProcessSitesModule(CurrentAssembly.MainModule);
}
catch (Exception e)
{
Log.Error("ProcessPropertySites exception: " + e);
return false;
}
if (WeavingFailed)
{
//Log.Error("Failed phase II.");
return false;
}
// write to outputDir if specified, otherwise perform in-place write
WriterParameters writeParams = new WriterParameters { WriteSymbols = true };
if (outputDir != null)
{
CurrentAssembly.Write(Helpers.DestinationFileFor(outputDir, assName), writeParams);
}
else
{
CurrentAssembly.Write(writeParams);
}
}
}
return true;
}
public static bool WeaveAssemblies(IEnumerable<string> assemblies, IEnumerable<string> dependencies, string outputDir, string unityEngineDLLPath, string mirrorNetDLLPath)
{
WeavingFailed = false;
WeaveLists = new WeaverLists();
using (UnityAssembly = AssemblyDefinition.ReadAssembly(unityEngineDLLPath))
using (NetAssembly = AssemblyDefinition.ReadAssembly(mirrorNetDLLPath))
{
SetupUnityTypes();
try
{
foreach (string ass in assemblies)
{
if (!Weave(ass, dependencies, unityEngineDLLPath, mirrorNetDLLPath, outputDir))
{
return false;
}
}
}
catch (Exception e)
{
Log.Error("Exception :" + e);
return false;
}
}
return true;
}
}
}