using FishNet.CodeGenerating.Helping; using FishNet.CodeGenerating.Helping.Extension; using FishNet.CodeGenerating.Processing.Rpc; using FishNet.Configuring; using FishNet.Managing.Logging; using MonoFN.Cecil; using MonoFN.Cecil.Cil; using System.Collections.Generic; using System.Linq; namespace FishNet.CodeGenerating.Processing { internal class QolAttributeProcessor : CodegenBase { internal bool Process(TypeDefinition typeDef, bool moveStrippedCalls) { bool modified = false; List methods = typeDef.Methods.ToList(); foreach (MethodDefinition md in methods) { //Has RPC attribute, doesn't quality for a quality of life attribute. if (base.GetClass().Attributes.HasRpcAttributes(md)) continue; QolAttributeType qolType; CustomAttribute qolAttribute = GetQOLAttribute(md, out qolType); if (qolAttribute == null) continue; /* This is a one time check to make sure the qolType is * a supported value. Multiple methods beyond this rely on the * value being supported. Rather than check in each method a * single check is performed here. */ if (qolType != QolAttributeType.Server && qolType != QolAttributeType.Client) { base.LogError($"QolAttributeType of {qolType.ToString()} is unhandled."); continue; } CreateAttributeMethod(md, qolAttribute, qolType); modified = true; } return modified; } /// /// Returns the RPC attribute on a method, if one exist. Otherwise returns null. /// /// /// /// private CustomAttribute GetQOLAttribute(MethodDefinition methodDef, out QolAttributeType qolType) { CustomAttribute foundAttribute = null; qolType = QolAttributeType.None; //Becomes true if an error occurred during this process. bool error = false; //Nothing to check. if (methodDef == null || methodDef.CustomAttributes == null) return null; foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) { QolAttributeType thisQolType = base.GetClass().GetQolAttributeType(customAttribute.AttributeType.FullName); if (thisQolType != QolAttributeType.None) { //A qol attribute already exist. if (foundAttribute != null) { base.LogError($"{methodDef.Name} {thisQolType.ToString()} method cannot have multiple quality of life attributes."); error = true; } ////Static method. //if (methodDef.IsStatic) //{ // CodegenSession.AddError($"{methodDef.Name} {thisQolType.ToString()} method cannot be static."); // error = true; //} //Abstract method. if (methodDef.IsAbstract) { base.LogError($"{methodDef.Name} {thisQolType.ToString()} method cannot be abstract."); error = true; } //If all checks passed. if (!error) { foundAttribute = customAttribute; qolType = thisQolType; } } } //If an error occurred then reset results. if (error) { foundAttribute = null; qolType = QolAttributeType.None; } return foundAttribute; } /// /// Modifies the specified method to use QolType. /// private void CreateAttributeMethod(MethodDefinition methodDef, CustomAttribute qolAttribute, QolAttributeType qolType) { bool inheritsNetworkBehaviour = methodDef.DeclaringType.InheritsNetworkBehaviour(base.Session); //True to use InstanceFInder. bool useStatic = (methodDef.IsStatic || !inheritsNetworkBehaviour); if (qolType == QolAttributeType.Client) { if (!StripMethod(methodDef)) { LoggingType logging = qolAttribute.GetField("Logging", LoggingType.Warning); /* Since isClient also uses insert first * it will be put ahead of the IsOwner check, since the * codegen processes it after IsOwner. EG... * IsOwner will be added first, then IsClient will be added first over IsOwner. */ bool requireOwnership = qolAttribute.GetField("RequireOwnership", false); if (requireOwnership && useStatic) { base.LogError($"Method {methodDef.Name} has a [Client] attribute which requires ownership but the method may not use this attribute. Either the method is static, or the script does not inherit from NetworkBehaviour."); return; } //If (!base.IsOwner); if (requireOwnership) base.GetClass().CreateLocalClientIsOwnerCheck(methodDef, logging, true, false, true); //Otherwise normal IsClient check. else base.GetClass().CreateIsClientCheck(methodDef, logging, useStatic, true); } } else if (qolType == QolAttributeType.Server) { if (!StripMethod(methodDef)) { LoggingType logging = qolAttribute.GetField("Logging", LoggingType.Warning); base.GetClass().CreateIsServerCheck(methodDef, logging, useStatic, true); } } bool StripMethod(MethodDefinition md) { //Fall through. return false; } } } }