The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [2.0.0-exp.2] - 2024-04-02 ### Added - Added updates to all internal messages to account for a distributed authority network session connection. (#2863) - Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion. (#2863) - For a customized `NetworkRigidbodyBase` class: - `NetworkRigidbodyBase.AutoUpdateKinematicState` provides control on whether the kinematic setting will be automatically set or not when ownership changes. - `NetworkRigidbodyBase.AutoSetKinematicOnDespawn` provides control on whether isKinematic will automatically be set to true when the associated `NetworkObject` is despawned. - `NetworkRigidbodyBase.Initialize` is a protected method that, when invoked, will initialize the instance. This includes options to: - Set whether using a `RigidbodyTypes.Rigidbody` or `RigidbodyTypes.Rigidbody2D`. - Includes additional optional parameters to set the `NetworkTransform`, `Rigidbody`, and `Rigidbody2d` to use. - Provides additional public methods: - `NetworkRigidbodyBase.GetPosition` to return the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.GetRotation` to return the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.MovePosition` to move to the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.MoveRotation` to move to the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.SetPosition` to set the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.SetRotation` to set the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.ApplyCurrentTransform` to set the position and rotation of the `Rigidbody` or `Rigidbody2d` based on the associated `GameObject` transform (depending upon its initialized setting). - `NetworkRigidbodyBase.WakeIfSleeping` to wake up the rigid body if sleeping. - `NetworkRigidbodyBase.SleepRigidbody` to put the rigid body to sleep. - `NetworkRigidbodyBase.IsKinematic` to determine if the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) is currently kinematic. - `NetworkRigidbodyBase.SetIsKinematic` to set the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) current kinematic state. - `NetworkRigidbodyBase.ResetInterpolation` to reset the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) back to its original interpolation value when initialized. - Now includes a `MonoBehaviour.FixedUpdate` implementation that will update the assigned `NetworkTransform` when `NetworkRigidbodyBase.UseRigidBodyForMotion` is true. (#2863) - Added `RigidbodyContactEventManager` that provides a more optimized way to process collision enter and collision stay events as opposed to the `Monobehaviour` approach. (#2863) - Can be used in client-server and distributed authority modes, but is particularly useful in distributed authority. - Added rigid body motion updates to `NetworkTransform` which allows users to set interolation on rigid bodies. (#2863) - Extrapolation is only allowed on authoritative instances, but custom class derived from `NetworkRigidbodyBase` or `NetworkRigidbody` or `NetworkRigidbody2D` automatically switches non-authoritative instances to interpolation if set to extrapolation. - Added distributed authority mode support to `NetworkAnimator`. (#2863) - Added session mode selection to `NetworkManager` inspector view. (#2863) - Added distributed authority permissions feature. (#2863) - Added distributed authority mode specific `NetworkObject` permissions flags (Distributable, Transferable, and RequestRequired). (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipStatus` method that applies one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) - Added distributed authority mode specific `NetworkObject.RemoveOwnershipStatus` method that removes one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) - Added distributed authority mode specific `NetworkObject.HasOwnershipStatus` method that will return (true or false) whether one or more ownership flags is set. (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipLock` method that locks ownership of a `NetworkObject` to prevent ownership from changing until the current owner releases the lock. (#2863) - Added distributed authority mode specific `NetworkObject.RequestOwnership` method that sends an ownership request to the current owner of a spawned `NetworkObject` instance. (#2863) - Added distributed authority mode specific `NetworkObject.OnOwnershipRequested` callback handler that is invoked on the owner/authoritative side when a non-owner requests ownership. Depending upon the boolean returned value depends upon whether the request is approved or denied. (#2863) - Added distributed authority mode specific `NetworkObject.OnOwnershipRequestResponse` callback handler that is invoked when a non-owner's request has been processed. This callback includes a `NetworkObjet.OwnershipRequestResponseStatus` response parameter that describes whether the request was approved or the reason why it was not approved. (#2863) - Added distributed authority mode specific `NetworkObject.DeferDespawn` method that defers the despawning of `NetworkObject` instances on non-authoritative clients based on the tick offset parameter. (#2863) - Added distributed authority mode specific `NetworkObject.OnDeferredDespawnComplete` callback handler that can be used to further control when deferring the despawning of a `NetworkObject` on non-authoritative instances. (#2863) - Added `NetworkClient.SessionModeType` as one way to determine the current session mode of the network session a client is connected to. (#2863) - Added distributed authority mode specific `NetworkClient.IsSessionOwner` property to determine if the current local client is the current session owner of a distributed authority session. (#2863) - Added distributed authority mode specific client side spawning capabilities. When running in distributed authority mode, clients can instantiate and spawn `NetworkObject` instances (the local client is authomatically the owner of the spawned object). (#2863) - This is useful to better visually synchronize owner authoritative motion models and newly spawned `NetworkObject` instances (i.e. projectiles for example). - Added distributed authority mode specific client side player spawning capabilities. Clients will automatically spawn their associated player object locally. (#2863) - Added distributed authority mode specific `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property (default is true) to provide control over the automatic spawning of player prefabs on the local client side. (#2863) - Added distributed authority mode specific `NetworkManager.OnFetchLocalPlayerPrefabToSpawn` callback that, when assigned, will allow the local client to provide the player prefab to be spawned for the local client. (#2863) - This is only invoked if the `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property is set to true. - Added distributed authority mode specific `NetworkBehaviour.HasAuthority` property that determines if the local client has authority over the associated `NetworkObject` instance (typical use case is within a `NetworkBehaviour` script much like that of `IsServer` or `IsClient`). (#2863) - Added distributed authority mode specific `NetworkBehaviour.IsSessionOwner` property that determines if the local client is the session owner (typical use case would be to determine if the local client can has scene management authority within a `NetworkBehaviour` script). (#2863) - Added support for distributed authority mode scene management where the currently assigned session owner can start scene events (i.e. scene loading and scene unloading). (#2863) ### Fixed - Fixed issue where the host was not invoking `OnClientDisconnectCallback` for its own local client when internally shutting down. (#2822) - Fixed issue where NetworkTransform could potentially attempt to "unregister" a named message prior to it being registered. (#2807) - Fixed issue where in-scene placed `NetworkObject`s with complex nested children `NetworkObject`s (more than one child in depth) would not synchronize properly if WorldPositionStays was set to true. (#2796) ### Changed - Changed client side awareness of other clients is now the same as a server or host. (#2863) - Changed `NetworkManager.ConnectedClients` can now be accessed by both server and clients. (#2863) - Changed `NetworkManager.ConnectedClientsList` can now be accessed by both server and clients. (#2863) - Changed `NetworkTransform` defaults to owner authoritative when connected to a distributed authority session. (#2863) - Changed `NetworkVariable` defaults to owner write and everyone read permissions when connected to a distributed authority session (even if declared with server read or write permissions). (#2863) - Changed `NetworkObject` no longer implements the `MonoBehaviour.Update` method in order to determine whether a `NetworkObject` instance has been migrated to a different scene. Instead, only `NetworkObjects` with the `SceneMigrationSynchronization` property set will be updated internally during the `NetworkUpdateStage.PostLateUpdate` by `NetworkManager`. (#2863) - Changed `NetworkManager` inspector view layout where properties are now organized by category. (#2863) - Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) - Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807)
312 lines
15 KiB
C#
312 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using Mono.Cecil.Rocks;
|
|
using Unity.CompilationPipeline.Common.Diagnostics;
|
|
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
|
using UnityEngine;
|
|
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
|
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
|
|
|
namespace Unity.Netcode.Editor.CodeGen
|
|
{
|
|
internal sealed class INetworkMessageILPP : ILPPInterface
|
|
{
|
|
public override ILPPInterface GetInstance() => this;
|
|
|
|
public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiledAssembly.Name == CodeGenHelpers.RuntimeAssemblyName || compiledAssembly.Name == CodeGenHelpers.ComponentsAssemblyName;
|
|
|
|
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
|
|
|
|
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
|
|
{
|
|
if (!WillProcess(compiledAssembly))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
m_Diagnostics.Clear();
|
|
|
|
// read
|
|
var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly, out m_AssemblyResolver);
|
|
if (assemblyDefinition == null)
|
|
{
|
|
m_Diagnostics.AddError($"Cannot read assembly definition: {compiledAssembly.Name}");
|
|
return null;
|
|
}
|
|
|
|
// modules
|
|
(_, m_NetcodeModule) = CodeGenHelpers.FindBaseModules(assemblyDefinition, m_AssemblyResolver);
|
|
|
|
if (m_NetcodeModule == null)
|
|
{
|
|
m_Diagnostics.AddError($"Cannot find Netcode module: {CodeGenHelpers.NetcodeModuleName}");
|
|
return null;
|
|
}
|
|
|
|
// process
|
|
var mainModule = assemblyDefinition.MainModule;
|
|
if (mainModule != null)
|
|
{
|
|
if (ImportReferences(mainModule))
|
|
{
|
|
var types = mainModule.GetTypes()
|
|
.Where(t => t.Resolve().HasInterface(CodeGenHelpers.INetworkMessage_FullName) && !t.Resolve().IsAbstract)
|
|
.ToList();
|
|
// process `INetworkMessage` types
|
|
if (types.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
CreateModuleInitializer(assemblyDefinition, types);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_Diagnostics.AddError((e.ToString() + e.StackTrace).Replace("\n", "|").Replace("\r", "|"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Diagnostics.AddError($"Cannot import references into main module: {mainModule.Name}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Diagnostics.AddError($"Cannot get main module from assembly definition: {compiledAssembly.Name}");
|
|
}
|
|
|
|
mainModule.RemoveRecursiveReferences();
|
|
|
|
// write
|
|
var pe = new MemoryStream();
|
|
var pdb = new MemoryStream();
|
|
|
|
var writerParameters = new WriterParameters
|
|
{
|
|
SymbolWriterProvider = new PortablePdbWriterProvider(),
|
|
SymbolStream = pdb,
|
|
WriteSymbols = true
|
|
};
|
|
|
|
assemblyDefinition.Write(pe, writerParameters);
|
|
|
|
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
|
}
|
|
|
|
private ModuleDefinition m_NetcodeModule;
|
|
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
|
|
|
private MethodReference m_RuntimeInitializeOnLoadAttribute_Ctor;
|
|
|
|
private MethodReference m_MessageManager_ReceiveMessage_MethodRef;
|
|
private MethodReference m_MessageManager_CreateMessageAndGetVersion_MethodRef;
|
|
private TypeReference m_MessageManager_MessageWithHandler_TypeRef;
|
|
private MethodReference m_MessageManager_MessageHandler_Constructor_TypeRef;
|
|
private MethodReference m_MessageManager_VersionGetter_Constructor_TypeRef;
|
|
private FieldReference m_ILPPMessageProvider___network_message_types_FieldRef;
|
|
private FieldReference m_MessageManager_MessageWithHandler_MessageType_FieldRef;
|
|
private FieldReference m_MessageManager_MessageWithHandler_Handler_FieldRef;
|
|
private FieldReference m_MessageManager_MessageWithHandler_GetVersion_FieldRef;
|
|
private MethodReference m_Type_GetTypeFromHandle_MethodRef;
|
|
private MethodReference m_List_Add_MethodRef;
|
|
|
|
private const string k_ReceiveMessageName = nameof(NetworkMessageManager.ReceiveMessage);
|
|
private const string k_CreateMessageAndGetVersionName = nameof(NetworkMessageManager.CreateMessageAndGetVersion);
|
|
|
|
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
|
{
|
|
// Different environments seem to have different situations...
|
|
// Some have these definitions in netstandard.dll, some seem to have them elsewhere...
|
|
// Since they're standard .net classes they're not going to cause the same issues as referencing other assemblies,
|
|
// in theory, since the definitions should be standard and consistent across platforms
|
|
// (i.e., there's no #if UNITY_EDITOR in them that could create invalid IL code)
|
|
TypeDefinition typeTypeDef = moduleDefinition.ImportReference(typeof(Type)).Resolve();
|
|
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
|
m_RuntimeInitializeOnLoadAttribute_Ctor = moduleDefinition.ImportReference(typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new Type[] { }));
|
|
|
|
TypeDefinition messageHandlerTypeDef = null;
|
|
TypeDefinition versionGetterTypeDef = null;
|
|
TypeDefinition messageWithHandlerTypeDef = null;
|
|
TypeDefinition ilppMessageProviderTypeDef = null;
|
|
TypeDefinition messageManagerSystemTypeDef = null;
|
|
foreach (var netcodeTypeDef in m_NetcodeModule.GetAllTypes())
|
|
{
|
|
if (messageHandlerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.MessageHandler))
|
|
{
|
|
messageHandlerTypeDef = netcodeTypeDef;
|
|
continue;
|
|
}
|
|
|
|
if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.VersionGetter))
|
|
{
|
|
versionGetterTypeDef = netcodeTypeDef;
|
|
continue;
|
|
}
|
|
|
|
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.MessageWithHandler))
|
|
{
|
|
messageWithHandlerTypeDef = netcodeTypeDef;
|
|
continue;
|
|
}
|
|
|
|
if (ilppMessageProviderTypeDef == null && netcodeTypeDef.Name == nameof(ILPPMessageProvider))
|
|
{
|
|
ilppMessageProviderTypeDef = netcodeTypeDef;
|
|
continue;
|
|
}
|
|
|
|
if (messageManagerSystemTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager))
|
|
{
|
|
messageManagerSystemTypeDef = netcodeTypeDef;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
m_MessageManager_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
|
|
m_MessageManager_VersionGetter_Constructor_TypeRef = moduleDefinition.ImportReference(versionGetterTypeDef.GetConstructors().First());
|
|
|
|
m_MessageManager_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
|
|
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
|
|
{
|
|
switch (fieldDef.Name)
|
|
{
|
|
case nameof(NetworkMessageManager.MessageWithHandler.MessageType):
|
|
m_MessageManager_MessageWithHandler_MessageType_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
|
break;
|
|
case nameof(NetworkMessageManager.MessageWithHandler.Handler):
|
|
m_MessageManager_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
|
break;
|
|
case nameof(NetworkMessageManager.MessageWithHandler.GetVersion):
|
|
m_MessageManager_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (var methodDef in typeTypeDef.Methods)
|
|
{
|
|
switch (methodDef.Name)
|
|
{
|
|
case nameof(Type.GetTypeFromHandle):
|
|
m_Type_GetTypeFromHandle_MethodRef = moduleDefinition.ImportReference(methodDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (var fieldDef in ilppMessageProviderTypeDef.Fields)
|
|
{
|
|
switch (fieldDef.Name)
|
|
{
|
|
case nameof(ILPPMessageProvider.__network_message_types):
|
|
m_ILPPMessageProvider___network_message_types_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (var methodDef in listTypeDef.Methods)
|
|
{
|
|
switch (methodDef.Name)
|
|
{
|
|
case "Add":
|
|
m_List_Add_MethodRef = methodDef;
|
|
m_List_Add_MethodRef.DeclaringType = listTypeDef.MakeGenericInstanceType(messageWithHandlerTypeDef);
|
|
m_List_Add_MethodRef = moduleDefinition.ImportReference(m_List_Add_MethodRef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (var methodDef in messageManagerSystemTypeDef.Methods)
|
|
{
|
|
switch (methodDef.Name)
|
|
{
|
|
case k_ReceiveMessageName:
|
|
m_MessageManager_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
|
|
break;
|
|
case k_CreateMessageAndGetVersionName:
|
|
m_MessageManager_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod, MethodReference versionMethod)
|
|
{
|
|
// NetworkMessageManager.__network_message_types.Add(new NetworkMessageManager.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
|
|
processor.Body.Variables.Add(new VariableDefinition(m_MessageManager_MessageWithHandler_TypeRef));
|
|
int messageWithHandlerLocIdx = processor.Body.Variables.Count - 1;
|
|
|
|
instructions.Add(processor.Create(OpCodes.Ldsfld, m_ILPPMessageProvider___network_message_types_FieldRef));
|
|
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
|
instructions.Add(processor.Create(OpCodes.Initobj, m_MessageManager_MessageWithHandler_TypeRef));
|
|
|
|
// tmp.MessageType = typeof(type);
|
|
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
|
instructions.Add(processor.Create(OpCodes.Ldtoken, type));
|
|
instructions.Add(processor.Create(OpCodes.Call, m_Type_GetTypeFromHandle_MethodRef));
|
|
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_MessageType_FieldRef));
|
|
|
|
// tmp.Handler = MessageHandler.ReceveMessage<type>
|
|
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
|
instructions.Add(processor.Create(OpCodes.Ldnull));
|
|
|
|
instructions.Add(processor.Create(OpCodes.Ldftn, receiveMethod));
|
|
instructions.Add(processor.Create(OpCodes.Newobj, m_MessageManager_MessageHandler_Constructor_TypeRef));
|
|
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_Handler_FieldRef));
|
|
|
|
|
|
// tmp.GetVersion = MessageHandler.CreateMessageAndGetVersion<type>
|
|
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
|
instructions.Add(processor.Create(OpCodes.Ldnull));
|
|
|
|
instructions.Add(processor.Create(OpCodes.Ldftn, versionMethod));
|
|
instructions.Add(processor.Create(OpCodes.Newobj, m_MessageManager_VersionGetter_Constructor_TypeRef));
|
|
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_GetVersion_FieldRef));
|
|
|
|
// ILPPMessageProvider.__network_message_types.Add(tmp);
|
|
instructions.Add(processor.Create(OpCodes.Ldloc, messageWithHandlerLocIdx));
|
|
instructions.Add(processor.Create(OpCodes.Callvirt, m_List_Add_MethodRef));
|
|
}
|
|
|
|
// Creates a static module constructor (which is executed when the module is loaded) that registers all the message types in the assembly with NetworkMessageManager.
|
|
// This is the same behavior as annotating a static method with [ModuleInitializer] in standardized C# (that attribute doesn't exist in Unity, but the static module constructor still works).
|
|
// https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-5.0
|
|
// https://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
|
|
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefinition> networkMessageTypes)
|
|
{
|
|
var typeDefinition = new TypeDefinition("__GEN", "INetworkMessageHelper", TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, assembly.MainModule.TypeSystem.Object);
|
|
|
|
var staticCtorMethodDef = new MethodDefinition(
|
|
$"InitializeMessages",
|
|
MethodAttributes.Assembly |
|
|
MethodAttributes.Static,
|
|
assembly.MainModule.TypeSystem.Void);
|
|
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
|
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
|
|
typeDefinition.Methods.Add(staticCtorMethodDef);
|
|
|
|
var instructions = new List<Instruction>();
|
|
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
|
|
|
foreach (var type in networkMessageTypes)
|
|
{
|
|
var receiveMethod = new GenericInstanceMethod(m_MessageManager_ReceiveMessage_MethodRef);
|
|
receiveMethod.GenericArguments.Add(type);
|
|
var versionMethod = new GenericInstanceMethod(m_MessageManager_CreateMessageAndGetVersion_MethodRef);
|
|
versionMethod.GenericArguments.Add(type);
|
|
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod, versionMethod);
|
|
}
|
|
|
|
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
|
|
|
assembly.MainModule.Types.Add(typeDefinition);
|
|
}
|
|
}
|
|
}
|