Add StickGame Assets

This commit is contained in:
Dzejkobik007
2024-03-24 22:21:16 +01:00
parent 5a5812c0c7
commit 6c8b523d1f
6643 changed files with 596260 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
using MonoFN.Cecil;
using SR = System.Reflection;
namespace FishNet.CodeGenerating
{
internal abstract class CodegenBase
{
//Lazy debug checks.
public bool IsIsolatedAsm => (Module.Name.Contains("IsolatedAsm"));
public bool IsRuntimeAsm => (Module.Name.Contains("FishNet.Runtime"));
public CodegenSession Session { get; private set; }
public ModuleDefinition Module { get; private set; }
public virtual bool ImportReferences() { return true; }
public void Initialize(CodegenSession session)
{
Session = session;
Module = session.Module;
}
/// <summary>
/// Returns class of type if found within Session.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal T GetClass<T>() where T : CodegenBase => Session.GetClass<T>();
#region Logging.
/// <summary>
/// Logs a warning.
/// </summary>
/// <param name="msg"></param>
internal void LogWarning(string msg) => Session.LogWarning(msg);
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="msg"></param>
internal void LogError(string msg) => Session.LogError(msg);
#endregion
#region ImportReference.
public MethodReference ImportReference(SR.MethodBase method) => Session.ImportReference(method);
public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context) => Session.ImportReference(method, context);
public TypeReference ImportReference(TypeReference type) => Session.ImportReference(type);
public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) => Session.ImportReference(type, context);
public FieldReference ImportReference(FieldReference field) => Session.ImportReference(field);
public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) => Session.ImportReference(field, context);
public FieldReference ImportReference(SR.FieldInfo field) => Session.ImportReference(field);
public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context) => Session.ImportReference(field, context);
public MethodReference ImportReference(MethodReference method) => Session.ImportReference(method);
public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) => Session.ImportReference(method, context);
public TypeReference ImportReference(System.Type type) => Session.ImportReference(type, null);
public TypeReference ImportReference(System.Type type, IGenericParameterProvider context) => Session.ImportReference(type, context);
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8462034e5255191499a018bd8fbbf751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,361 @@

using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Serializing;
using FishNet.Serializing.Helping;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.CodeGenerating.Processing
{
internal class CustomSerializerProcessor : CodegenBase
{
#region Types.
internal enum ExtensionType
{
None,
Write,
Read
}
#endregion
internal bool CreateSerializerDelegates(TypeDefinition typeDef, bool replace)
{
bool modified = false;
/* Find all declared methods and register delegates to them.
* After they are all registered create any custom writers
* needed to complete the declared methods. It's important to
* make generated writers after so that a generated method
* isn't made for a type when the user has already made a declared one. */
foreach (MethodDefinition methodDef in typeDef.Methods)
{
ExtensionType extensionType = GetExtensionType(methodDef);
if (extensionType == ExtensionType.None)
continue;
if (base.GetClass<GeneralHelper>().CodegenExclude(methodDef))
continue;
MethodReference methodRef = base.ImportReference(methodDef);
if (extensionType == ExtensionType.Write)
{
base.GetClass<WriterProcessor>().AddWriterMethod(methodRef.Parameters[1].ParameterType, methodRef, false, !replace);
modified = true;
}
else if (extensionType == ExtensionType.Read)
{
base.GetClass<ReaderProcessor>().AddReaderMethod(methodRef.ReturnType, methodRef, false, !replace);
modified = true;
}
}
return modified;
}
/// <summary>
/// Creates serializers for any custom types for declared methods.
/// </summary>
/// <param name="declaredMethods"></param>
/// <param name="moduleDef"></param>
internal bool CreateSerializers(TypeDefinition typeDef)
{
bool modified = false;
List<(MethodDefinition, ExtensionType)> declaredMethods = new List<(MethodDefinition, ExtensionType)>();
/* Go through all custom serializers again and see if
* they use any types that the user didn't make a serializer for
* and that there isn't a built-in type for. Create serializers
* for these types. */
foreach (MethodDefinition methodDef in typeDef.Methods)
{
ExtensionType extensionType = GetExtensionType(methodDef);
if (extensionType == ExtensionType.None)
continue;
if (base.GetClass<GeneralHelper>().CodegenExclude(methodDef))
continue;
declaredMethods.Add((methodDef, extensionType));
modified = true;
}
//Now that all declared are loaded see if any of them need generated serializers.
foreach ((MethodDefinition methodDef, ExtensionType extensionType) in declaredMethods)
CreateSerializers(extensionType, methodDef);
return modified;
}
/// <summary>
/// Creates a custom serializer for any types not handled within users declared.
/// </summary>
/// <param name="extensionType"></param>
/// <param name="moduleDef"></param>
/// <param name="methodDef"></param>
/// <param name="diagnostics"></param>
private void CreateSerializers(ExtensionType extensionType, MethodDefinition methodDef)
{
for (int i = 0; i < methodDef.Body.Instructions.Count; i++)
CheckToModifyInstructions(extensionType, methodDef, ref i);
}
/// <summary>
/// Creates delegates for custom comparers.
/// </summary>
internal bool CreateComparerDelegates(TypeDefinition typeDef)
{
bool modified = false;
GeneralHelper gh = base.GetClass<GeneralHelper>();
/* Find all declared methods and register delegates to them.
* After they are all registered create any custom writers
* needed to complete the declared methods. It's important to
* make generated writers after so that a generated method
* isn't made for a type when the user has already made a declared one. */
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (gh.CodegenExclude(methodDef))
continue;
if (!methodDef.HasCustomAttribute<CustomComparerAttribute>())
continue;
//Validate return type.
if (methodDef.ReturnType.FullName != gh.GetTypeReference(typeof(bool)).FullName)
{
base.LogError($"Comparer method {methodDef.Name} in type {typeDef.FullName} must return bool.");
continue;
}
/* Make sure parameters are correct. */
//Invalid count.
if (methodDef.Parameters.Count != 2)
{
base.LogError($"Comparer method {methodDef.Name} in type {typeDef.FullName} must have exactly two parameters, each of the same type which is being compared.");
continue;
}
TypeReference p0Tr = methodDef.Parameters[0].ParameterType;
TypeReference p1Tr = methodDef.Parameters[0].ParameterType;
//Not the same types.
if (p0Tr != p1Tr)
{
base.LogError($"Both parameters must be the same type in comparer method {methodDef.Name} in type {typeDef.FullName}.");
continue;
}
base.ImportReference(methodDef);
base.ImportReference(p0Tr);
gh.RegisterComparerDelegate(methodDef, p0Tr);
gh.CreateComparerDelegate(methodDef, p0Tr);
}
return modified;
}
/// <summary>
/// Checks if instructions need to be modified and does so.
/// </summary>
/// <param name="methodDef"></param>
/// <param name="instructionIndex"></param>
private void CheckToModifyInstructions(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex)
{
Instruction instruction = methodDef.Body.Instructions[instructionIndex];
//Fields.
if (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld)
CheckFieldReferenceInstruction(extensionType, methodDef, ref instructionIndex);
//Method calls.
else if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt)
CheckCallInstruction(extensionType, methodDef, ref instructionIndex, (MethodReference)instruction.Operand);
}
/// <summary>
/// Checks if a reader or writer must be generated for a field type.
/// </summary>
/// <param name="methodDef"></param>
/// <param name="instructionIndex"></param>
private void CheckFieldReferenceInstruction(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex)
{
Instruction instruction = methodDef.Body.Instructions[instructionIndex];
FieldReference field = (FieldReference)instruction.Operand;
TypeReference typeRef = field.DeclaringType;
if (typeRef.IsType(typeof(GenericWriter<>)) || typeRef.IsType(typeof(GenericReader<>)) && typeRef.IsGenericInstance)
{
GenericInstanceType typeGenericInst = (GenericInstanceType)typeRef;
TypeReference parameterType = typeGenericInst.GenericArguments[0];
CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType);
}
}
/// <summary>
/// Checks if a reader or writer must be generated for a call type.
/// </summary>
/// <param name="extensionType"></param>
/// <param name="moduleDef"></param>
/// <param name="methodDef"></param>
/// <param name="instructionIndex"></param>
/// <param name="method"></param>
private void CheckCallInstruction(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, MethodReference method)
{
if (!method.IsGenericInstance)
return;
//True if call is to read/write.
bool canCreate = (
method.Is<Writer>(nameof(Writer.Write)) ||
method.Is<Reader>(nameof(Reader.Read))
);
if (canCreate)
{
GenericInstanceMethod instanceMethod = (GenericInstanceMethod)method;
TypeReference parameterType = instanceMethod.GenericArguments[0];
if (parameterType.IsGenericParameter)
return;
CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType);
}
}
/// <summary>
/// Creates a reader or writer for parameterType if needed. Otherwise calls existing reader.
/// </summary>
private void CreateReaderOrWriter(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, TypeReference parameterType)
{
if (!parameterType.IsGenericParameter && parameterType.CanBeResolved(base.Session))
{
TypeDefinition typeDefinition = parameterType.CachedResolve(base.Session);
//If class and not value type check for accessible constructor.
if (typeDefinition.IsClass && !typeDefinition.IsValueType)
{
MethodDefinition constructor = typeDefinition.GetDefaultConstructor(base.Session);
//Constructor is inaccessible, cannot create serializer for type.
if (constructor != null && !constructor.IsPublic)
{
base.LogError($"Unable to generator serializers for {typeDefinition.FullName} because it's constructor is not public.");
return;
}
}
ILProcessor processor = methodDef.Body.GetILProcessor();
//Find already existing read or write method.
MethodReference createdMethodRef = (extensionType == ExtensionType.Write) ?
base.GetClass<WriterProcessor>().GetWriteMethodReference(parameterType) :
base.GetClass<ReaderProcessor>().GetReadMethodReference(parameterType);
//If a created method already exist nothing further is required.
if (createdMethodRef != null)
{
TryInsertAutoPack(ref instructionIndex);
//Replace call to generic with already made serializer.
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
methodDef.Body.Instructions[instructionIndex] = newInstruction;
return;
}
else
{
createdMethodRef = (extensionType == ExtensionType.Write) ?
base.GetClass<WriterProcessor>().CreateWriter(parameterType) :
base.GetClass<ReaderProcessor>().CreateReader(parameterType);
}
//If method was created.
if (createdMethodRef != null)
{
TryInsertAutoPack(ref instructionIndex);
//Set new instruction.
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
methodDef.Body.Instructions[instructionIndex] = newInstruction;
}
}
void TryInsertAutoPack(ref int insertIndex)
{
if (IsAutoPackMethod(parameterType, extensionType))
{
ILProcessor processor = methodDef.Body.GetILProcessor();
AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(parameterType);
Instruction autoPack = processor.Create(OpCodes.Ldc_I4, (int)packType);
methodDef.Body.Instructions.Insert(insertIndex, autoPack);
insertIndex++;
}
}
}
/// <summary>
/// Returns if a typeRef serializer requires or uses autopacktype.
/// </summary>
private bool IsAutoPackMethod(TypeReference typeRef, ExtensionType extensionType)
{
if (extensionType == ExtensionType.Write)
return base.GetClass<WriterProcessor>().IsAutoPackedType(typeRef);
else if (extensionType == ExtensionType.Read)
return base.GetClass<ReaderProcessor>().IsAutoPackedType(typeRef);
else
return false;
}
/// <summary>
/// Returns the RPC attribute on a method, if one exist. Otherwise returns null.
/// </summary>
/// <param name="methodDef"></param>
/// <returns></returns>
private ExtensionType GetExtensionType(MethodDefinition methodDef)
{
bool hasExtensionAttribute = methodDef.HasCustomAttribute<System.Runtime.CompilerServices.ExtensionAttribute>();
if (!hasExtensionAttribute)
return ExtensionType.None;
bool write = (methodDef.ReturnType == methodDef.Module.TypeSystem.Void);
//Return None for Mirror types.
#if MIRROR
if (write)
{
if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkWriter")
return ExtensionType.None;
}
else
{
if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkReader")
return ExtensionType.None;
}
#endif
string prefix = (write) ? WriterProcessor.WRITE_PREFIX : ReaderProcessor.READ_PREFIX;
//Does not contain prefix.
if (methodDef.Name.Length < prefix.Length || methodDef.Name.Substring(0, prefix.Length) != prefix)
return ExtensionType.None;
//Make sure first parameter is right.
if (methodDef.Parameters.Count >= 1)
{
TypeReference tr = methodDef.Parameters[0].ParameterType;
if (tr.FullName != base.GetClass<WriterImports>().Writer_TypeRef.FullName &&
tr.FullName != base.GetClass<ReaderImports>().Reader_TypeRef.FullName)
return ExtensionType.None;
}
if (write && methodDef.Parameters.Count < 2)
{
base.LogError($"{methodDef.FullName} must have at least two parameters, the first being PooledWriter, and second value to write.");
return ExtensionType.None;
}
else if (!write && methodDef.Parameters.Count < 1)
{
base.LogError($"{methodDef.FullName} must have at least one parameters, the first being PooledReader.");
return ExtensionType.None;
}
return (write) ? ExtensionType.Write : ExtensionType.Read;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9269fd8a62199e24c965b4c99b641244
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,523 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.CodeGenerating.Processing.Rpc;
using FishNet.Configuring;
using FishNet.Object;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using MonoFN.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace FishNet.CodeGenerating.Processing
{
internal class NetworkBehaviourProcessor : CodegenBase
{
#region Private.
/// <summary>
/// Classes which have been processed for all NetworkBehaviour features.
/// </summary>
private HashSet<TypeDefinition> _processedClasses = new HashSet<TypeDefinition>();
#endregion
#region Const.
internal const string EARLY_INITIALIZED_NAME = "NetworkInitializeEarly_";
internal const string LATE_INITIALIZED_NAME = "NetworkInitializeLate_";
internal const string NETWORKINITIALIZE_EARLY_INTERNAL_NAME = "NetworkInitialize___Early";
internal const string NETWORKINITIALIZE_LATE_INTERNAL_NAME = "NetworkInitialize__Late";
#endregion
/// <summary>
///
/// </summary>
/// <param name="typeDef"></param>
/// <param name="processedSyncs">SyncTypes processed for typeDef and inherited.</param>
/// <returns></returns>
internal bool ProcessLocal(TypeDefinition typeDef, List<(SyncType, ProcessedSync)> processedSyncs)
{
bool modified = false;
TypeDefinition copyTypeDef = typeDef;
//TypeDefs which are using prediction.
List<TypeDefinition> _usesPredictionTypeDefs = new List<TypeDefinition>();
//Make collection of NBs to processor.
List<TypeDefinition> typeDefs = new List<TypeDefinition>();
do
{
if (!HasClassBeenProcessed(copyTypeDef))
{
//Disallow nested network behaviours.
ICollection<TypeDefinition> nestedTds = copyTypeDef.NestedTypes;
foreach (TypeDefinition item in nestedTds)
{
if (item.InheritsNetworkBehaviour(base.Session))
{
base.LogError($"{copyTypeDef.FullName} contains nested NetworkBehaviours. These are not supported.");
return modified;
}
}
typeDefs.Add(copyTypeDef);
}
copyTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTypeDef, base.Session);
} while (copyTypeDef != null);
/* Reverse type definitions so that the parent
* is first. This counts indexes up as we go further
* down the children. By doing so we do not have to
* rebuild rpc or synctype indexes when a parent is inherited
* multiple times. EG: with this solution if parent had 1 sync type
* and childA had 2 the parent would be index 0, and childA would have 1 and 2.
* But also, if childB inherited childA it would have 3+.
*
* Going in reverse also gaurantees the awake method will already be created
* or modified in any class a child inherits. This lets us call it appropriately
* as well error if the awake does not exist, such as could not be created. */
typeDefs.Reverse();
foreach (TypeDefinition td in typeDefs)
{
/* Create NetworkInitialize before-hand so the other procesors
* can use it. */
MethodDefinition networkInitializeIfDisabledMd;
CreateNetworkInitializeMethods(td, out networkInitializeIfDisabledMd);
CallNetworkInitializesFromNetworkInitializeIfDisabled(networkInitializeIfDisabledMd);
/* Prediction. */
/* Run prediction first since prediction will modify
* user data passed into prediction methods. Because of this
* other RPCs should use the modified version and reader/writers
* made for prediction. */
if (base.GetClass<PredictionProcessor>().Process(td))
{
_usesPredictionTypeDefs.Add(td);
modified = true;
}
//25ms
/* RPCs. */
modified |= base.GetClass<RpcProcessor>().ProcessLocal(td);
//30ms
/* //perf rpcCounts can be optimized by having different counts
* for target, observers, server, replicate, and reoncile rpcs. Since
* each registers to their own delegates this is possible. */
/* SyncTypes. */
modified |= base.GetClass<NetworkBehaviourSyncProcessor>().ProcessLocal(td, processedSyncs);
//Call base networkinitialize early/late.
CallBaseOnNetworkInitializeMethods(td);
//Add networkinitialize executed check to early/late.
AddNetworkInitializeExecutedChecks(td);
//Copy user logic from awake into a new method.
CopyAwakeUserLogic(td);
/* Create awake method or if already exist make
* it public virtual. */
if (!ModifyAwakeMethod(td, out bool awakeCreated))
{
//This is a hard fail and will break the solution so throw here.
base.LogError($"{td.FullName} has an Awake method which could not be modified, or could not be found. This often occurs when a child class is in an assembly different from the parent, and the parent does not implement Awake. To resolve this make an Awake in {td.Name} public virtual.");
return modified;
}
//Calls NetworkInitializeEarly from awake.
CallMethodFromAwake(td, NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
//Only call base if awake was created. Otherwise let the users implementation handle base calling.
if (awakeCreated)
CallBaseAwake(td);
//Call logic user may have put in awake.
CallAwakeUserLogic(td);
//NetworkInitializeLate from awake.
CallMethodFromAwake(td, NETWORKINITIALIZE_LATE_INTERNAL_NAME);
//Since awake methods are erased ret has to be added at the end.
AddReturnToAwake(td);
//70ms
_processedClasses.Add(td);
}
if (processedSyncs.Count > NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE)
{
base.LogError($"Found {processedSyncs.Count} SyncTypes within {typeDef.FullName} and inherited classes. The maximum number of allowed SyncTypes within type and inherited types is {NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE}. Remove SyncTypes or condense them using data containers, or a custom SyncObject.");
return false;
}
/* If here then all inerited classes for firstTypeDef have
* been processed. */
return modified;
}
/// <summary>
/// Gets the name to use for user awake logic method.
/// </summary>
internal string GetAwakeUserLogicMethodDefinition(TypeDefinition td) => $"Awake_UserLogic_{td.FullName}_{base.Module.Name}";
/// <summary>
/// Returns if a class has been processed.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
private bool HasClassBeenProcessed(TypeDefinition typeDef)
{
return _processedClasses.Contains(typeDef);
}
/// <summary>
/// Returns if any typeDefs have attributes which are not allowed to be used outside NetworkBehaviour.
/// </summary>
/// <param name="typeDefs"></param>
/// <returns></returns>
internal bool NonNetworkBehaviourHasInvalidAttributes(Collection<TypeDefinition> typeDefs)
{
NetworkBehaviourSyncProcessor nbSyncProcessor = base.GetClass<NetworkBehaviourSyncProcessor>();
RpcProcessor rpcProcessor = base.GetClass<RpcProcessor>();
foreach (TypeDefinition typeDef in typeDefs)
{
//Inherits, don't need to check.
if (typeDef.InheritsNetworkBehaviour(base.Session))
continue;
//Check each method for attribute.
foreach (MethodDefinition md in typeDef.Methods)
{
//Has RPC attribute but doesn't inherit from NB.
if (rpcProcessor.Attributes.HasRpcAttributes(md))
{
base.LogError($"{typeDef.FullName} has one or more RPC attributes but does not inherit from NetworkBehaviour.");
return true;
}
}
//Check fields for attribute.
foreach (FieldDefinition fd in typeDef.Fields)
{
if (nbSyncProcessor.IsSyncType(fd))
{
base.LogError($"{typeDef.FullName} implements one or more SyncTypes but does not inherit from NetworkBehaviour.");
return true;
}
}
}
//Fallthrough / pass.
return false;
}
/// <summary>
/// Calls the next awake method if the nested awake was created by codegen.
/// </summary>
/// <returns></returns>
private void CallBaseAwake(TypeDefinition td)
{
/* If base is not a class which can be processed then there
* is no need to continue. */
if (!td.CanProcessBaseType(base.Session))
return;
MethodReference baseAwakeMr = td.GetMethodReferenceInBase(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME);
//This Awake.
MethodDefinition tdAwakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
ILProcessor processor = tdAwakeMd.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0); //base.
processor.Emit(OpCodes.Call, baseAwakeMr);
}
/// <summary>
/// Calls the next awake method if the nested awake was created by codegen.
/// </summary>
/// <returns></returns>
private void CallAwakeUserLogic(TypeDefinition td)
{
//UserLogic.
MethodDefinition userLogicMd = td.GetMethod(GetAwakeUserLogicMethodDefinition(td));
/* Userlogic may be null if Awake was created.
* If so, there's no need to proceed. */
if (userLogicMd == null)
return;
//This Awake.
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
//Call logic.
base.GetClass<GeneralHelper>().CallCopiedMethod(awakeMd, userLogicMd);
}
/// <summary>
/// Adds a check to NetworkInitialize to see if it has already run.
/// </summary>
/// <param name="typeDef"></param>
private void AddNetworkInitializeExecutedChecks(TypeDefinition typeDef)
{
AddCheck(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
AddCheck(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
void AddCheck(string methodName)
{
string fieldName = $"{methodName}{typeDef.FullName}{typeDef.Module.Name}_Excuted";
MethodDefinition md = typeDef.GetMethod(methodName);
if (md == null)
return;
TypeReference boolTr = base.GetClass<GeneralHelper>().GetTypeReference(typeof(bool));
FieldReference fr = typeDef.GetOrCreateFieldReference(base.Session, fieldName, FieldAttributes.Private, boolTr, out bool created);
if (created)
{
List<Instruction> insts = new List<Instruction>();
ILProcessor processor = md.Body.GetILProcessor();
//Add check if already called.
//if (alreadyInitialized) return;
Instruction skipFirstRetInst = processor.Create(OpCodes.Nop);
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldfld, fr));
insts.Add(processor.Create(OpCodes.Brfalse_S, skipFirstRetInst));
insts.Add(processor.Create(OpCodes.Ret));
insts.Add(skipFirstRetInst);
//Set field to true.
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldc_I4_1));
insts.Add(processor.Create(OpCodes.Stfld, fr));
processor.InsertFirst(insts);
}
}
}
/// <summary>
/// Calls base for NetworkInitializeEarly/Late on a TypeDefinition.
/// </summary>
private void CallBaseOnNetworkInitializeMethods(TypeDefinition typeDef)
{
//If base class cannot have a networkinitialize no reason to continue.
if (!typeDef.CanProcessBaseType(base.Session))
return;
string[] initializeMethodNames = new string[] { NETWORKINITIALIZE_EARLY_INTERNAL_NAME, NETWORKINITIALIZE_LATE_INTERNAL_NAME };
foreach (string mdName in initializeMethodNames)
{
/* Awake will always exist because it was added previously.
* Get awake for copy and base of copy. */
MethodDefinition thisMd = typeDef.GetMethod(mdName);
ILProcessor processor = thisMd.Body.GetILProcessor();
/* Awake will always exist because it was added previously.
* Get awake for copy and base of copy. */
MethodReference baseMr = typeDef.GetMethodReferenceInBase(base.Session, mdName);
MethodDefinition baseMd = baseMr.CachedResolve(base.Session);
bool alreadyHasBaseCall = false;
//Check if already calls baseAwake.
foreach (Instruction item in thisMd.Body.Instructions)
{
//If a call or call virt. Although, callvirt should never occur.
if (item.OpCode == OpCodes.Call || item.OpCode == OpCodes.Callvirt)
{
if (item.Operand != null && item.Operand.GetType().Name == nameof(MethodDefinition))
{
MethodDefinition md = (MethodDefinition)item.Operand;
if (md == baseMd)
{
alreadyHasBaseCall = true;
break;
}
}
}
}
if (!alreadyHasBaseCall)
{
//Create instructions for base call.
List<Instruction> instructions = new List<Instruction>
{
processor.Create(OpCodes.Ldarg_0), //this.
processor.Create(OpCodes.Call, baseMr)
};
processor.InsertFirst(instructions);
}
}
}
/// <summary>
/// Adds returns awake method definitions within awakeDatas.
/// </summary>
private void AddReturnToAwake(TypeDefinition td)
{
//This Awake.
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
ILProcessor processor = awakeMd.Body.GetILProcessor();
//If no instructions or the last instruction isnt ret.
if (processor.Body.Instructions.Count == 0
|| processor.Body.Instructions[processor.Body.Instructions.Count - 1].OpCode != OpCodes.Ret)
{
processor.Emit(OpCodes.Ret);
}
}
/// <summary>
/// Calls a method by name from awake.
/// </summary>
private void CallMethodFromAwake(TypeDefinition typeDef, string methodName)
{
//Will never be null because we added it previously.
MethodDefinition awakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
MethodReference networkInitMr = typeDef.GetMethodReference(base.Session, methodName);
ILProcessor processor = awakeMethodDef.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(networkInitMr.GetCallOpCode(base.Session), networkInitMr);
}
/// <summary>
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
/// </summary>
private void CreateNetworkInitializeMethods(TypeDefinition typeDef, out MethodDefinition networkInitializeIfDisabledMd)
{
CreateMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
CreateMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
networkInitializeIfDisabledMd = CreateMethod(nameof(NetworkBehaviour.NetworkInitializeIfDisabled));
MethodDefinition CreateMethod(string name, MethodDefinition copied = null)
{
bool created;
MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, name, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
if (created)
{
//Emit ret into new method.
ILProcessor processor = md.Body.GetILProcessor();
//End of method return.
processor.Emit(OpCodes.Ret);
}
return md;
}
}
/// <summary>
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
/// </summary>
private void CallNetworkInitializesFromNetworkInitializeIfDisabled(MethodDefinition networkInitializeIfDisabledMd)
{
ILProcessor processor = networkInitializeIfDisabledMd.Body.GetILProcessor();
networkInitializeIfDisabledMd.Body.Instructions.Clear();
CallMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
CallMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
processor.Emit(OpCodes.Ret);
void CallMethod(string name)
{
MethodReference initIfDisabledMr = networkInitializeIfDisabledMd.DeclaringType.GetMethodReference(base.Session, name);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(initIfDisabledMr.GetCallOpCode(base.Session), initIfDisabledMr);
}
}
/// <summary>
/// Copies logic from users Awake if present, to a new method.
/// </summary>
private void CopyAwakeUserLogic(TypeDefinition typeDef)
{
MethodDefinition awakeMd = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
//If found copy.
if (awakeMd != null)
base.GetClass<GeneralHelper>().CopyIntoNewMethod(awakeMd, GetAwakeUserLogicMethodDefinition(typeDef), out _);
}
/// <summary>
/// Erases content in awake if it already exist, otherwise makes a new Awake.
/// Makes Awake public and virtual.
/// </summary>
/// <returns>True if successful.</returns>
private bool ModifyAwakeMethod(TypeDefinition typeDef, out bool created)
{
MethodDefinition awakeMd = typeDef.GetOrCreateMethodDefinition(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
//Awake is found. Check for invalid return type.
if (!created)
{
if (awakeMd.ReturnType != typeDef.Module.TypeSystem.Void)
{
base.LogError($"IEnumerator Awake methods are not supported within NetworkBehaviours.");
return false;
}
//Make public if good.
awakeMd.SetPublicAttributes();
}
//Already was made.
else
{
ILProcessor processor = awakeMd.Body.GetILProcessor();
processor.Emit(OpCodes.Ret);
}
//Clear original awake.
awakeMd.Body.Instructions.Clear();
return true;
}
/// <summary>
/// Makes all Awake methods within typeDef and base classes public and virtual.
/// </summary>
/// <param name="typeDef"></param>
internal void CreateFirstNetworkInitializeCall(TypeDefinition typeDef, MethodDefinition firstUserAwakeMethodDef, MethodDefinition firstNetworkInitializeMethodDef)
{
ILProcessor processor;
//Get awake for current method.
MethodDefinition thisAwakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
bool created = false;
//If no awake then make one.
if (thisAwakeMethodDef == null)
{
created = true;
thisAwakeMethodDef = new MethodDefinition(NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES,
typeDef.Module.TypeSystem.Void);
thisAwakeMethodDef.Body.InitLocals = true;
typeDef.Methods.Add(thisAwakeMethodDef);
processor = thisAwakeMethodDef.Body.GetILProcessor();
processor.Emit(OpCodes.Ret);
}
//MethodRefs for networkinitialize and awake.
MethodReference networkInitializeMethodRef = typeDef.Module.ImportReference(firstNetworkInitializeMethodDef);
processor = thisAwakeMethodDef.Body.GetILProcessor();
//Create instructions for base call.
List<Instruction> instructions = new List<Instruction>
{
processor.Create(OpCodes.Ldarg_0), //this.
processor.Create(OpCodes.Call, networkInitializeMethodRef)
};
/* If awake was created then make a call to the users
* first awake. There's no reason to do this if awake
* already existed because the user would have control
* over making that call. */
if (created && firstUserAwakeMethodDef != null)
{
MethodReference baseAwakeMethodRef = typeDef.Module.ImportReference(firstUserAwakeMethodDef);
instructions.Add(processor.Create(OpCodes.Ldarg_0));//this.
instructions.Add(processor.Create(OpCodes.Call, baseAwakeMethodRef));
}
processor.InsertFirst(instructions);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23866e4d620216745a837fa99e801164
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec95af37f78b9e340b5eaa199c1af94a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
<<<<<<<< HEAD:Assets/FishNet/CodeGenerating/Processing/Prediction.meta
guid: 98f6937bb72a7254b92b4656f28b7333
folderAsset: yes
========
guid: 89b00a3741f72ac4e861a08cf1202228
>>>>>>>> origin/3-pre-3.1:Assets/Test/InitializeOrder/InitializeOrder_Test.unity.meta
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0663606e86b0b34bae85df164747e72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,165 @@
using FishNet.CodeGenerating.Extension;
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<MethodDefinition> methods = typeDef.Methods.ToList();
foreach (MethodDefinition md in methods)
{
//Has RPC attribute, doesn't quality for a quality of life attribute.
if (base.GetClass<RpcProcessor>().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;
}
/// <summary>
/// Returns the RPC attribute on a method, if one exist. Otherwise returns null.
/// </summary>
/// <param name="methodDef"></param>
/// <param name="rpcType"></param>
/// <returns></returns>
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<AttributeHelper>().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;
}
/// <summary>
/// Modifies the specified method to use QolType.
/// </summary>
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<NetworkBehaviourHelper>().CreateLocalClientIsOwnerCheck(methodDef, logging, true, false, true);
//Otherwise normal IsClient check.
else
base.GetClass<NetworkBehaviourHelper>().CreateIsClientCheck(methodDef, logging, useStatic, true, !useStatic);
}
}
else if (qolType == QolAttributeType.Server)
{
if (!StripMethod(methodDef))
{
LoggingType logging = qolAttribute.GetField("Logging", LoggingType.Warning);
base.GetClass<NetworkBehaviourHelper>().CreateIsServerCheck(methodDef, logging, useStatic, true, !useStatic);
}
}
bool StripMethod(MethodDefinition md)
{
//Fall through.
return false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5080d7597ffca904b9a9fd5926e4e5a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f9d3654f5816c4409b88fa0602c6df4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 810757384532c3a4b9e4681f869acf1e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
using FishNet.CodeGenerating.Helping;
using FishNet.Object.Helping;
using MonoFN.Cecil;
using System.Collections.Generic;
namespace FishNet.CodeGenerating.Processing.Rpc
{
internal static class AttributeDataExtensions
{
/// <summary>
/// Returns RpcTypes in datas.
/// </summary>
public static List<RpcType> GetRpcTypes(this List<AttributeData> datas)
{
//RpcTypes for originalMd.
List<RpcType> rpcTypes = new List<RpcType>();
foreach (AttributeData ad in datas)
rpcTypes.Add(ad.RpcType);
return rpcTypes;
}
/// <summary>
/// Gets CustomAttribute for rpcType
/// </summary>
public static CustomAttribute GetAttribute(this List<AttributeData> datas, CodegenSession session, RpcType rpcType)
{
for (int i = 0; i < datas.Count; i++)
{
if (datas[i].RpcType == rpcType)
return datas[i].Attribute;
}
session.LogError($"RpcType {rpcType} not found in datas.");
return null;
}
/// <summary>
/// Returns RpcType as flag through combining datas.
/// </summary>
/// <param name="datas"></param>
/// <returns></returns>
public static RpcType GetCombinedRpcType(this List<AttributeData> datas)
{
RpcType result = RpcType.None;
for (int i = 0; i < datas.Count; i++)
result |= datas[i].RpcType;
return result;
}
}
internal class AttributeData
{
public readonly CustomAttribute Attribute;
public readonly RpcType RpcType;
public AttributeData(CustomAttribute attribute, RpcType rpcType)
{
Attribute = attribute;
RpcType = rpcType;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91f84e00db3d1ad448fb6a760afb6927
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,166 @@
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Connection;
using FishNet.Object.Helping;
using MonoFN.Cecil;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace FishNet.CodeGenerating.Processing.Rpc
{
internal class Attributes : CodegenBase
{
/// <summary>
/// Returns if methodDef has any Rpc attribute.
/// </summary>
public bool HasRpcAttributes(MethodDefinition methodDef)
{
foreach (CustomAttribute customAttribute in methodDef.CustomAttributes)
{
RpcType rt = base.Session.GetClass<AttributeHelper>().GetRpcAttributeType(customAttribute);
if (rt != RpcType.None)
return true;
}
//Fall through, nothing found.
return false;
}
/// <summary>
/// Returns a collection of RpcAttribute for methodDef.
/// </summary>
public List<AttributeData> GetRpcAttributes(MethodDefinition methodDef)
{
List<AttributeData> results = new List<AttributeData>();
string asyncAttributeFullName = typeof(AsyncStateMachineAttribute).FullName;
bool isAsync = false;
foreach (CustomAttribute customAttribute in methodDef.CustomAttributes)
{
RpcType rt = base.Session.GetClass<AttributeHelper>().GetRpcAttributeType(customAttribute);
if (rt != RpcType.None)
{
results.Add(new AttributeData(customAttribute, rt));
}
//Not a rpc attribute.
else
{
//Check if async.
if (customAttribute.Is(asyncAttributeFullName))
isAsync = true;
}
}
//Nothing found, exit early.
if (results.Count == 0)
{
return results;
}
//If has at least one RPC attrivbute and is an async method.
else if (isAsync)
{
base.Session.LogError($"{methodDef.Name} is an async RPC. This feature is not currently supported. You may instead run an async method from this RPC.");
return new List<AttributeData>();
}
//If more than one attribute make sure the combination is allowed.
else if (results.Count >= 2)
{
RpcType allRpcTypes = results.GetCombinedRpcType();
if (allRpcTypes != (RpcType.Observers | RpcType.Target))
{
base.Session.LogError($"{methodDef.Name} contains multiple RPC attributes. Only ObserversRpc and TargetRpc attributes may be combined.");
return new List<AttributeData>();
}
}
//Next validate that the method is setup properly for each rpcType.
foreach (AttributeData ad in results)
{
//If not valid then return empty list.
if (!IsRpcMethodValid(methodDef, ad.RpcType))
return new List<AttributeData>();
}
return results;
}
/// <summary>
/// Returns if a RpcMethod can be serialized and has a proper signature.
/// </summary>
private bool IsRpcMethodValid(MethodDefinition methodDef, RpcType rpcType)
{
//Static method.
if (methodDef.IsStatic)
{
base.Session.LogError($"{methodDef.Name} RPC method cannot be static.");
return false;
}
//Is generic type.
else if (methodDef.HasGenericParameters)
{
base.Session.LogError($"{methodDef.Name} RPC method cannot contain generic parameters.");
return false;
}
//Abstract method.
else if (methodDef.IsAbstract)
{
base.Session.LogError($"{methodDef.Name} RPC method cannot be abstract.");
return false;
}
//Non void return.
else if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void)
{
base.Session.LogError($"{methodDef.Name} RPC method must return void.");
return false;
}
//Misc failing conditions.
else
{
//Check for async attribute.
foreach (CustomAttribute ca in methodDef.CustomAttributes)
{
}
}
//TargetRpc but missing correct parameters.
if (rpcType == RpcType.Target)
{
if (methodDef.Parameters.Count == 0 || !methodDef.Parameters[0].Is(typeof(NetworkConnection)))
{
base.Session.LogError($"Target RPC {methodDef.Name} must have a NetworkConnection as the first parameter.");
return false;
}
}
//Make sure all parameters can be serialized.
for (int i = 0; i < methodDef.Parameters.Count; i++)
{
ParameterDefinition parameterDef = methodDef.Parameters[i];
//If NetworkConnection, TargetRpc, and first parameter.
if ((i == 0) && (rpcType == RpcType.Target) && parameterDef.Is(typeof(NetworkConnection)))
continue;
if (parameterDef.ParameterType.IsGenericParameter)
{
base.Session.LogError($"RPC method{methodDef.Name} contains a generic parameter. This is currently not supported.");
return false;
}
//Can be serialized/deserialized.
bool canSerialize = base.GetClass<GeneralHelper>().HasSerializerAndDeserializer(parameterDef.ParameterType, true);
if (!canSerialize)
{
base.Session.LogError($"RPC method {methodDef.Name} parameter type {parameterDef.ParameterType.FullName} does not support serialization. Use a supported type or create a custom serializer.");
return false;
}
}
//Fall through, success.
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 974ebf09757267941a86f92e5072258c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using FishNet.Object.Helping;
using MonoFN.Cecil;
using System.Collections.Generic;
namespace FishNet.CodeGenerating.Processing.Rpc
{
internal class CreatedRpc
{
public MethodDefinition OriginalMethodDef;
public uint MethodHash;
public AttributeData AttributeData;
public MethodDefinition WriterMethodDef;
public MethodDefinition ReaderMethodDef;
public MethodDefinition LogicMethodDef;
public MethodDefinition RedirectMethodDef;
public bool RunLocally;
public RpcType RpcType => AttributeData.RpcType;
public CustomAttribute Attribute => AttributeData.Attribute;
public TypeDefinition TypeDef => OriginalMethodDef.DeclaringType;
public ModuleDefinition Module => OriginalMethodDef.Module;
}
internal static class CreatedRpcExtensions
{
/// <summary>
/// Returns CreatedRpc for rpcType.
/// </summary>
/// <returns></returns>
public static CreatedRpc GetCreatedRpc(this List<CreatedRpc> lst, RpcType rpcType)
{
for (int i = 0; i < lst.Count; i++)
{
if (lst[i].RpcType == rpcType)
return lst[i];
}
//Fall through.
return null;
}
/// <summary>
/// Returns combined RpcType for all entries.
/// </summary>
/// <returns></returns>
public static RpcType GetCombinedRpcType(this List<CreatedRpc> lst)
{
RpcType result = RpcType.None;
for (int i = 0; i < lst.Count; i++)
result |= lst[i].RpcType;
return result;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c2176b6bfcc49934d8f36fba3df74d0c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d4adb5891ee44f4397cd07ac2df0ce0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a1bf86d2c7d5eb64cae05f726e038c06
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using MonoFN.Cecil;
namespace FishNet.CodeGenerating.Processing
{
public class ProcessedSync
{
public FieldReference OriginalFieldRef;
public FieldReference GeneratedFieldRef;
public MethodReference SetMethodRef;
public MethodReference GetMethodRef;
public ProcessedSync(FieldReference originalFieldRef,FieldReference generatedFieldRef, MethodReference setMethodRef, MethodReference getMethodRef)
{
OriginalFieldRef = originalFieldRef;
GeneratedFieldRef = generatedFieldRef;
SetMethodRef = setMethodRef;
GetMethodRef = getMethodRef;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0dc2fea60bfe1341b04e7165251d36f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a4021bd44dc40f47abb494e0a4326f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: