void ProcessMethod(MethodDefinition md, FoundType foundType) { if (IgnoreMethod(md)) { return; } ProcessMethodAttributes(md, foundType); }
void ProcessMethodAttributes(MethodDefinition md, FoundType foundType) { InjectGuard <ServerAttribute>(md, foundType, IsServer, "[Server] function '{0}' called when server not active"); InjectGuard <ClientAttribute>(md, foundType, IsClient, "[Client] function '{0}' called when client not active"); InjectGuard <HasAuthorityAttribute>(md, foundType, HasAuthority, "[Has Authority] function '{0}' called on player without authority"); InjectGuard <LocalPlayerAttribute>(md, foundType, IsLocalPlayer, "[Local Player] function '{0}' called on nonlocal player"); CheckAttribute <ServerRpcAttribute>(md, foundType); CheckAttribute <ClientRpcAttribute>(md, foundType); }
/// <summary> /// Check if Syncvar or SyncObject are used outside of NetworkBehaviour /// </summary> /// <param name="fd"></param> /// <param name="foundType"></param> void ProcessFields(FieldDefinition fd, FoundType foundType) { if (fd.HasCustomAttribute <SyncVarAttribute>()) { logger.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {foundType.TypeDefinition.Name} is not a NetworkBehaviour", fd); } // only check SyncObjects inside Monobehaviours if (foundType.IsMonoBehaviour && SyncObjectProcessor.ImplementsSyncObject(fd.FieldType)) { logger.Error($"{fd.Name} is a SyncObject and can not be used inside Monobehaviour. {foundType.TypeDefinition.Name} is not a NetworkBehaviour", fd); } }
void CheckAttribute <TAttribute>(MethodDefinition md, FoundType foundType) { CustomAttribute attribute = md.GetCustomAttribute <TAttribute>(); if (attribute == null) { return; } if (!foundType.IsNetworkBehaviour) { logger.Error($"{attribute.AttributeType.Name} method {md.Name} must be declared in a NetworkBehaviour", md); } }
void ProcessType(FoundType foundType) { foreach (MethodDefinition md in foundType.TypeDefinition.Methods) { ProcessMethod(md, foundType); } if (!foundType.IsNetworkBehaviour) { foreach (FieldDefinition fd in foundType.TypeDefinition.Fields) { ProcessFields(fd, foundType); } } }
void InjectGuard <TAttribute>(MethodDefinition md, FoundType foundType, MethodReference predicate, string format) { CustomAttribute attribute = md.GetCustomAttribute <TAttribute>(); if (attribute == null) { return; } if (md.IsAbstract) { logger.Error($"{typeof(TAttribute)} can't be applied to abstract method. Apply to override methods instead.", md); return; } if (!foundType.IsNetworkBehaviour) { logger.Error($"{attribute.AttributeType.Name} method {md.Name} must be declared in a NetworkBehaviour", md); return; } if (md.Name == "Awake" && !md.HasParameters) { logger.Error($"{attribute.AttributeType.Name} will not work on the Awake method.", md); return; } // dont need to set modified for errors, so we set it here when we start doing ILProcessing modified = true; bool throwError = attribute.GetField("error", true); ILProcessor worker = md.Body.GetILProcessor(); Instruction top = md.Body.Instructions[0]; worker.InsertBefore(top, worker.Create(OpCodes.Ldarg_0)); worker.InsertBefore(top, worker.Create(OpCodes.Call, predicate)); worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); if (throwError) { string message = string.Format(format, md.Name); worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, message)); worker.InsertBefore(top, worker.Create(OpCodes.Newobj, () => new MethodInvocationException(""))); worker.InsertBefore(top, worker.Create(OpCodes.Throw)); } InjectGuardParameters(md, worker, top); InjectGuardReturnValue(md, worker, top); worker.InsertBefore(top, worker.Create(OpCodes.Ret)); }
/// <summary> /// Returns all base types that are between the type and NetworkBehaviour /// </summary> /// <param name="foundType"></param> /// <returns></returns> static List <TypeDefinition> FindAllBaseTypes(FoundType foundType) { var behaviourClasses = new List <TypeDefinition>(); TypeDefinition type = foundType.TypeDefinition; while (type != null) { if (type.Is <NetworkBehaviour>()) { break; } behaviourClasses.Add(type); type = type.BaseType.TryResolve(); } return(behaviourClasses); }
bool WeaveNetworkBehavior(FoundType foundType) { List <TypeDefinition> behaviourClasses = FindAllBaseTypes(foundType); bool modified = false; // process this and base classes from parent to child order for (int i = behaviourClasses.Count - 1; i >= 0; i--) { TypeDefinition behaviour = behaviourClasses[i]; if (NetworkBehaviourProcessor.WasProcessed(behaviour)) { continue; } modified |= new NetworkBehaviourProcessor(behaviour, readers, writers, propertySiteProcessor, logger).Process(); } return(modified); }