fishnet installed
This commit is contained in:
@ -0,0 +1,960 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Connection;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Object.Prediction.Delegating;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Serializing.Helping;
|
||||
using FishNet.Transporting;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using MonoFN.Cecil.Rocks;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using SR = System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Processing
|
||||
{
|
||||
internal class PredictionProcessor : CodegenBase
|
||||
{
|
||||
#region Types.
|
||||
private enum InsertType
|
||||
{
|
||||
First,
|
||||
Last,
|
||||
Current
|
||||
}
|
||||
|
||||
private class CreatedPredictionFields
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for calling replicate user logic.
|
||||
/// </summary>
|
||||
public readonly FieldReference ReplicateULDelegate;
|
||||
/// <summary>
|
||||
/// Delegate for calling replicate user logic.
|
||||
/// </summary>
|
||||
public readonly FieldReference ReconcileULDelegate;
|
||||
/// <summary>
|
||||
/// Replicate data buffered on the server.
|
||||
/// </summary>
|
||||
public readonly FieldReference ServerReplicateDatas;
|
||||
/// <summary>
|
||||
/// Replicate data buffered on the client.
|
||||
/// </summary>
|
||||
public readonly FieldReference ClientReplicateDatas;
|
||||
/// <summary>
|
||||
/// Last reconcile data received from the server.
|
||||
/// </summary>
|
||||
public readonly FieldReference ReconcileData;
|
||||
/// <summary>
|
||||
/// A buffer to read replicates into.
|
||||
/// </summary>
|
||||
public readonly FieldReference ServerReplicateReaderBuffer;
|
||||
|
||||
public CreatedPredictionFields(FieldReference replicateULDelegate, FieldReference reconcileULDelegate, FieldReference serverReplicateDatas, FieldReference clientReplicateDatas, FieldReference reconcileData,
|
||||
FieldReference serverReplicateReaderBuffer)
|
||||
{
|
||||
ReplicateULDelegate = replicateULDelegate;
|
||||
ReconcileULDelegate = reconcileULDelegate;
|
||||
ServerReplicateDatas = serverReplicateDatas;
|
||||
ClientReplicateDatas = clientReplicateDatas;
|
||||
ReconcileData = reconcileData;
|
||||
ServerReplicateReaderBuffer = serverReplicateReaderBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private class PredictionReaders
|
||||
{
|
||||
public MethodReference ReplicateReader;
|
||||
public MethodReference ReconcileReader;
|
||||
|
||||
public PredictionReaders(MethodReference replicateReader, MethodReference reconcileReader)
|
||||
{
|
||||
ReplicateReader = replicateReader;
|
||||
ReconcileReader = reconcileReader;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public.
|
||||
public string IReplicateData_FullName = typeof(IReplicateData).FullName;
|
||||
public string IReconcileData_FullName = typeof(IReconcileData).FullName;
|
||||
public TypeReference ReplicateULDelegate_TypeRef;
|
||||
public TypeReference ReconcileULDelegate_TypeRef;
|
||||
public MethodReference IReplicateData_GetTick_MethodRef;
|
||||
public MethodReference IReplicateData_SetTick_MethodRef;
|
||||
public MethodReference IReconcileData_GetTick_MethodRef;
|
||||
public MethodReference IReconcileData_SetTick_MethodRef;
|
||||
public MethodReference Unity_GetGameObject_MethodRef;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
public const string REPLICATE_LOGIC_PREFIX = "Logic_Replicate___";
|
||||
public const string REPLICATE_READER_PREFIX = "Reader_Replicate___";
|
||||
public const string RECONCILE_LOGIC_PREFIX = "Logic_Reconcile___";
|
||||
public const string RECONCILE_READER_PREFIX = "Reader_Reconcile___";
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
System.Type locType;
|
||||
SR.MethodInfo locMi;
|
||||
|
||||
ReplicateULDelegate_TypeRef = base.ImportReference(typeof(ReplicateUserLogicDelegate<>));
|
||||
ReconcileULDelegate_TypeRef = base.ImportReference(typeof(ReconcileUserLogicDelegate<>));
|
||||
|
||||
//GetGameObject.
|
||||
locMi = typeof(UnityEngine.Component).GetMethod("get_gameObject");
|
||||
Unity_GetGameObject_MethodRef = base.ImportReference(locMi);
|
||||
|
||||
//Get/Set tick.
|
||||
locType = typeof(IReplicateData);
|
||||
foreach (SR.MethodInfo mi in locType.GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(IReplicateData.GetTick))
|
||||
IReplicateData_GetTick_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(IReplicateData.SetTick))
|
||||
IReplicateData_SetTick_MethodRef = base.ImportReference(mi);
|
||||
}
|
||||
|
||||
locType = typeof(IReconcileData);
|
||||
foreach (SR.MethodInfo mi in locType.GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(IReconcileData.GetTick))
|
||||
IReconcileData_GetTick_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(IReconcileData.SetTick))
|
||||
IReconcileData_SetTick_MethodRef = base.ImportReference(mi);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool Process(TypeDefinition typeDef, ref uint rpcCount)
|
||||
{
|
||||
bool modified = false;
|
||||
modified |= ProcessLocal(typeDef, ref rpcCount);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
#region Setup and checks.
|
||||
/// <summary>
|
||||
/// Gets number of predictions by checking for prediction attributes. This does not perform error checking.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
internal uint GetPredictionCount(TypeDefinition typeDef)
|
||||
{
|
||||
/* Currently only one prediction method is allowed per typeDef.
|
||||
* Return 1 soon as a method is found. */
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
foreach (CustomAttribute customAttribute in methodDef.CustomAttributes)
|
||||
{
|
||||
if (customAttribute.Is(base.GetClass<AttributeHelper>().ReplicateAttribute_FullName))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensures only one prediction and reconile method exist per typeDef, and outputs finding.
|
||||
/// </summary>
|
||||
/// <returns>True if there is only one set of prediction methods. False if none, or more than one set.</returns>
|
||||
internal bool GetPredictionMethods(TypeDefinition typeDef, out MethodDefinition replicateMd, out MethodDefinition reconcileMd)
|
||||
{
|
||||
replicateMd = null;
|
||||
reconcileMd = null;
|
||||
|
||||
bool error = false;
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
foreach (CustomAttribute customAttribute in methodDef.CustomAttributes)
|
||||
{
|
||||
if (customAttribute.Is(base.GetClass<AttributeHelper>().ReplicateAttribute_FullName))
|
||||
{
|
||||
if (!MethodIsPrivate(methodDef) || AlreadyFound(replicateMd))
|
||||
error = true;
|
||||
else
|
||||
replicateMd = methodDef;
|
||||
}
|
||||
else if (customAttribute.Is(base.GetClass<AttributeHelper>().ReconcileAttribute_FullName))
|
||||
{
|
||||
if (!MethodIsPrivate(methodDef) || AlreadyFound(reconcileMd))
|
||||
error = true;
|
||||
else
|
||||
reconcileMd = methodDef;
|
||||
}
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
bool MethodIsPrivate(MethodDefinition md)
|
||||
{
|
||||
bool isPrivate = md.Attributes.HasFlag(MethodAttributes.Private);
|
||||
if (!isPrivate)
|
||||
base.LogError($"Method {md.Name} within {typeDef.Name} is a prediction method and must be private.");
|
||||
return isPrivate;
|
||||
}
|
||||
|
||||
bool AlreadyFound(MethodDefinition md)
|
||||
{
|
||||
bool alreadyFound = (md != null);
|
||||
if (alreadyFound)
|
||||
base.LogError($"{typeDef.Name} contains multiple prediction sets; currently only one set is allowed.");
|
||||
|
||||
return alreadyFound;
|
||||
}
|
||||
|
||||
if (!error && ((replicateMd == null) != (reconcileMd == null)))
|
||||
{
|
||||
base.LogError($"{typeDef.Name} must contain both a [Replicate] and [Reconcile] method when using prediction.");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error || (replicateMd == null) || (reconcileMd == null))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool ProcessLocal(TypeDefinition typeDef, ref uint rpcCount)
|
||||
{
|
||||
MethodDefinition replicateMd;
|
||||
MethodDefinition reconcileMd;
|
||||
|
||||
//Not using prediction methods.
|
||||
if (!GetPredictionMethods(typeDef, out replicateMd, out reconcileMd))
|
||||
return false;
|
||||
|
||||
//If replication methods found but this hierarchy already has max.
|
||||
if (rpcCount >= NetworkBehaviourHelper.MAX_RPC_ALLOWANCE)
|
||||
{
|
||||
base.LogError($"{typeDef.FullName} and inherited types exceed {NetworkBehaviourHelper.MAX_RPC_ALLOWANCE} replicated methods. Only {NetworkBehaviourHelper.MAX_RPC_ALLOWANCE} replicated methods are supported per inheritance hierarchy.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parameterError = false;
|
||||
parameterError |= HasParameterError(replicateMd, typeDef, true);
|
||||
parameterError |= HasParameterError(reconcileMd, typeDef, false);
|
||||
if (parameterError)
|
||||
return false;
|
||||
|
||||
TypeDefinition replicateDataTd = replicateMd.Parameters[0].ParameterType.CachedResolve(base.Session);
|
||||
TypeDefinition reconcileDataTd = reconcileMd.Parameters[0].ParameterType.CachedResolve(base.Session);
|
||||
//Ensure datas implement interfaces.
|
||||
bool interfacesImplemented = true;
|
||||
DataImplementInterfaces(replicateMd, true, ref interfacesImplemented);
|
||||
DataImplementInterfaces(reconcileMd, false, ref interfacesImplemented);
|
||||
if (!interfacesImplemented)
|
||||
return false;
|
||||
if (!TickFieldIsNonSerializable(replicateDataTd, true))
|
||||
return false;
|
||||
if (!TickFieldIsNonSerializable(reconcileDataTd, false))
|
||||
return false;
|
||||
|
||||
/* Make sure data can serialize. Use array type, this will
|
||||
* generate a serializer for element type as well. */
|
||||
bool canSerialize;
|
||||
//Make sure replicate data can serialize.
|
||||
canSerialize = base.GetClass<GeneralHelper>().HasSerializerAndDeserializer(replicateDataTd.MakeArrayType(), true);
|
||||
if (!canSerialize)
|
||||
{
|
||||
base.LogError($"Replicate data type {replicateDataTd.Name} does not support serialization. Use a supported type or create a custom serializer.");
|
||||
return false;
|
||||
}
|
||||
//Make sure reconcile data can serialize.
|
||||
canSerialize = base.GetClass<GeneralHelper>().HasSerializerAndDeserializer(reconcileDataTd, true);
|
||||
if (!canSerialize)
|
||||
{
|
||||
base.LogError($"Reconcile data type {reconcileDataTd.Name} does not support serialization. Use a supported type or create a custom serializer.");
|
||||
return false;
|
||||
}
|
||||
//Creates fields for buffers.
|
||||
CreatedPredictionFields predictionFields;
|
||||
CreateFields(typeDef, replicateMd, reconcileMd, out predictionFields);
|
||||
|
||||
PredictionReaders predictionReaders;
|
||||
MethodDefinition replicateULMd;
|
||||
MethodDefinition reconcileULMd;
|
||||
CreatePredictionMethods(typeDef, replicateMd, reconcileMd, predictionFields, rpcCount, out predictionReaders, out replicateULMd, out reconcileULMd);
|
||||
InitializeCollections(typeDef, replicateMd, predictionFields);
|
||||
InitializeULDelegates(typeDef, predictionFields, replicateMd, reconcileMd, replicateULMd, reconcileULMd);
|
||||
RegisterRpcs(typeDef, rpcCount, predictionReaders);
|
||||
|
||||
rpcCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the tick field for GetTick is non-serializable.
|
||||
/// </summary>
|
||||
/// <param name="dataTd"></param>
|
||||
/// <returns></returns>
|
||||
private bool TickFieldIsNonSerializable(TypeDefinition dataTd, bool replicate)
|
||||
{
|
||||
string methodName = (replicate) ? IReplicateData_GetTick_MethodRef.Name : IReconcileData_GetTick_MethodRef.Name;
|
||||
MethodDefinition getMd = dataTd.GetMethod(methodName);
|
||||
|
||||
//Try to find ldFld.
|
||||
Instruction ldFldInst = null;
|
||||
foreach (Instruction item in getMd.Body.Instructions)
|
||||
{
|
||||
if (item.OpCode == OpCodes.Ldfld)
|
||||
{
|
||||
ldFldInst = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//If ldFld not found.
|
||||
if (ldFldInst == null)
|
||||
{
|
||||
base.LogError($"{dataTd.FullName} method {getMd.Name} does not return a field type for the Tick. Make a new private field of uint type and return it's value within {getMd.Name}.");
|
||||
return false;
|
||||
}
|
||||
//Make sure the field is private.
|
||||
else
|
||||
{
|
||||
FieldDefinition fd = (FieldDefinition)ldFldInst.Operand;
|
||||
if (!fd.Attributes.HasFlag(FieldAttributes.Private))
|
||||
{
|
||||
base.LogError($"{dataTd.FullName} method {getMd.Name} returns a tick field but it's not marked as private. Make the field {fd.Name} private.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//All checks pass.
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DataImplementInterfaces(MethodDefinition methodDef, bool isReplicate, ref bool interfacesImplemented)
|
||||
{
|
||||
TypeReference dataTr = methodDef.Parameters[0].ParameterType;
|
||||
string interfaceName = (isReplicate) ? IReplicateData_FullName : IReconcileData_FullName;
|
||||
//If does not implement.
|
||||
if (!dataTr.CachedResolve(base.Session).ImplementsInterfaceRecursive(base.Session, interfaceName))
|
||||
{
|
||||
string name = (isReplicate) ? typeof(IReplicateData).Name : typeof(IReconcileData).Name;
|
||||
base.LogError($"Prediction data type {dataTr.Name} for method {methodDef.Name} in class {methodDef.DeclaringType.Name} must implement the {name} interface.");
|
||||
interfacesImplemented = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers RPCs that prediction uses.
|
||||
/// </summary>
|
||||
private void RegisterRpcs(TypeDefinition typeDef, uint hash, PredictionReaders readers)
|
||||
{
|
||||
MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
ILProcessor processor = injectionMethodDef.Body.GetILProcessor();
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
|
||||
Register(readers.ReplicateReader.CachedResolve(base.Session), true);
|
||||
Register(readers.ReconcileReader.CachedResolve(base.Session), false);
|
||||
|
||||
void Register(MethodDefinition readerMd, bool replicate)
|
||||
{
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldc_I4, (int)hash));
|
||||
/* Create delegate and call NetworkBehaviour method. */
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldftn, readerMd));
|
||||
|
||||
MethodReference ctorMr;
|
||||
MethodReference callMr;
|
||||
if (replicate)
|
||||
{
|
||||
ctorMr = base.GetClass<NetworkBehaviourHelper>().ReplicateRpcDelegateConstructor_MethodRef;
|
||||
callMr = base.GetClass<NetworkBehaviourHelper>().RegisterReplicateRpc_MethodRef;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctorMr = base.GetClass<NetworkBehaviourHelper>().ReconcileRpcDelegateConstructor_MethodRef;
|
||||
callMr = base.GetClass<NetworkBehaviourHelper>().RegisterReconcileRpc_MethodRef;
|
||||
}
|
||||
|
||||
insts.Add(processor.Create(OpCodes.Newobj, ctorMr));
|
||||
insts.Add(processor.Create(OpCodes.Call, callMr));
|
||||
}
|
||||
|
||||
processor.InsertLast(insts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes collection fields made during this process.
|
||||
/// </summary>
|
||||
/// <param name="predictionFields"></param>
|
||||
private void InitializeCollections(TypeDefinition typeDef, MethodDefinition replicateMd, CreatedPredictionFields predictionFields)
|
||||
{
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType;
|
||||
MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
ILProcessor processor = injectionMethodDef.Body.GetILProcessor();
|
||||
|
||||
Generate(predictionFields.ClientReplicateDatas, true);
|
||||
Generate(predictionFields.ServerReplicateDatas, false);
|
||||
|
||||
void Generate(FieldReference fr, bool isList)
|
||||
{
|
||||
MethodDefinition ctorMd = base.GetClass<GeneralHelper>().List_TypeRef.CachedResolve(base.Session).GetConstructor();
|
||||
GenericInstanceType collectionGit;
|
||||
if (isList)
|
||||
gh.GetGenericLists(replicateDataTr, out collectionGit);
|
||||
else
|
||||
gh.GetGenericQueues(replicateDataTr, out collectionGit);
|
||||
MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, collectionGit);
|
||||
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Newobj, ctorMr));
|
||||
insts.Add(processor.Create(OpCodes.Stfld, fr));
|
||||
processor.InsertFirst(insts);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes collection fields made during this process.
|
||||
/// </summary>
|
||||
/// <param name="predictionFields"></param>
|
||||
private void InitializeULDelegates(TypeDefinition typeDef, CreatedPredictionFields predictionFields, MethodDefinition replicateMd, MethodDefinition reconcileMd, MethodDefinition replicateULMd, MethodDefinition reconcileULMd)
|
||||
{
|
||||
TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType;
|
||||
TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType;
|
||||
MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
ILProcessor processor = injectionMethodDef.Body.GetILProcessor();
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
|
||||
Generate(replicateULMd, replicateDataTr, predictionFields.ReplicateULDelegate, typeof(ReplicateUserLogicDelegate<>), ReplicateULDelegate_TypeRef);
|
||||
Generate(reconcileULMd, reconcileDataTr, predictionFields.ReconcileULDelegate, typeof(ReconcileUserLogicDelegate<>), ReconcileULDelegate_TypeRef);
|
||||
|
||||
void Generate(MethodDefinition ulMd, TypeReference dataTr, FieldReference fr, System.Type delegateType, TypeReference delegateTr)
|
||||
{
|
||||
insts.Clear();
|
||||
|
||||
MethodDefinition ctorMd = delegateTr.CachedResolve(base.Session).GetFirstConstructor(base.Session, true);
|
||||
GenericInstanceType collectionGit;
|
||||
GetGenericULDelegate(dataTr, delegateType, out collectionGit);
|
||||
MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, collectionGit);
|
||||
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldftn, ulMd));
|
||||
insts.Add(processor.Create(OpCodes.Newobj, ctorMr));
|
||||
insts.Add(processor.Create(OpCodes.Stfld, fr));
|
||||
processor.InsertFirst(insts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates field buffers for replicate datas.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="replicateMd"></param>
|
||||
/// <param name=""></param>
|
||||
/// <returns></returns>
|
||||
private void CreateFields(TypeDefinition typeDef, MethodDefinition replicateMd, MethodDefinition reconcileMd, out CreatedPredictionFields predictionFields)
|
||||
{
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType;
|
||||
TypeReference replicateDataArrTr = replicateDataTr.MakeArrayType();
|
||||
TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType;
|
||||
|
||||
GenericInstanceType replicateULDelegateGit;
|
||||
GenericInstanceType reconcileULDelegateGit;
|
||||
GenericInstanceType lstDataGit;
|
||||
GenericInstanceType queueDataGit;
|
||||
GetGenericULDelegate(replicateDataTr, typeof(ReplicateUserLogicDelegate<>), out replicateULDelegateGit);
|
||||
GetGenericULDelegate(reconcileDataTr, typeof(ReconcileUserLogicDelegate<>), out reconcileULDelegateGit);
|
||||
gh.GetGenericLists(replicateDataTr, out lstDataGit);
|
||||
gh.GetGenericQueues(replicateDataTr, out queueDataGit);
|
||||
|
||||
/* Data buffer. */
|
||||
FieldDefinition replicateULDelegateFd = new FieldDefinition($"_replicateULDelegate___{replicateMd.Name}", FieldAttributes.Private, replicateULDelegateGit);
|
||||
FieldDefinition reconcileULDelegateFd = new FieldDefinition($"_reconcileULDelegate___{reconcileMd.Name}", FieldAttributes.Private, reconcileULDelegateGit);
|
||||
FieldDefinition serverReplicatesFd = new FieldDefinition($"_serverReplicates___{replicateMd.Name}", FieldAttributes.Private, queueDataGit);
|
||||
FieldDefinition clientReplicatesFd = new FieldDefinition($"_clientReplicates___{replicateMd.Name}", FieldAttributes.Private, lstDataGit);
|
||||
FieldDefinition reconcileDataFd = new FieldDefinition($"_reconcileData___{replicateMd.Name}", FieldAttributes.Private, reconcileDataTr);
|
||||
FieldDefinition serverReplicatesReadBufferFd = new FieldDefinition($"{replicateMd.Name}___serverReplicateReadBuffer", FieldAttributes.Private, replicateDataArrTr);
|
||||
|
||||
typeDef.Fields.Add(replicateULDelegateFd);
|
||||
typeDef.Fields.Add(reconcileULDelegateFd);
|
||||
typeDef.Fields.Add(serverReplicatesFd);
|
||||
typeDef.Fields.Add(clientReplicatesFd);
|
||||
typeDef.Fields.Add(reconcileDataFd);
|
||||
typeDef.Fields.Add(serverReplicatesReadBufferFd);
|
||||
|
||||
predictionFields = new CreatedPredictionFields(replicateULDelegateFd, reconcileULDelegateFd, serverReplicatesFd, clientReplicatesFd, reconcileDataFd,
|
||||
serverReplicatesReadBufferFd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if there are any errors with the prediction methods parameters and will print if so.
|
||||
/// </summary>
|
||||
private bool HasParameterError(MethodDefinition methodDef, TypeDefinition typeDef, bool replicateMethod)
|
||||
{
|
||||
//Replicate: data, asServer, channel, replaying.
|
||||
//Reconcile: data, asServer, channel.
|
||||
int count = (replicateMethod) ? 4 : 3;
|
||||
|
||||
//Check parameter count.
|
||||
if (methodDef.Parameters.Count != count)
|
||||
{
|
||||
PrintParameterExpectations();
|
||||
return true;
|
||||
}
|
||||
|
||||
//Data check.
|
||||
if (!methodDef.Parameters[0].ParameterType.IsClassOrStruct(base.Session))
|
||||
{
|
||||
base.LogError($"Prediction methods must use a class or structure as the first parameter type. Structures are recommended to avoid allocations.");
|
||||
return true;
|
||||
}
|
||||
//asServer
|
||||
if (methodDef.Parameters[1].ParameterType.Name != typeof(bool).Name)
|
||||
{
|
||||
PrintParameterExpectations();
|
||||
return true;
|
||||
}
|
||||
//Channel.
|
||||
if (methodDef.Parameters[2].ParameterType.Name != typeof(Channel).Name)
|
||||
{
|
||||
PrintParameterExpectations();
|
||||
return true;
|
||||
}
|
||||
if (replicateMethod)
|
||||
{
|
||||
//replaying
|
||||
if (methodDef.Parameters[3].ParameterType.Name != typeof(bool).Name)
|
||||
{
|
||||
PrintParameterExpectations();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PrintParameterExpectations()
|
||||
{
|
||||
if (replicateMethod)
|
||||
base.LogError($"Replicate method {methodDef.Name} within {typeDef.Name} requires exactly {count} parameters. In order: replicate data, asServer boolean, channel = Channel.Unreliable, replaying boolean.");
|
||||
else
|
||||
base.LogError($"Reconcile method {methodDef.Name} within {typeDef.Name} requires exactly {count} parameters. In order: replicate data, asServer boolean, channel = Channel.Unreliable.");
|
||||
}
|
||||
|
||||
//No errors with parameters.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates all methods needed for a RPC.
|
||||
/// </summary>
|
||||
/// <param name="originalMethodDef"></param>
|
||||
/// <param name="rpcAttribute"></param>
|
||||
/// <returns></returns>
|
||||
private bool CreatePredictionMethods(TypeDefinition typeDef, MethodDefinition replicateMd, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, uint rpcCount, out PredictionReaders predictionReaders, out MethodDefinition replicateULMd, out MethodDefinition reconcileULMd)
|
||||
{
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
NetworkBehaviourHelper nbh = base.GetClass<NetworkBehaviourHelper>();
|
||||
predictionReaders = null;
|
||||
|
||||
string copySuffix = "___UL";
|
||||
replicateULMd = base.GetClass<GeneralHelper>().CopyIntoNewMethod(replicateMd, $"{replicateMd.Name}{copySuffix}", out _);
|
||||
reconcileULMd = base.GetClass<GeneralHelper>().CopyIntoNewMethod(reconcileMd, $"{reconcileMd.Name}{copySuffix}", out _);
|
||||
replicateMd.Body.Instructions.Clear();
|
||||
reconcileMd.Body.Instructions.Clear();
|
||||
|
||||
MethodDefinition replicateReader;
|
||||
MethodDefinition reconcileReader;
|
||||
|
||||
if (!CreateReplicate())
|
||||
return false;
|
||||
if (!CreateReconcile())
|
||||
return false;
|
||||
|
||||
CreateClearReplicateCacheMethod(typeDef, replicateMd.Parameters[0].ParameterType, predictionFields);
|
||||
CreateReplicateReader(typeDef, replicateMd, predictionFields, out replicateReader);
|
||||
CreateReconcileReader(typeDef, reconcileMd, predictionFields, out reconcileReader);
|
||||
predictionReaders = new PredictionReaders(replicateReader, reconcileReader);
|
||||
|
||||
bool CreateReplicate()
|
||||
{
|
||||
ILProcessor processor = replicateMd.Body.GetILProcessor();
|
||||
ParameterDefinition replicateDataPd = replicateMd.Parameters[0];
|
||||
MethodDefinition comparerMd = gh.CreateEqualityComparer(replicateDataPd.ParameterType);
|
||||
gh.CreateIsDefaultComparer(replicateDataPd.ParameterType, comparerMd);
|
||||
ParameterDefinition asServerPd = replicateMd.Parameters[1];
|
||||
ParameterDefinition replayingPd = replicateMd.Parameters[3];
|
||||
|
||||
Instruction exitMethodInst = processor.Create(OpCodes.Nop);
|
||||
//Exit early conditions.
|
||||
processor.Emit(OpCodes.Ldarg_0); //base.
|
||||
processor.Emit(OpCodes.Ldarg, asServerPd);
|
||||
processor.Emit(OpCodes.Ldarg, replayingPd);
|
||||
processor.Emit(OpCodes.Call, base.GetClass<NetworkBehaviourHelper>().Replicate_ExitEarly_A_MethodRef);
|
||||
processor.Emit(OpCodes.Brtrue, exitMethodInst);
|
||||
|
||||
//Wrap server content in an asServer if statement.
|
||||
Instruction notAsServerInst = processor.Create(OpCodes.Nop);
|
||||
processor.Emit(OpCodes.Ldarg, asServerPd);
|
||||
processor.Emit(OpCodes.Brfalse, notAsServerInst);
|
||||
/***************************/
|
||||
ServerCreateReplicate(replicateMd, predictionFields);
|
||||
processor.Emit(OpCodes.Br, exitMethodInst);
|
||||
/***************************/
|
||||
|
||||
//Wrap client content in an !asServer if statement.
|
||||
processor.Append(notAsServerInst);
|
||||
/***************************/
|
||||
ClientCreateReplicate(replicateMd, predictionFields, rpcCount);
|
||||
/***************************/
|
||||
|
||||
processor.Append(exitMethodInst);
|
||||
processor.Emit(OpCodes.Ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CreateReconcile()
|
||||
{
|
||||
ILProcessor processor = reconcileMd.Body.GetILProcessor();
|
||||
ParameterDefinition reconcileDataPd = reconcileMd.Parameters[0];
|
||||
ParameterDefinition asServerPd = reconcileMd.Parameters[1];
|
||||
ParameterDefinition channelPd = reconcileMd.Parameters[2];
|
||||
TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType;
|
||||
|
||||
//ExitEarly A.
|
||||
Instruction exitMethodInst = processor.Create(OpCodes.Nop);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldarg, asServerPd);
|
||||
processor.Emit(OpCodes.Ldarga, channelPd);
|
||||
processor.Emit(OpCodes.Call, base.GetClass<NetworkBehaviourHelper>().Reconcile_ExitEarly_A_MethodRef);
|
||||
processor.Emit(OpCodes.Brtrue, exitMethodInst);
|
||||
|
||||
//Wrap server content in an asServer if statement.
|
||||
Instruction notAsServerInst = processor.Create(OpCodes.Nop);
|
||||
processor.Emit(OpCodes.Ldarg, asServerPd);
|
||||
processor.Emit(OpCodes.Brfalse, notAsServerInst);
|
||||
/***************************/
|
||||
ServerCreateReconcile(reconcileMd, predictionFields, ref rpcCount);
|
||||
/***************************/
|
||||
processor.Emit(OpCodes.Br, exitMethodInst);
|
||||
|
||||
processor.Append(notAsServerInst);
|
||||
|
||||
MethodReference reconcileClientGim = nbh.Reconcile_Client_MethodRef.GetMethodReference(
|
||||
base.Session, new TypeReference[] { reconcileDataPd.ParameterType, replicateDataTr });
|
||||
//<T>(ReplicateULDelegate<T> replicateDel, ReconcileULDelegate<T> reconcileDel, List<T> collection,
|
||||
//T data, Channel channel) where T : IReconcileData
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileULDelegate);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateULDelegate);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ClientReplicateDatas);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileData);
|
||||
processor.Emit(OpCodes.Ldarg, channelPd);
|
||||
processor.Emit(OpCodes.Call, reconcileClientGim);
|
||||
|
||||
processor.Append(exitMethodInst);
|
||||
processor.Emit(OpCodes.Ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Universal prediction.
|
||||
/// <summary>
|
||||
/// Creates an override for the method responsible for resetting replicates.
|
||||
/// </summary>
|
||||
/// <param name=""></param>
|
||||
/// <param name=""></param>
|
||||
private void CreateClearReplicateCacheMethod(TypeDefinition typeDef, TypeReference dataTr, CreatedPredictionFields predictionFields)
|
||||
{
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
string clearDatasName = base.GetClass<NetworkBehaviourHelper>().ClearReplicateCache_Method_Name;
|
||||
MethodDefinition md = typeDef.GetMethod(clearDatasName);
|
||||
|
||||
//Already exist when it shouldn't.
|
||||
if (md != null)
|
||||
{
|
||||
base.LogWarning($"{typeDef.Name} overrides method {md.Name} when it should not. Logic within {md.Name} will be replaced by code generation.");
|
||||
md.Body.Instructions.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
md = new MethodDefinition(clearDatasName, (MethodAttributes.Public | MethodAttributes.Virtual), base.Module.TypeSystem.Void);
|
||||
gh.CreateParameter(md, typeof(bool), "asServer");
|
||||
typeDef.Methods.Add(md);
|
||||
base.ImportReference(md);
|
||||
}
|
||||
|
||||
ILProcessor processor = md.Body.GetILProcessor();
|
||||
|
||||
GenericInstanceType dataListGit;
|
||||
gh.GetGenericLists(dataTr, out dataListGit);
|
||||
//Get clear method.
|
||||
MethodReference lstClearMr = gh.List_Clear_MethodRef.MakeHostInstanceGeneric(base.Session, dataListGit);
|
||||
ParameterDefinition asServerPd = md.Parameters[0];
|
||||
|
||||
Instruction afterAsServerInst = processor.Create(OpCodes.Nop);
|
||||
Instruction resetTicksInst = processor.Create(OpCodes.Nop);
|
||||
|
||||
processor.Emit(OpCodes.Ldarg, asServerPd);
|
||||
processor.Emit(OpCodes.Brfalse_S, afterAsServerInst);
|
||||
|
||||
//Clear on server replicates.
|
||||
MethodReference clrQueueMr = base.ImportReference(typeof(NetworkBehaviour).GetMethod(nameof(NetworkBehaviour.ClearQueue_Server_Internal)));
|
||||
GenericInstanceMethod clrQueueGim = clrQueueMr.MakeGenericMethod(new TypeReference[] { dataTr });
|
||||
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateDatas);
|
||||
processor.Emit(clrQueueMr.GetCallOpCode(base.Session), clrQueueGim);
|
||||
processor.Emit(OpCodes.Br_S, resetTicksInst);
|
||||
processor.Append(afterAsServerInst);
|
||||
//Clear on client replicates.
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ClientReplicateDatas);
|
||||
processor.Emit(lstClearMr.GetCallOpCode(base.Session), lstClearMr);
|
||||
|
||||
processor.Append(resetTicksInst);
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs generic ReplicateULDelegate for dataTr.
|
||||
/// </summary>
|
||||
private void GetGenericULDelegate(TypeReference dataTr, System.Type delegateType, out GenericInstanceType git)
|
||||
{
|
||||
TypeReference delDataTr = base.ImportReference(delegateType);
|
||||
git = delDataTr.MakeGenericInstanceType(new TypeReference[] { dataTr });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts 1 from a field.
|
||||
/// </summary>
|
||||
private List<Instruction> SubtractFromField(MethodDefinition methodDef, FieldDefinition fieldDef)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
// _field--;
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldfld, fieldDef));
|
||||
insts.Add(processor.Create(OpCodes.Ldc_I4_1));
|
||||
insts.Add(processor.Create(OpCodes.Sub));
|
||||
insts.Add(processor.Create(OpCodes.Stfld, fieldDef));
|
||||
|
||||
return insts;
|
||||
}
|
||||
/// <summary>
|
||||
/// Subtracts 1 from a variable.
|
||||
/// </summary>
|
||||
private List<Instruction> SubtractFromVariable(MethodDefinition methodDef, VariableDefinition variableDef)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
// variable--;
|
||||
insts.Add(processor.Create(OpCodes.Ldloc, variableDef));
|
||||
insts.Add(processor.Create(OpCodes.Ldc_I4_1));
|
||||
insts.Add(processor.Create(OpCodes.Sub));
|
||||
insts.Add(processor.Create(OpCodes.Stloc, variableDef));
|
||||
|
||||
return insts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts 1 from a variable.
|
||||
/// </summary>
|
||||
private List<Instruction> SubtractOneVariableFromAnother(MethodDefinition methodDef, VariableDefinition srcVd, VariableDefinition modifierVd)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
// variable -= v2;
|
||||
insts.Add(processor.Create(OpCodes.Ldloc, srcVd));
|
||||
insts.Add(processor.Create(OpCodes.Ldloc, modifierVd));
|
||||
insts.Add(processor.Create(OpCodes.Sub));
|
||||
insts.Add(processor.Create(OpCodes.Stloc, srcVd));
|
||||
|
||||
return insts;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Server side.
|
||||
/// <summary>
|
||||
/// Creates replicate code for client.
|
||||
/// </summary>
|
||||
private void ServerCreateReplicate(MethodDefinition replicateMd, CreatedPredictionFields predictionFields)
|
||||
{
|
||||
ILProcessor processor = replicateMd.Body.GetILProcessor();
|
||||
|
||||
ParameterDefinition replicateDataPd = replicateMd.Parameters[0];
|
||||
ParameterDefinition channelPd = replicateMd.Parameters[2];
|
||||
TypeReference replicateDataTr = replicateDataPd.ParameterType;
|
||||
|
||||
GenericInstanceMethod replicateGim = base.GetClass<NetworkBehaviourHelper>().Replicate_Server_MethodRef.MakeGenericMethod(new TypeReference[] { replicateDataTr });
|
||||
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateULDelegate);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateDatas);
|
||||
processor.Emit(OpCodes.Ldarg, channelPd);
|
||||
processor.Emit(OpCodes.Call, replicateGim);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reader for replicate data received from clients.
|
||||
/// </summary>
|
||||
private bool CreateReplicateReader(TypeDefinition typeDef, MethodDefinition replicateMd, CreatedPredictionFields predictionFields, out MethodDefinition result)
|
||||
{
|
||||
string methodName = $"{REPLICATE_READER_PREFIX}{replicateMd.Name}";
|
||||
MethodDefinition createdMd = new MethodDefinition(methodName,
|
||||
MethodAttributes.Private,
|
||||
replicateMd.Module.TypeSystem.Void);
|
||||
typeDef.Methods.Add(createdMd);
|
||||
createdMd.Body.InitLocals = true;
|
||||
|
||||
ILProcessor processor = createdMd.Body.GetILProcessor();
|
||||
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
NetworkBehaviourHelper nbh = base.GetClass<NetworkBehaviourHelper>();
|
||||
|
||||
TypeReference dataTr = replicateMd.Parameters[0].ParameterType;
|
||||
//Create parameters.
|
||||
ParameterDefinition readerPd = gh.CreateParameter(createdMd, typeof(PooledReader));
|
||||
ParameterDefinition networkConnectionPd = gh.CreateParameter(createdMd, typeof(NetworkConnection));
|
||||
ParameterDefinition channelPd = gh.CreateParameter(createdMd, typeof(Channel));
|
||||
|
||||
MethodReference replicateReaderGim = nbh.Replicate_Reader_MethodRef.GetMethodReference(base.Session, dataTr);
|
||||
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
//Reader, NetworkConnection.
|
||||
processor.Emit(OpCodes.Ldarg, readerPd);
|
||||
processor.Emit(OpCodes.Ldarg, networkConnectionPd);
|
||||
//arrBuffer.
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateReaderBuffer);
|
||||
//replicates.
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ServerReplicateDatas);
|
||||
//Channel.
|
||||
processor.Emit(OpCodes.Ldarg, channelPd);
|
||||
processor.Emit(OpCodes.Call, replicateReaderGim);
|
||||
//Add end of method.
|
||||
processor.Emit(OpCodes.Ret);
|
||||
result = createdMd;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates server side code for reconcileMd.
|
||||
/// </summary>
|
||||
/// <param name="reconcileMd"></param>
|
||||
/// <returns></returns>
|
||||
private void ServerCreateReconcile(MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, ref uint rpcCount)
|
||||
{
|
||||
ParameterDefinition reconcileDataPd = reconcileMd.Parameters[0];
|
||||
ParameterDefinition channelPd = reconcileMd.Parameters[2];
|
||||
ILProcessor processor = reconcileMd.Body.GetILProcessor();
|
||||
|
||||
GenericInstanceMethod methodGim = base.GetClass<NetworkBehaviourHelper>().Reconcile_Server_MethodRef.MakeGenericMethod(new TypeReference[] { reconcileDataPd.ParameterType });
|
||||
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldc_I4, (int)rpcCount);
|
||||
processor.Emit(OpCodes.Ldarg, reconcileDataPd);
|
||||
processor.Emit(OpCodes.Ldarg, channelPd);
|
||||
processor.Emit(OpCodes.Call, methodGim);
|
||||
|
||||
rpcCount++;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Client side.
|
||||
/// <summary>
|
||||
/// Creates replicate code for client.
|
||||
/// </summary>
|
||||
private void ClientCreateReplicate(MethodDefinition replicateMd, CreatedPredictionFields predictionFields, uint rpcCount)
|
||||
{
|
||||
ParameterDefinition dataPd = replicateMd.Parameters[0];
|
||||
ParameterDefinition channelPd = replicateMd.Parameters[2];
|
||||
TypeReference dataTr = dataPd.ParameterType;
|
||||
|
||||
ILProcessor processor = replicateMd.Body.GetILProcessor();
|
||||
|
||||
//Make method reference NB.SendReplicateRpc<dataTr>
|
||||
GenericInstanceMethod replicateClientGim = base.GetClass<NetworkBehaviourHelper>().Replicate_Client_MethodRef.MakeGenericMethod(new TypeReference[] { dataTr });
|
||||
processor.Emit(OpCodes.Ldarg_0);//base.
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateULDelegate);
|
||||
processor.Emit(OpCodes.Ldc_I4, (int)rpcCount);
|
||||
processor.Emit(OpCodes.Ldarg_0);//this.
|
||||
processor.Emit(OpCodes.Ldfld, predictionFields.ClientReplicateDatas.CachedResolve(base.Session));
|
||||
processor.Emit(OpCodes.Ldarg, dataPd);
|
||||
processor.Emit(OpCodes.Ldarg, channelPd);
|
||||
processor.Emit(OpCodes.Call, replicateClientGim);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reader for replicate data received from clients.
|
||||
/// </summary>
|
||||
private bool CreateReconcileReader(TypeDefinition typeDef, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields, out MethodDefinition result)
|
||||
{
|
||||
string methodName = $"{RECONCILE_READER_PREFIX}{reconcileMd.Name}";
|
||||
MethodDefinition createdMd = new MethodDefinition(methodName,
|
||||
MethodAttributes.Private,
|
||||
reconcileMd.Module.TypeSystem.Void);
|
||||
typeDef.Methods.Add(createdMd);
|
||||
createdMd.Body.InitLocals = true;
|
||||
|
||||
ILProcessor processor = createdMd.Body.GetILProcessor();
|
||||
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
NetworkBehaviourHelper nbh = base.GetClass<NetworkBehaviourHelper>();
|
||||
|
||||
TypeReference dataTr = reconcileMd.Parameters[0].ParameterType;
|
||||
//Create parameters.
|
||||
ParameterDefinition readerPd = gh.CreateParameter(createdMd, typeof(PooledReader));
|
||||
ParameterDefinition channelPd = gh.CreateParameter(createdMd, typeof(Channel));
|
||||
|
||||
MethodReference methodGim = nbh.Reconcile_Reader_MethodRef.GetMethodReference(base.Session, dataTr);
|
||||
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
//Reader, data, channel.
|
||||
processor.Emit(OpCodes.Ldarg, readerPd);
|
||||
//Data to assign read value to.
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldflda, predictionFields.ReconcileData);
|
||||
//Channel.
|
||||
processor.Emit(OpCodes.Ldarg, channelPd);
|
||||
processor.Emit(OpCodes.Call, methodGim);
|
||||
//Add end of method.
|
||||
processor.Emit(OpCodes.Ret);
|
||||
|
||||
result = createdMd;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0663606e86b0b34bae85df164747e72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user