Overhaul NetworkManager.
Old systems of network injection and intercept are deprecated and will be removed in the future!
This commit is contained in:
@@ -17,7 +17,7 @@ using VRageMath;
|
|||||||
|
|
||||||
namespace Torch.Managers
|
namespace Torch.Managers
|
||||||
{
|
{
|
||||||
public class NetworkManager : Manager, INetworkManager
|
public partial class NetworkManager : Manager, INetworkManager
|
||||||
{
|
{
|
||||||
private static Logger _log = LogManager.GetCurrentClassLogger();
|
private static Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
@@ -26,18 +26,8 @@ namespace Torch.Managers
|
|||||||
private readonly HashSet<INetworkHandler> _networkHandlers = new HashSet<INetworkHandler>();
|
private readonly HashSet<INetworkHandler> _networkHandlers = new HashSet<INetworkHandler>();
|
||||||
private bool _init;
|
private bool _init;
|
||||||
|
|
||||||
private const int MAX_ARGUMENT = 6;
|
|
||||||
private const int GENERIC_PARAMETERS = 8;
|
|
||||||
private const int DISPATCH_PARAMETERS = 10;
|
|
||||||
private static readonly DBNull DbNull = DBNull.Value;
|
|
||||||
private static MethodInfo _dispatchInfo;
|
|
||||||
|
|
||||||
private static MethodInfo DispatchEventInfo => _dispatchInfo ?? (_dispatchInfo = typeof(MyReplicationLayerBase).GetMethod("DispatchEvent", BindingFlags.NonPublic | BindingFlags.Instance));
|
|
||||||
|
|
||||||
[ReflectedGetter(Name = "m_typeTable")]
|
[ReflectedGetter(Name = "m_typeTable")]
|
||||||
private static Func<MyReplicationLayerBase, MyTypeTable> _typeTableGetter;
|
private static Func<MyReplicationLayerBase, MyTypeTable> _typeTableGetter;
|
||||||
[ReflectedGetter(Name = "m_methodInfoLookup")]
|
|
||||||
private static Func<MyEventTable, Dictionary<MethodInfo, CallSite>> _methodInfoLookupGetter;
|
|
||||||
[ReflectedMethod(Type = typeof(MyReplicationLayer), Name = "GetObjectByNetworkId")]
|
[ReflectedMethod(Type = typeof(MyReplicationLayer), Name = "GetObjectByNetworkId")]
|
||||||
private static Func<MyReplicationLayer, NetworkId, IMyNetObject> _getObjectByNetworkId;
|
private static Func<MyReplicationLayer, NetworkId, IMyNetObject> _getObjectByNetworkId;
|
||||||
|
|
||||||
@@ -116,281 +106,112 @@ namespace Torch.Managers
|
|||||||
// TODO reverse what was done in Attach
|
// TODO reverse what was done in Attach
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Network Intercept
|
|
||||||
|
|
||||||
//TODO: Change this to a method patch so I don't have to try to keep up with Keen.
|
|
||||||
/// <summary>
|
|
||||||
/// This is the main body of the network intercept system. When messages come in from clients, they are processed here
|
|
||||||
/// before being passed on to the game server.
|
|
||||||
///
|
|
||||||
/// DO NOT modify this method unless you're absolutely sure of what you're doing. This can very easily destabilize the game!
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packet"></param>
|
|
||||||
private void OnEvent(MyPacket packet)
|
|
||||||
{
|
|
||||||
if (_networkHandlers.Count == 0)
|
|
||||||
{
|
|
||||||
//pass the message back to the game server
|
|
||||||
try
|
|
||||||
{
|
|
||||||
((MyReplicationLayer)MyMultiplayer.ReplicationLayer).OnEvent(packet);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_log.Error(ex);
|
|
||||||
//crash after logging, bad things could happen if we continue on with bad data
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream = new BitStream();
|
|
||||||
stream.ResetRead(packet.BitStream);
|
|
||||||
|
|
||||||
var networkId = stream.ReadNetworkId();
|
|
||||||
//this value is unused, but removing this line corrupts the rest of the stream
|
|
||||||
var blockedNetworkId = stream.ReadNetworkId();
|
|
||||||
var eventId = (uint)stream.ReadUInt16();
|
|
||||||
bool flag = stream.ReadBool();
|
|
||||||
Vector3D? position = new Vector3D?();
|
|
||||||
if (flag)
|
|
||||||
position = new Vector3D?(stream.ReadVector3D());
|
|
||||||
|
|
||||||
CallSite site;
|
|
||||||
object obj;
|
|
||||||
if (networkId.IsInvalid) // Static event
|
|
||||||
{
|
|
||||||
site = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).StaticEventTable.Get(eventId);
|
|
||||||
obj = null;
|
|
||||||
}
|
|
||||||
else // Instance event
|
|
||||||
{
|
|
||||||
//var sendAs = ((MyReplicationLayer)MyMultiplayer.ReplicationLayer).GetObjectByNetworkId(networkId);
|
|
||||||
var sendAs = _getObjectByNetworkId((MyReplicationLayer)MyMultiplayer.ReplicationLayer, networkId);
|
|
||||||
if (sendAs == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(sendAs.GetType());
|
|
||||||
var eventCount = typeInfo.EventTable.Count;
|
|
||||||
if (eventId < eventCount) // Directly
|
|
||||||
{
|
|
||||||
obj = sendAs;
|
|
||||||
site = typeInfo.EventTable.Get(eventId);
|
|
||||||
}
|
|
||||||
else // Through proxy
|
|
||||||
{
|
|
||||||
obj = ((IMyProxyTarget)sendAs).Target;
|
|
||||||
typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(obj.GetType());
|
|
||||||
site = typeInfo.EventTable.Get(eventId - (uint)eventCount); // Subtract max id of Proxy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//we're handling the network live in the game thread, this needs to go as fast as possible
|
|
||||||
var discard = false;
|
|
||||||
foreach (var handler in _networkHandlers)
|
|
||||||
//Parallel.ForEach(_networkHandlers, handler =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (handler.CanHandle(site))
|
|
||||||
discard |= handler.Handle(packet.Sender.Id.Value, site, stream, obj, packet);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
//ApplicationLog.Error(ex.ToString());
|
|
||||||
_log.Error(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//one of the handlers wants us to discard this packet
|
|
||||||
if (discard)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//pass the message back to the game server
|
|
||||||
try
|
|
||||||
{
|
|
||||||
((MyReplicationLayer)MyMultiplayer.ReplicationLayer).OnEvent(packet);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_log.Error(ex, "Error processing network event!");
|
|
||||||
_log.Error(ex);
|
|
||||||
//crash after logging, bad things could happen if we continue on with bad data
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void RegisterNetworkHandler(INetworkHandler handler)
|
|
||||||
{
|
|
||||||
var handlerType = handler.GetType().FullName;
|
|
||||||
var toRemove = new List<INetworkHandler>();
|
|
||||||
foreach (var item in _networkHandlers)
|
|
||||||
{
|
|
||||||
if (item.GetType().FullName == handlerType)
|
|
||||||
{
|
|
||||||
//if (ExtenderOptions.IsDebugging)
|
|
||||||
_log.Error("Network handler already registered! " + handlerType);
|
|
||||||
toRemove.Add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var oldHandler in toRemove)
|
|
||||||
_networkHandlers.Remove(oldHandler);
|
|
||||||
|
|
||||||
_networkHandlers.Add(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool UnregisterNetworkHandler(INetworkHandler handler)
|
|
||||||
{
|
|
||||||
return _networkHandlers.Remove(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterNetworkHandlers(params INetworkHandler[] handlers)
|
|
||||||
{
|
|
||||||
foreach (var handler in handlers)
|
|
||||||
RegisterNetworkHandler(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Network Injection
|
#region Network Injection
|
||||||
|
|
||||||
|
private static Func<T, TA> GetDelegate<T, TA>(MethodInfo method) where TA : class
|
||||||
/// <summary>
|
|
||||||
/// Broadcasts an event to all connected clients
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void RaiseEvent(MethodInfo method, object obj, params object[] args)
|
|
||||||
{
|
{
|
||||||
//default(EndpointId) tells the network to broadcast the message
|
return x => Delegate.CreateDelegate(typeof(TA), x, method) as TA;
|
||||||
RaiseEvent(method, obj, default(EndpointId), args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static void RaiseEvent<T1>(T1 instance, MethodInfo method, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
/// Sends an event to one client by SteamId
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <param name="steamId"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void RaiseEvent(MethodInfo method, object obj, ulong steamId, params object[] args)
|
|
||||||
{
|
{
|
||||||
RaiseEvent(method, obj, new EndpointId(steamId), args);
|
var del = GetDelegate<T1, Action>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseEvent(instance, del, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static void RaiseEvent<T1, T2>(T1 instance, MethodInfo method, T2 arg1, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
/// Sends an event to one client
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <param name="endpoint"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void RaiseEvent(MethodInfo method, object obj, EndpointId endpoint, params object[] args)
|
|
||||||
{
|
{
|
||||||
if (method == null)
|
var del = GetDelegate<T1, Action<T2>> (method);
|
||||||
throw new ArgumentNullException(nameof(method), "MethodInfo cannot be null!");
|
|
||||||
|
|
||||||
if (args.Length > MAX_ARGUMENT)
|
MyMultiplayer.RaiseEvent(instance, del, arg1, target);
|
||||||
throw new ArgumentOutOfRangeException(nameof(args), $"Cannot pass more than {MAX_ARGUMENT} arguments!");
|
|
||||||
|
|
||||||
var owner = obj as IMyEventOwner;
|
|
||||||
if (obj != null && owner == null)
|
|
||||||
throw new InvalidCastException("Provided event target is not of type IMyEventOwner!");
|
|
||||||
|
|
||||||
if (!method.HasAttribute<EventAttribute>())
|
|
||||||
throw new CustomAttributeFormatException("Provided event target does not have the Event attribute! Replication will not succeed!");
|
|
||||||
|
|
||||||
//array to hold arguments to pass into DispatchEvent
|
|
||||||
object[] arguments = new object[DISPATCH_PARAMETERS];
|
|
||||||
|
|
||||||
|
|
||||||
arguments[0] = obj == null ? TryGetStaticCallSite(method) : TryGetCallSite(method, obj);
|
|
||||||
arguments[1] = endpoint;
|
|
||||||
arguments[2] = owner;
|
|
||||||
|
|
||||||
//copy supplied arguments into the reflection arguments
|
|
||||||
for (var i = 0; i < args.Length; i++)
|
|
||||||
arguments[i + 3] = args[i];
|
|
||||||
|
|
||||||
//pad the array out with DBNull, skip last element
|
|
||||||
//last element should stay null (this is for blocking events -- not used?)
|
|
||||||
for (var j = args.Length + 3; j < arguments.Length - 1; j++)
|
|
||||||
arguments[j] = DbNull;
|
|
||||||
|
|
||||||
//create an array of Types so we can create a generic method
|
|
||||||
var argTypes = new Type[GENERIC_PARAMETERS];
|
|
||||||
|
|
||||||
//any null arguments (not DBNull) must be of type IMyEventOwner
|
|
||||||
for (var k = 2; k < arguments.Length; k++)
|
|
||||||
argTypes[k - 2] = arguments[k]?.GetType() ?? typeof(IMyEventOwner);
|
|
||||||
|
|
||||||
var parameters = method.GetParameters();
|
|
||||||
for (var i = 0; i < parameters.Length; i++)
|
|
||||||
{
|
|
||||||
if (argTypes[i + 1] != parameters[i].ParameterType)
|
|
||||||
throw new TypeLoadException($"Type mismatch on method parameters. Expected {string.Join(", ", parameters.Select(p => p.ParameterType.ToString()))} got {string.Join(", ", argTypes.Select(t => t.ToString()))}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//create a generic method of DispatchEvent and invoke to inject our data into the network
|
public static void RaiseEvent<T1, T2, T3>(T1 instance, MethodInfo method, T2 arg1, T3 arg2, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
var dispatch = DispatchEventInfo.MakeGenericMethod(argTypes);
|
|
||||||
dispatch.Invoke(MyMultiplayer.ReplicationLayer, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Broadcasts a static event to all connected clients
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void RaiseStaticEvent(MethodInfo method, params object[] args)
|
|
||||||
{
|
{
|
||||||
//default(EndpointId) tells the network to broadcast the message
|
var del = GetDelegate<T1, Action<T2, T3>>(method);
|
||||||
RaiseStaticEvent(method, default(EndpointId), args);
|
|
||||||
|
MyMultiplayer.RaiseEvent(instance, del, arg1, arg2, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static void RaiseEvent<T1, T2, T3, T4>(T1 instance, MethodInfo method, T2 arg1, T3 arg2, T4 arg3, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
/// Sends a static event to one client by SteamId
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="steamId"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void RaiseStaticEvent(MethodInfo method, ulong steamId, params object[] args)
|
|
||||||
{
|
{
|
||||||
RaiseEvent(method, null, new EndpointId(steamId), args);
|
var del = GetDelegate<T1, Action<T2, T3, T4>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseEvent(instance, del, arg1, arg2, arg3, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static void RaiseEvent<T1, T2, T3, T4, T5>(T1 instance, MethodInfo method, T2 arg1, T3 arg2, T4 arg3, T5 arg4, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
/// Sends a static event to one client
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="endpoint"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public void RaiseStaticEvent(MethodInfo method, EndpointId endpoint, params object[] args)
|
|
||||||
{
|
{
|
||||||
RaiseEvent(method, null, endpoint, args);
|
var del = GetDelegate<T1, Action<T2, T3, T4, T5>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseEvent(instance, del, arg1, arg2, arg3, arg4, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallSite TryGetStaticCallSite(MethodInfo method)
|
public static void RaiseEvent<T1, T2, T3, T4, T5, T6>(T1 instance, MethodInfo method, T2 arg1, T3 arg2, T4 arg3, T5 arg4, T6 arg5, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
{
|
{
|
||||||
MyTypeTable typeTable = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer);
|
var del = GetDelegate<T1, Action<T2, T3, T4, T5, T6>>(method);
|
||||||
if (!_methodInfoLookupGetter.Invoke(typeTable.StaticEventTable).TryGetValue(method, out CallSite result))
|
|
||||||
throw new MissingMemberException("Provided event target not found!");
|
MyMultiplayer.RaiseEvent(instance, del, arg1, arg2, arg3, arg4, arg5, target);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallSite TryGetCallSite(MethodInfo method, object arg)
|
public static void RaiseEvent<T1, T2, T3, T4, T5, T6, T7>(T1 instance, MethodInfo method, T2 arg1, T3 arg2, T4 arg3, T5 arg4, T6 arg5, T7 arg6, EndpointId target = default(EndpointId)) where T1 : IMyEventOwner
|
||||||
{
|
{
|
||||||
MySynchronizedTypeInfo typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(arg.GetType());
|
var del = GetDelegate<T1, Action<T2, T3, T4, T5, T6, T7>>(method);
|
||||||
if (!_methodInfoLookupGetter.Invoke(typeInfo.EventTable).TryGetValue(method, out CallSite result))
|
|
||||||
throw new MissingMemberException("Provided event target not found!");
|
MyMultiplayer.RaiseEvent(instance, del, arg1, arg2, arg3, arg4, arg5, arg6, target);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent(MethodInfo method, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, target, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent<T1>(MethodInfo method, T1 arg1, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action<T1>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, arg1, target, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent<T1, T2>(MethodInfo method, T1 arg1, T2 arg2, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action<T1, T2>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, arg1, arg2, target, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent<T1, T2, T3>(MethodInfo method, T1 arg1, T2 arg2, T3 arg3, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action<T1, T2, T3>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, arg1, arg2, arg3, target, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent<T1, T2, T3, T4>(MethodInfo method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action<T1, T2, T3, T4>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, arg1, arg2, arg3, arg4, target, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent<T1, T2, T3, T4, T5>(MethodInfo method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action<T1, T2, T3, T4, T5>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, arg1, arg2, arg3, arg4, arg5, target, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RaiseStaticEvent<T1, T2, T3, T4, T5, T6>(MethodInfo method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, EndpointId target = default(EndpointId), Vector3D? position = null)
|
||||||
|
{
|
||||||
|
var del = GetDelegate<IMyEventOwner, Action<T1, T2, T3, T4, T5, T6>>(method);
|
||||||
|
|
||||||
|
MyMultiplayer.RaiseStaticEvent(del, arg1, arg2, arg3, arg4, arg5, arg6, target, position);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
318
Torch/Managers/NetworkManager/NetworkManager_Deprecated.cs
Normal file
318
Torch/Managers/NetworkManager/NetworkManager_Deprecated.cs
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Library.Collections;
|
||||||
|
using VRage.Network;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace Torch.Managers
|
||||||
|
{
|
||||||
|
//Everything in this file is deprecated and should be deleted Eventually(tm)
|
||||||
|
public partial class NetworkManager
|
||||||
|
{
|
||||||
|
[ReflectedGetter(Name = "m_methodInfoLookup")]
|
||||||
|
private static Func<MyEventTable, Dictionary<MethodInfo, CallSite>> _methodInfoLookupGetter;
|
||||||
|
private const int MAX_ARGUMENT = 6;
|
||||||
|
private const int GENERIC_PARAMETERS = 8;
|
||||||
|
private const int DISPATCH_PARAMETERS = 11;
|
||||||
|
private static readonly DBNull DbNull = DBNull.Value;
|
||||||
|
private static MethodInfo _dispatchInfo;
|
||||||
|
|
||||||
|
private static MethodInfo DispatchEventInfo => _dispatchInfo ?? (_dispatchInfo = typeof(MyReplicationLayerBase).GetMethod("DispatchEvent", BindingFlags.NonPublic | BindingFlags.Instance));
|
||||||
|
|
||||||
|
#region Network Injection
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Broadcasts an event to all connected clients
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
[Obsolete("Old injection system deprecated. Use the generic methods instead.")]
|
||||||
|
public void RaiseEvent(MethodInfo method, object obj, params object[] args)
|
||||||
|
{
|
||||||
|
//default(EndpointId) tells the network to broadcast the message
|
||||||
|
RaiseEvent(method, obj, default(EndpointId), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends an event to one client by SteamId
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <param name="steamId"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
[Obsolete("Old injection system deprecated. Use the generic methods instead.")]
|
||||||
|
public void RaiseEvent(MethodInfo method, object obj, ulong steamId, params object[] args)
|
||||||
|
{
|
||||||
|
RaiseEvent(method, obj, new EndpointId(steamId), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends an event to one client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <param name="endpoint"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
[Obsolete("Old injection system deprecated. Use the generic methods instead.")]
|
||||||
|
public void RaiseEvent(MethodInfo method, object obj, EndpointId endpoint, params object[] args)
|
||||||
|
{
|
||||||
|
if (method == null)
|
||||||
|
throw new ArgumentNullException(nameof(method), "MethodInfo cannot be null!");
|
||||||
|
|
||||||
|
if (args.Length > MAX_ARGUMENT)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(args), $"Cannot pass more than {MAX_ARGUMENT} arguments!");
|
||||||
|
|
||||||
|
var owner = obj as IMyEventOwner;
|
||||||
|
if (obj != null && owner == null)
|
||||||
|
throw new InvalidCastException("Provided event target is not of type IMyEventOwner!");
|
||||||
|
|
||||||
|
if (!method.HasAttribute<EventAttribute>())
|
||||||
|
throw new CustomAttributeFormatException("Provided event target does not have the Event attribute! Replication will not succeed!");
|
||||||
|
|
||||||
|
//array to hold arguments to pass into DispatchEvent
|
||||||
|
object[] arguments = new object[DISPATCH_PARAMETERS];
|
||||||
|
|
||||||
|
|
||||||
|
arguments[0] = obj == null ? TryGetStaticCallSite(method) : TryGetCallSite(method, obj);
|
||||||
|
arguments[1] = endpoint;
|
||||||
|
arguments[2] = new Vector3D?();
|
||||||
|
arguments[3] = owner;
|
||||||
|
|
||||||
|
//copy supplied arguments into the reflection arguments
|
||||||
|
for (var i = 0; i < args.Length; i++)
|
||||||
|
arguments[i + 4] = args[i];
|
||||||
|
|
||||||
|
//pad the array out with DBNull, skip last element
|
||||||
|
//last element should stay null (this is for blocking events -- not used?)
|
||||||
|
for (var j = args.Length + 4; j < arguments.Length - 1; j++)
|
||||||
|
arguments[j] = DbNull;
|
||||||
|
|
||||||
|
//create an array of Types so we can create a generic method
|
||||||
|
var argTypes = new Type[GENERIC_PARAMETERS];
|
||||||
|
|
||||||
|
//any null arguments (not DBNull) must be of type IMyEventOwner
|
||||||
|
for (var k = 2; k < arguments.Length; k++)
|
||||||
|
argTypes[k - 2] = arguments[k]?.GetType() ?? typeof(IMyEventOwner);
|
||||||
|
|
||||||
|
var parameters = method.GetParameters();
|
||||||
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
if (argTypes[i + 1] != parameters[i].ParameterType)
|
||||||
|
throw new TypeLoadException($"Type mismatch on method parameters. Expected {string.Join(", ", parameters.Select(p => p.ParameterType.ToString()))} got {string.Join(", ", argTypes.Select(t => t.ToString()))}");
|
||||||
|
}
|
||||||
|
|
||||||
|
//create a generic method of DispatchEvent and invoke to inject our data into the network
|
||||||
|
var dispatch = DispatchEventInfo.MakeGenericMethod(argTypes);
|
||||||
|
dispatch.Invoke(MyMultiplayer.ReplicationLayer, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Broadcasts a static event to all connected clients
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
[Obsolete("Old injection system deprecated. Use the generic methods instead.")]
|
||||||
|
public void RaiseStaticEvent(MethodInfo method, params object[] args)
|
||||||
|
{
|
||||||
|
//default(EndpointId) tells the network to broadcast the message
|
||||||
|
RaiseStaticEvent(method, default(EndpointId), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a static event to one client by SteamId
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="steamId"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
[Obsolete("Old injection system deprecated. Use the generic methods instead.")]
|
||||||
|
public void RaiseStaticEvent(MethodInfo method, ulong steamId, params object[] args)
|
||||||
|
{
|
||||||
|
RaiseEvent(method, (object)null, new EndpointId(steamId), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a static event to one client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="endpoint"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
[Obsolete("Old injection system deprecated. Use the generic methods instead.")]
|
||||||
|
public void RaiseStaticEvent(MethodInfo method, EndpointId endpoint, params object[] args)
|
||||||
|
{
|
||||||
|
RaiseEvent(method, (object)null, endpoint, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CallSite TryGetStaticCallSite(MethodInfo method)
|
||||||
|
{
|
||||||
|
MyTypeTable typeTable = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer);
|
||||||
|
if (!_methodInfoLookupGetter.Invoke(typeTable.StaticEventTable).TryGetValue(method, out CallSite result))
|
||||||
|
throw new MissingMemberException("Provided event target not found!");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CallSite TryGetCallSite(MethodInfo method, object arg)
|
||||||
|
{
|
||||||
|
MySynchronizedTypeInfo typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(arg.GetType());
|
||||||
|
if (!_methodInfoLookupGetter.Invoke(typeInfo.EventTable).TryGetValue(method, out CallSite result))
|
||||||
|
throw new MissingMemberException("Provided event target not found!");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Network Intercept
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
|
//TODO: Change this to a method patch so I don't have to try to keep up with Keen.
|
||||||
|
/// <summary>
|
||||||
|
/// This is the main body of the network intercept system. When messages come in from clients, they are processed here
|
||||||
|
/// before being passed on to the game server.
|
||||||
|
///
|
||||||
|
/// DO NOT modify this method unless you're absolutely sure of what you're doing. This can very easily destabilize the game!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packet"></param>
|
||||||
|
private void OnEvent(MyPacket packet)
|
||||||
|
{
|
||||||
|
if (_networkHandlers.Count == 0)
|
||||||
|
{
|
||||||
|
//pass the message back to the game server
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((MyReplicationLayer)MyMultiplayer.ReplicationLayer).OnEvent(packet);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error(ex);
|
||||||
|
//crash after logging, bad things could happen if we continue on with bad data
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = new BitStream();
|
||||||
|
stream.ResetRead(packet.BitStream);
|
||||||
|
|
||||||
|
var networkId = stream.ReadNetworkId();
|
||||||
|
//this value is unused, but removing this line corrupts the rest of the stream
|
||||||
|
var blockedNetworkId = stream.ReadNetworkId();
|
||||||
|
var eventId = (uint)stream.ReadUInt16();
|
||||||
|
bool flag = stream.ReadBool();
|
||||||
|
Vector3D? position = new Vector3D?();
|
||||||
|
if (flag)
|
||||||
|
position = new Vector3D?(stream.ReadVector3D());
|
||||||
|
|
||||||
|
CallSite site;
|
||||||
|
object obj;
|
||||||
|
if (networkId.IsInvalid) // Static event
|
||||||
|
{
|
||||||
|
site = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).StaticEventTable.Get(eventId);
|
||||||
|
obj = null;
|
||||||
|
}
|
||||||
|
else // Instance event
|
||||||
|
{
|
||||||
|
//var sendAs = ((MyReplicationLayer)MyMultiplayer.ReplicationLayer).GetObjectByNetworkId(networkId);
|
||||||
|
var sendAs = _getObjectByNetworkId((MyReplicationLayer)MyMultiplayer.ReplicationLayer, networkId);
|
||||||
|
if (sendAs == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(sendAs.GetType());
|
||||||
|
var eventCount = typeInfo.EventTable.Count;
|
||||||
|
if (eventId < eventCount) // Directly
|
||||||
|
{
|
||||||
|
obj = sendAs;
|
||||||
|
site = typeInfo.EventTable.Get(eventId);
|
||||||
|
}
|
||||||
|
else // Through proxy
|
||||||
|
{
|
||||||
|
obj = ((IMyProxyTarget)sendAs).Target;
|
||||||
|
typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(obj.GetType());
|
||||||
|
site = typeInfo.EventTable.Get(eventId - (uint)eventCount); // Subtract max id of Proxy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//we're handling the network live in the game thread, this needs to go as fast as possible
|
||||||
|
var discard = false;
|
||||||
|
foreach (var handler in _networkHandlers)
|
||||||
|
//Parallel.ForEach(_networkHandlers, handler =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (handler.CanHandle(site))
|
||||||
|
discard |= handler.Handle(packet.Sender.Id.Value, site, stream, obj, packet);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//ApplicationLog.Error(ex.ToString());
|
||||||
|
_log.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//one of the handlers wants us to discard this packet
|
||||||
|
if (discard)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//pass the message back to the game server
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((MyReplicationLayer)MyMultiplayer.ReplicationLayer).OnEvent(packet);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error(ex, "Error processing network event!");
|
||||||
|
_log.Error(ex);
|
||||||
|
//crash after logging, bad things could happen if we continue on with bad data
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Deprecated. Use a method patch instead.")]
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RegisterNetworkHandler(INetworkHandler handler)
|
||||||
|
{
|
||||||
|
var handlerType = handler.GetType().FullName;
|
||||||
|
var toRemove = new List<INetworkHandler>();
|
||||||
|
foreach (var item in _networkHandlers)
|
||||||
|
{
|
||||||
|
if (item.GetType().FullName == handlerType)
|
||||||
|
{
|
||||||
|
//if (ExtenderOptions.IsDebugging)
|
||||||
|
_log.Error("Network handler already registered! " + handlerType);
|
||||||
|
toRemove.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var oldHandler in toRemove)
|
||||||
|
_networkHandlers.Remove(oldHandler);
|
||||||
|
|
||||||
|
_networkHandlers.Add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Deprecated. Use a method patch instead.")]
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool UnregisterNetworkHandler(INetworkHandler handler)
|
||||||
|
{
|
||||||
|
return _networkHandlers.Remove(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Deprecated. Use a method patch instead.")]
|
||||||
|
public void RegisterNetworkHandlers(params INetworkHandler[] handlers)
|
||||||
|
{
|
||||||
|
foreach (var handler in handlers)
|
||||||
|
RegisterNetworkHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -194,6 +194,7 @@
|
|||||||
<Compile Include="Event\EventManager.cs" />
|
<Compile Include="Event\EventManager.cs" />
|
||||||
<Compile Include="Event\IEventList.cs" />
|
<Compile Include="Event\IEventList.cs" />
|
||||||
<Compile Include="Managers\KeenLogPatch.cs" />
|
<Compile Include="Managers\KeenLogPatch.cs" />
|
||||||
|
<Compile Include="Managers\NetworkManager\NetworkManager_Deprecated.cs" />
|
||||||
<Compile Include="Managers\PatchManager\AssemblyMemory.cs" />
|
<Compile Include="Managers\PatchManager\AssemblyMemory.cs" />
|
||||||
<Compile Include="Managers\PatchManager\DecoratedMethod.cs" />
|
<Compile Include="Managers\PatchManager\DecoratedMethod.cs" />
|
||||||
<Compile Include="Managers\PatchManager\EmitExtensions.cs" />
|
<Compile Include="Managers\PatchManager\EmitExtensions.cs" />
|
||||||
|
Reference in New Issue
Block a user