175 lines
6.1 KiB
C#
175 lines
6.1 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Reflection;
|
|||
|
using System.Linq;
|
|||
|
namespace ln.type.rpc
|
|||
|
{
|
|||
|
public class PublishAttribute : Attribute
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public class RPCContainer
|
|||
|
{
|
|||
|
Dictionary<string, RPCModule> modules = new Dictionary<string, RPCModule>();
|
|||
|
|
|||
|
Func<RPCContainer, RPCModule, MethodInfo, bool> checkAccessHook;
|
|||
|
|
|||
|
public RPCContainer()
|
|||
|
{
|
|||
|
}
|
|||
|
public RPCContainer(Func<RPCContainer, RPCModule, MethodInfo, bool> checkAccessHook)
|
|||
|
{
|
|||
|
this.checkAccessHook = checkAccessHook;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void Add(object moduleInstance) => Add(moduleInstance.GetType().Name, moduleInstance);
|
|||
|
public void Add(string moduleName,object moduleInstance)
|
|||
|
{
|
|||
|
if (modules.ContainsKey(moduleName))
|
|||
|
throw new ArgumentOutOfRangeException(nameof(moduleName), "Module with same name already added");
|
|||
|
|
|||
|
RPCModule rpcModule = new RPCModule(this,moduleName,moduleInstance);
|
|||
|
modules.Add(moduleName, rpcModule);
|
|||
|
}
|
|||
|
|
|||
|
public void Remove(string moduleName)
|
|||
|
{
|
|||
|
modules.Remove(moduleName);
|
|||
|
}
|
|||
|
public void Remove(object moduleInstance)
|
|||
|
{
|
|||
|
foreach (RPCModule module in modules.Values)
|
|||
|
{
|
|||
|
if (module.ModuleInstance == moduleInstance)
|
|||
|
{
|
|||
|
modules.Remove(module.Name);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public RPCResult Invoke(RPCCall call)
|
|||
|
{
|
|||
|
if ((call.ModuleName != null) && !modules.ContainsKey(call.ModuleName))
|
|||
|
throw new KeyNotFoundException(call.ModuleName);
|
|||
|
|
|||
|
RPCModule rpcModule = call.ModuleName != null ? modules[call.ModuleName] : modules[""];
|
|||
|
return rpcModule.Invoke(call);
|
|||
|
}
|
|||
|
|
|||
|
public class RPCModule
|
|||
|
{
|
|||
|
public RPCContainer Container { get; }
|
|||
|
|
|||
|
public String Name { get; }
|
|||
|
public object ModuleInstance { get; }
|
|||
|
|
|||
|
public Dictionary<string, MethodInfo> methodInfos = new Dictionary<string, MethodInfo>();
|
|||
|
|
|||
|
public RPCModule(RPCContainer container,string moduleName,object instance)
|
|||
|
{
|
|||
|
Container = container;
|
|||
|
|
|||
|
Name = moduleName;
|
|||
|
ModuleInstance = instance;
|
|||
|
|
|||
|
Initialize(ModuleInstance.GetType());
|
|||
|
}
|
|||
|
public RPCModule(RPCContainer container,string moduleName,Type type)
|
|||
|
{
|
|||
|
Container = container;
|
|||
|
|
|||
|
Name = moduleName;
|
|||
|
ModuleInstance = null;
|
|||
|
|
|||
|
Initialize(type);
|
|||
|
}
|
|||
|
|
|||
|
private void Initialize(Type type)
|
|||
|
{
|
|||
|
foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
|
|||
|
{
|
|||
|
if (methodInfo.IsStatic || !Object.ReferenceEquals(ModuleInstance,null) )
|
|||
|
{
|
|||
|
if (methodInfo.IsPublic || (methodInfo.GetCustomAttribute<PublishAttribute>() != null))
|
|||
|
{
|
|||
|
methodInfos.Add(methodInfo.Name, methodInfo);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public RPCResult Invoke(RPCCall call)
|
|||
|
{
|
|||
|
if (!Name.Equals(call.ModuleName) && (call.ModuleName != null))
|
|||
|
throw new ArgumentOutOfRangeException(nameof(call.ModuleName), "RPC: Invoke called for wrong module");
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
if (!methodInfos.ContainsKey(call.MethodName))
|
|||
|
throw new KeyNotFoundException(call.MethodName);
|
|||
|
|
|||
|
MethodInfo methodInfo = methodInfos[call.MethodName];
|
|||
|
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
|||
|
|
|||
|
object[] parameters = new object[parameterInfos.Length];
|
|||
|
for (int n = 0; n < parameters.Length; n++)
|
|||
|
{
|
|||
|
Type pType = parameterInfos[n].ParameterType;
|
|||
|
if (pType.Equals(typeof(Guid)))
|
|||
|
{
|
|||
|
parameters[n] = Guid.Parse(call.Parameters[n].ToString());
|
|||
|
} else if (pType.IsArray)
|
|||
|
{
|
|||
|
Type eType = pType.GetElementType();
|
|||
|
object[] src = (object[])call.Parameters[n];
|
|||
|
Array a = Array.CreateInstance(eType, src.Length);
|
|||
|
|
|||
|
for (int m=0;m<src.Length;m++)
|
|||
|
{
|
|||
|
a.SetValue(Convert.ChangeType(src[m], eType), m);
|
|||
|
}
|
|||
|
|
|||
|
parameters[n] = a;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
parameters[n] = ImplicitCast(parameterInfos[n].ParameterType, call.Parameters[n]);
|
|||
|
if (parameters[n] == null)
|
|||
|
parameters[n] = Convert.ChangeType(call.Parameters[n], parameterInfos[n].ParameterType);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
object result = null;
|
|||
|
if ((Container.checkAccessHook == null) || Container.checkAccessHook(Container,this,methodInfo))
|
|||
|
result = methodInfo.Invoke(ModuleInstance, parameters);
|
|||
|
|
|||
|
return new RPCResult(call, result);
|
|||
|
} catch (Exception e)
|
|||
|
{
|
|||
|
return new RPCResult(call, e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public object ImplicitCast(Type targetType,object value)
|
|||
|
{
|
|||
|
foreach (MethodInfo methodInfo in targetType.GetMethods(BindingFlags.Public | BindingFlags.Static))
|
|||
|
{
|
|||
|
if (methodInfo.Name.Equals("op_Implicit") && methodInfo.ReturnType.Equals(targetType))
|
|||
|
{
|
|||
|
ParameterInfo parameterInfo = methodInfo.GetParameters().FirstOrDefault();
|
|||
|
if ((parameterInfo != null)&&(parameterInfo.ParameterType.Equals(value.GetType())))
|
|||
|
{
|
|||
|
return methodInfo.Invoke(null, new object[] { value });
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|