Add network intercept and injection. Add basic ChatManager that allows us to do command hiding on the server
This commit is contained in:
64
Torch/Managers/ChatManager.cs
Normal file
64
Torch/Managers/ChatManager.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Torch.Managers.NetworkManager;
|
||||
using VRage.Library.Collections;
|
||||
using VRage.Network;
|
||||
using VRage.Utils;
|
||||
|
||||
namespace Torch.Managers
|
||||
{
|
||||
public class ChatManager
|
||||
{
|
||||
private static ChatManager _instance;
|
||||
public static ChatManager Instance => _instance ?? (_instance = new ChatManager());
|
||||
|
||||
public delegate void MessageRecievedDel( ChatMsg msg, ref bool sendToOthers );
|
||||
|
||||
public event MessageRecievedDel MessageRecieved;
|
||||
|
||||
internal void RaiseMessageRecieved( ChatMsg msg, ref bool sendToOthers ) =>
|
||||
MessageRecieved?.Invoke(msg, ref sendToOthers);
|
||||
}
|
||||
|
||||
internal class ChatIntercept : NetworkHandlerBase
|
||||
{
|
||||
private bool? _unitTestResult;
|
||||
public override bool CanHandle( CallSite site )
|
||||
{
|
||||
if ( site.MethodInfo.Name != "OnChatMessageRecieved" )
|
||||
return false;
|
||||
|
||||
if ( _unitTestResult.HasValue )
|
||||
return _unitTestResult.Value;
|
||||
|
||||
var parameters = site.MethodInfo.GetParameters();
|
||||
if ( parameters.Length != 1 )
|
||||
{
|
||||
_unitTestResult = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( parameters[0].ParameterType != typeof(ChatMsg) )
|
||||
_unitTestResult = false;
|
||||
|
||||
_unitTestResult = true;
|
||||
|
||||
return _unitTestResult.Value;
|
||||
}
|
||||
|
||||
public override bool Handle( ulong remoteUserId, CallSite site, BitStream stream, object obj )
|
||||
{
|
||||
ChatMsg msg = new ChatMsg();
|
||||
|
||||
base.Serialize( site.MethodInfo, stream, ref msg );
|
||||
|
||||
bool sendToOthers = true;
|
||||
ChatManager.Instance.RaiseMessageRecieved( msg, ref sendToOthers );
|
||||
|
||||
return !sendToOthers;
|
||||
}
|
||||
}
|
||||
}
|
242
Torch/Managers/NetworkManager/NetworkHandlerBase.cs
Normal file
242
Torch/Managers/NetworkManager/NetworkHandlerBase.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Library.Collections;
|
||||
using VRage.Network;
|
||||
using VRage.Serialization;
|
||||
|
||||
namespace Torch.Managers.NetworkManager
|
||||
{
|
||||
public abstract class NetworkHandlerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Check the method name and do unit tests on parameters in here.
|
||||
/// </summary>
|
||||
/// <param name="site"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool CanHandle( CallSite site );
|
||||
|
||||
/// <summary>
|
||||
/// Performs action on network packet. Return value of true means the packet has been handled, and will not be passed on to the game server.
|
||||
/// </summary>
|
||||
/// <param name="remoteUserId"></param>
|
||||
/// <param name="site"></param>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj );
|
||||
|
||||
/// <summary>
|
||||
/// Extracts method arguments from the bitstream or packs them back in, depending on stream read mode.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="arg1"></param>
|
||||
public void Serialize<T1>(MethodInfo info, BitStream stream, ref T1 arg1)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
|
||||
if (stream.Reading)
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write(stream, ref arg1, s1, info1);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize<T1, T2>(MethodInfo info, BitStream stream, ref T1 arg1, ref T2 arg2)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
var s2 = MyFactory.GetSerializer<T2>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
var info2 = MySerializeInfo.CreateForParameter(args, 1);
|
||||
|
||||
if (stream.Reading)
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg2, s2, info2);
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write(stream, ref arg1, s1, info1);
|
||||
MySerializationHelpers.Write(stream, ref arg2, s2, info2);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize<T1, T2, T3>(MethodInfo info, BitStream stream, ref T1 arg1, ref T2 arg2, ref T3 arg3)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
var s2 = MyFactory.GetSerializer<T2>();
|
||||
var s3 = MyFactory.GetSerializer<T3>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
var info2 = MySerializeInfo.CreateForParameter(args, 1);
|
||||
var info3 = MySerializeInfo.CreateForParameter(args, 2);
|
||||
|
||||
if (stream.Reading)
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg2, s2, info2);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg3, s3, info3);
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write(stream, ref arg1, s1, info1);
|
||||
MySerializationHelpers.Write(stream, ref arg2, s2, info2);
|
||||
MySerializationHelpers.Write(stream, ref arg3, s3, info3);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize<T1, T2, T3, T4>(MethodInfo info, BitStream stream, ref T1 arg1, ref T2 arg2, ref T3 arg3, ref T4 arg4)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
var s2 = MyFactory.GetSerializer<T2>();
|
||||
var s3 = MyFactory.GetSerializer<T3>();
|
||||
var s4 = MyFactory.GetSerializer<T4>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
var info2 = MySerializeInfo.CreateForParameter(args, 1);
|
||||
var info3 = MySerializeInfo.CreateForParameter(args, 2);
|
||||
var info4 = MySerializeInfo.CreateForParameter(args, 3);
|
||||
|
||||
if (stream.Reading)
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg2, s2, info2);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg3, s3, info3);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg4, s4, info4);
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write(stream, ref arg1, s1, info1);
|
||||
MySerializationHelpers.Write(stream, ref arg2, s2, info2);
|
||||
MySerializationHelpers.Write(stream, ref arg3, s3, info3);
|
||||
MySerializationHelpers.Write(stream, ref arg4, s4, info4);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize<T1, T2, T3, T4, T5>(MethodInfo info, BitStream stream, ref T1 arg1, ref T2 arg2, ref T3 arg3, ref T4 arg4, ref T5 arg5)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
var s2 = MyFactory.GetSerializer<T2>();
|
||||
var s3 = MyFactory.GetSerializer<T3>();
|
||||
var s4 = MyFactory.GetSerializer<T4>();
|
||||
var s5 = MyFactory.GetSerializer<T5>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
var info2 = MySerializeInfo.CreateForParameter(args, 1);
|
||||
var info3 = MySerializeInfo.CreateForParameter(args, 2);
|
||||
var info4 = MySerializeInfo.CreateForParameter(args, 3);
|
||||
var info5 = MySerializeInfo.CreateForParameter(args, 4);
|
||||
|
||||
if (stream.Reading)
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg2, s2, info2);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg3, s3, info3);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg4, s4, info4);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg5, s5, info5);
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write(stream, ref arg1, s1, info1);
|
||||
MySerializationHelpers.Write(stream, ref arg2, s2, info2);
|
||||
MySerializationHelpers.Write(stream, ref arg3, s3, info3);
|
||||
MySerializationHelpers.Write(stream, ref arg4, s4, info4);
|
||||
MySerializationHelpers.Write(stream, ref arg5, s5, info5);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize<T1, T2, T3, T4, T5, T6>(MethodInfo info, BitStream stream, ref T1 arg1, ref T2 arg2, ref T3 arg3, ref T4 arg4, ref T5 arg5, ref T6 arg6)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
var s2 = MyFactory.GetSerializer<T2>();
|
||||
var s3 = MyFactory.GetSerializer<T3>();
|
||||
var s4 = MyFactory.GetSerializer<T4>();
|
||||
var s5 = MyFactory.GetSerializer<T5>();
|
||||
var s6 = MyFactory.GetSerializer<T6>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
var info2 = MySerializeInfo.CreateForParameter(args, 1);
|
||||
var info3 = MySerializeInfo.CreateForParameter(args, 2);
|
||||
var info4 = MySerializeInfo.CreateForParameter(args, 3);
|
||||
var info5 = MySerializeInfo.CreateForParameter(args, 4);
|
||||
var info6 = MySerializeInfo.CreateForParameter(args, 5);
|
||||
|
||||
if (stream.Reading)
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg2, s2, info2);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg3, s3, info3);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg4, s4, info4);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg5, s5, info5);
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg6, s6, info6);
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write(stream, ref arg1, s1, info1);
|
||||
MySerializationHelpers.Write(stream, ref arg2, s2, info2);
|
||||
MySerializationHelpers.Write(stream, ref arg3, s3, info3);
|
||||
MySerializationHelpers.Write(stream, ref arg4, s4, info4);
|
||||
MySerializationHelpers.Write(stream, ref arg5, s5, info5);
|
||||
MySerializationHelpers.Write(stream, ref arg6, s6, info6);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize<T1, T2, T3, T4, T5, T6, T7>(MethodInfo info, BitStream stream, ref T1 arg1, ref T2 arg2, ref T3 arg3, ref T4 arg4, ref T5 arg5, ref T6 arg6, ref T7 arg7)
|
||||
{
|
||||
var s1 = MyFactory.GetSerializer<T1>();
|
||||
var s2 = MyFactory.GetSerializer<T2>();
|
||||
var s3 = MyFactory.GetSerializer<T3>();
|
||||
var s4 = MyFactory.GetSerializer<T4>();
|
||||
var s5 = MyFactory.GetSerializer<T5>();
|
||||
var s6 = MyFactory.GetSerializer<T6>();
|
||||
var s7 = MyFactory.GetSerializer<T7>();
|
||||
|
||||
var args = info.GetParameters();
|
||||
var info1 = MySerializeInfo.CreateForParameter(args, 0);
|
||||
var info2 = MySerializeInfo.CreateForParameter(args, 1);
|
||||
var info3 = MySerializeInfo.CreateForParameter(args, 2);
|
||||
var info4 = MySerializeInfo.CreateForParameter(args, 3);
|
||||
var info5 = MySerializeInfo.CreateForParameter(args, 4);
|
||||
var info6 = MySerializeInfo.CreateForParameter(args, 5);
|
||||
var info7 = MySerializeInfo.CreateForParameter(args, 6);
|
||||
|
||||
if ( stream.Reading )
|
||||
{
|
||||
MySerializationHelpers.CreateAndRead(stream, out arg1, s1, info1);
|
||||
MySerializationHelpers.CreateAndRead( stream, out arg2, s2, info2 );
|
||||
MySerializationHelpers.CreateAndRead( stream, out arg3, s3, info3 );
|
||||
MySerializationHelpers.CreateAndRead( stream, out arg4, s4, info4 );
|
||||
MySerializationHelpers.CreateAndRead( stream, out arg5, s5, info5 );
|
||||
MySerializationHelpers.CreateAndRead( stream, out arg6, s6, info6 );
|
||||
MySerializationHelpers.CreateAndRead( stream, out arg7, s7, info7 );
|
||||
}
|
||||
else
|
||||
{
|
||||
MySerializationHelpers.Write( stream, ref arg1, s1, info1 );
|
||||
MySerializationHelpers.Write( stream, ref arg2, s2, info2 );
|
||||
MySerializationHelpers.Write( stream, ref arg3, s3, info3 );
|
||||
MySerializationHelpers.Write( stream, ref arg4, s4, info4 );
|
||||
MySerializationHelpers.Write( stream, ref arg5, s5, info5 );
|
||||
MySerializationHelpers.Write( stream, ref arg6, s6, info6 );
|
||||
MySerializationHelpers.Write( stream, ref arg7, s7, info7 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
353
Torch/Managers/NetworkManager/NetworkManager.cs
Normal file
353
Torch/Managers/NetworkManager/NetworkManager.cs
Normal file
@@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using VRage;
|
||||
using VRage.Library.Collections;
|
||||
using VRage.Network;
|
||||
|
||||
namespace Torch.Managers.NetworkManager
|
||||
{
|
||||
public class NetworkManager
|
||||
{
|
||||
public NetworkManager()
|
||||
{
|
||||
if (ReflectionUnitTest())
|
||||
InitNetworkIntercept();
|
||||
}
|
||||
|
||||
private static NetworkManager _instance;
|
||||
public static NetworkManager Instance => _instance ?? (_instance = new NetworkManager());
|
||||
|
||||
private const string MyTransportLayerField = "TransportLayer";
|
||||
private const string TypeTableField = "m_typeTable";
|
||||
private const string TransportHandlersField = "m_handlers";
|
||||
private MyTypeTable m_typeTable = new MyTypeTable();
|
||||
private HashSet<NetworkHandlerBase> _networkHandlers = new HashSet<NetworkHandlerBase>();
|
||||
|
||||
private bool ReflectionUnitTest(bool suppress = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
var syncLayerType = typeof(MySyncLayer);
|
||||
var transportLayerField = syncLayerType.GetField( MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance );
|
||||
|
||||
if ( transportLayerField == null )
|
||||
throw new TypeLoadException( "Could not find internal type for TransportLayer" );
|
||||
|
||||
Type transportLayerType = transportLayerField.FieldType;
|
||||
|
||||
Type replicationLayerType = typeof(MyReplicationLayerBase);
|
||||
if ( !Reflection.HasField( replicationLayerType, TypeTableField ) )
|
||||
throw new TypeLoadException( "Could not find TypeTable field" );
|
||||
|
||||
if ( !Reflection.HasField( transportLayerType, TransportHandlersField ) )
|
||||
throw new TypeLoadException( "Could not find Handlers field" );
|
||||
|
||||
return true;
|
||||
}
|
||||
catch ( TypeLoadException ex )
|
||||
{
|
||||
//ApplicationLog.BaseLog.Error(ex);
|
||||
TorchBase.Instance.Log.WriteException( ex );
|
||||
if ( suppress )
|
||||
return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads the network intercept system
|
||||
/// </summary>
|
||||
public void InitNetworkIntercept()
|
||||
{
|
||||
m_typeTable = typeof(MyReplicationLayerBase).GetField(TypeTableField, BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(MyMultiplayer.ReplicationLayer) as MyTypeTable;
|
||||
//don't bother with nullchecks here, it was all handled in ReflectionUnitTest
|
||||
var transportType = typeof(MySyncLayer).GetField(MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance).FieldType;
|
||||
var transportInstance = typeof(MySyncLayer).GetField(MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(MyMultiplayer.Static.SyncLayer);
|
||||
var handlers = (Dictionary<MyMessageId, Action<MyPacket>>)transportType.GetField(TransportHandlersField, BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(transportInstance);
|
||||
|
||||
//remove Keen's network listener
|
||||
handlers.Remove(MyMessageId.RPC);
|
||||
//replace it with our own
|
||||
handlers.Add(MyMessageId.RPC, ProcessEvent);
|
||||
|
||||
//PrintDebug();
|
||||
|
||||
//ApplicationLog.Info("Initialized network intercept!");
|
||||
}
|
||||
|
||||
#region Network Intercept
|
||||
|
||||
/// <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 ProcessEvent(MyPacket packet)
|
||||
{
|
||||
if (_networkHandlers.Count == 0)
|
||||
{
|
||||
//pass the message back to the game server
|
||||
try
|
||||
{
|
||||
((MyReplicationLayer)MyMultiplayer.ReplicationLayer).ProcessEvent(packet);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TorchBase.Instance.Log.WriteException(ex);
|
||||
//ApplicationLog.Error(ex, "~Error processing event!");
|
||||
//crash after logging, bad things could happen if we continue on with bad data
|
||||
throw;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BitStream stream = new BitStream();
|
||||
stream.ResetRead(packet);
|
||||
|
||||
NetworkId networkId = stream.ReadNetworkId();
|
||||
//this value is unused, but removing this line corrupts the rest of the stream
|
||||
NetworkId blockedNetworkId = stream.ReadNetworkId();
|
||||
uint eventId = (uint)stream.ReadByte();
|
||||
|
||||
|
||||
CallSite site;
|
||||
IMyNetObject sendAs;
|
||||
object obj;
|
||||
if (networkId.IsInvalid) // Static event
|
||||
{
|
||||
site = m_typeTable.StaticEventTable.Get(eventId);
|
||||
sendAs = null;
|
||||
obj = null;
|
||||
}
|
||||
else // Instance event
|
||||
{
|
||||
sendAs = ((MyReplicationLayer)MyMultiplayer.ReplicationLayer).GetObjectByNetworkId(networkId);
|
||||
if (sendAs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var typeInfo = m_typeTable.Get(sendAs.GetType());
|
||||
int eventCount = typeInfo.EventTable.Count;
|
||||
if (eventId < eventCount) // Directly
|
||||
{
|
||||
obj = sendAs;
|
||||
site = typeInfo.EventTable.Get(eventId);
|
||||
}
|
||||
else // Through proxy
|
||||
{
|
||||
obj = ((IMyProxyTarget)sendAs).Target;
|
||||
typeInfo = m_typeTable.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
|
||||
bool handled = false;
|
||||
Parallel.ForEach(_networkHandlers, handler =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (handler.CanHandle(site))
|
||||
handled |= handler.Handle(packet.Sender.Value, site, stream, obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//ApplicationLog.Error(ex.ToString());
|
||||
TorchBase.Instance.Log.WriteException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
//one of the handlers wants us to discard this packet
|
||||
if (handled)
|
||||
return;
|
||||
|
||||
//pass the message back to the game server
|
||||
try
|
||||
{
|
||||
((MyReplicationLayer)MyMultiplayer.ReplicationLayer).ProcessEvent(packet);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TorchBase.Instance.Log.WriteException(ex);
|
||||
//ApplicationLog.Error(ex, "Error when returning control to game server!");
|
||||
//crash after logging, bad things could happen if we continue on with bad data
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterNetworkHandler(NetworkHandlerBase handler)
|
||||
{
|
||||
string handlerType = handler.GetType().FullName;
|
||||
List<NetworkHandlerBase> toRemove = new List<NetworkHandlerBase>();
|
||||
foreach (var item in _networkHandlers)
|
||||
{
|
||||
if (item.GetType().FullName == handlerType)
|
||||
{
|
||||
//if (ExtenderOptions.IsDebugging)
|
||||
// ApplicationLog.BaseLog.Error("Network handler already registered! " + handlerType);
|
||||
toRemove.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var oldHandler in toRemove)
|
||||
_networkHandlers.Remove(oldHandler);
|
||||
|
||||
_networkHandlers.Add(handler);
|
||||
}
|
||||
|
||||
public void RegisterNetworkHandlers(params NetworkHandlerBase[] handlers)
|
||||
{
|
||||
foreach (var handler in handlers)
|
||||
RegisterNetworkHandler(handler);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Network Injection
|
||||
|
||||
|
||||
/// <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
|
||||
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>
|
||||
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>
|
||||
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 > 6)
|
||||
throw new ArgumentOutOfRangeException(nameof(args), "Cannot pass more than 6 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[11];
|
||||
|
||||
arguments[0] = (obj == null ? TryGetStaticCallSite(method) : TryGetCallSite(method, obj));
|
||||
arguments[1] = endpoint;
|
||||
arguments[2] = 1f;
|
||||
arguments[3] = owner;
|
||||
|
||||
//copy supplied arguments into the reflection arguments
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
arguments[i + 4] = args[i];
|
||||
|
||||
//pad the array out with DBNull
|
||||
for (int j = args.Length + 4; j < 10; j++)
|
||||
arguments[j] = e;
|
||||
|
||||
arguments[10] = (IMyEventOwner)null;
|
||||
|
||||
//create an array of Types so we can create a generic method
|
||||
Type[] argTypes = new Type[8];
|
||||
|
||||
for (int k = 3; k < 11; k++)
|
||||
argTypes[k - 3] = arguments[k]?.GetType() ?? typeof(IMyEventOwner);
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (argTypes[i] != 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 = typeof(MyReplicationLayerBase).GetMethod("DispatchEvent", BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(argTypes);
|
||||
dispatch.Invoke(MyMultiplayer.ReplicationLayer, arguments);
|
||||
}
|
||||
|
||||
private static DBNull e = DBNull.Value;
|
||||
|
||||
/// <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
|
||||
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>
|
||||
public void RaiseStaticEvent(MethodInfo method, ulong steamId, params object[] args)
|
||||
{
|
||||
RaiseEvent(method, 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>
|
||||
public void RaiseStaticEvent(MethodInfo method, EndpointId endpoint, params object[] args)
|
||||
{
|
||||
RaiseEvent(method, null, endpoint, args);
|
||||
}
|
||||
|
||||
private CallSite TryGetStaticCallSite(MethodInfo method)
|
||||
{
|
||||
var methodLookup = (Dictionary<MethodInfo, CallSite>)typeof(MyEventTable).GetField("m_methodInfoLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(m_typeTable.StaticEventTable);
|
||||
CallSite result;
|
||||
if(!methodLookup.TryGetValue(method, out result))
|
||||
throw new MissingMemberException("Provided event target not found!");
|
||||
return result;
|
||||
}
|
||||
|
||||
private CallSite TryGetCallSite(MethodInfo method, object arg)
|
||||
{
|
||||
var typeInfo = m_typeTable.Get(arg.GetType());
|
||||
var methodLookup = (Dictionary<MethodInfo, CallSite>)typeof(MyEventTable).GetField("m_methodInfoLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(typeInfo.EventTable);
|
||||
CallSite result;
|
||||
if (!methodLookup.TryGetValue(method, out result))
|
||||
throw new MissingMemberException("Provided event target not found!");
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
119
Torch/Reflection.cs
Normal file
119
Torch/Reflection.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch
|
||||
{
|
||||
public static class Reflection
|
||||
{
|
||||
//private static readonly Logger Log = LogManager.GetLogger( "BaseLog" );
|
||||
public static bool HasMethod( Type objectType, string methodName )
|
||||
{
|
||||
return Reflection.HasMethod( objectType, methodName, null );
|
||||
}
|
||||
|
||||
|
||||
public static bool HasMethod( Type objectType, string methodName, Type[ ] argTypes )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( String.IsNullOrEmpty( methodName ) )
|
||||
return false;
|
||||
|
||||
if ( argTypes == null )
|
||||
{
|
||||
MethodInfo method = objectType.GetMethod( methodName );
|
||||
if ( method == null )
|
||||
method = objectType.GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy );
|
||||
if ( method == null && objectType.BaseType != null )
|
||||
method = objectType.BaseType.GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy );
|
||||
if ( method == null )
|
||||
{
|
||||
//Log.Error( "Failed to find method '" + methodName + "' in type '" + objectType.FullName + "'" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo method = objectType.GetMethod( methodName, argTypes );
|
||||
if ( method == null )
|
||||
method = objectType.GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy, Type.DefaultBinder, argTypes, null );
|
||||
if ( method == null && objectType.BaseType != null )
|
||||
method = objectType.BaseType.GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy, Type.DefaultBinder, argTypes, null );
|
||||
if ( method == null )
|
||||
{
|
||||
//Log.Error( "Failed to find method '" + methodName + "' in type '" + objectType.FullName + "'" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch ( AmbiguousMatchException aex )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch ( Exception ex )
|
||||
{
|
||||
//Log.Error( "Failed to find method '" + methodName + "' in type '" + objectType.FullName + "': " + ex.Message );
|
||||
//Log.Error( ex );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasField( Type objectType, string fieldName )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( String.IsNullOrEmpty( fieldName ) )
|
||||
return false;
|
||||
FieldInfo field = objectType.GetField( fieldName );
|
||||
if ( field == null )
|
||||
field = objectType.GetField( fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy );
|
||||
if ( field == null )
|
||||
field = objectType.BaseType.GetField( fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy );
|
||||
if ( field == null )
|
||||
{
|
||||
//Log.Error( "Failed to find field '" + fieldName + "' in type '" + objectType.FullName + "'" );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch ( Exception ex )
|
||||
{
|
||||
//Log.Error( "Failed to find field '" + fieldName + "' in type '" + objectType.FullName + "': " + ex.Message );
|
||||
//Log.Error( ex );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasProperty( Type objectType, string propertyName )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( String.IsNullOrEmpty( propertyName ) )
|
||||
return false;
|
||||
PropertyInfo prop = objectType.GetProperty( propertyName );
|
||||
if ( prop == null )
|
||||
prop = objectType.GetProperty( propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy );
|
||||
if ( prop == null )
|
||||
prop = objectType.BaseType.GetProperty( propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy );
|
||||
if ( prop == null )
|
||||
{
|
||||
//Log.Error( "Failed to find property '" + propertyName + "' in type '" + objectType.FullName + "'" );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch ( Exception ex )
|
||||
{
|
||||
//Log.Error( "Failed to find property '" + propertyName + "' in type '" + objectType.FullName + "': " + ex.Message );
|
||||
//Log.Error( ex );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -129,7 +129,11 @@
|
||||
<Compile Include="Commands\CommandSystem.cs" />
|
||||
<Compile Include="Commands\CommandTree.cs" />
|
||||
<Compile Include="Logger.cs" />
|
||||
<Compile Include="Managers\ChatManager.cs" />
|
||||
<Compile Include="Managers\NetworkManager\NetworkHandlerBase.cs" />
|
||||
<Compile Include="Managers\NetworkManager\NetworkManager.cs" />
|
||||
<Compile Include="MultiplayerManager.cs" />
|
||||
<Compile Include="Reflection.cs" />
|
||||
<Compile Include="TorchBase.cs" />
|
||||
<Compile Include="Player.cs" />
|
||||
<Compile Include="Collections\PlayerInfoCache.cs" />
|
||||
|
Reference in New Issue
Block a user