352 lines
13 KiB
C#
352 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using InnerSpaceAPI;
|
|
using LavishScriptAPI;
|
|
using LavishScriptAPI.Interfaces;
|
|
using MRBot.IsxEq2.Extensions;
|
|
|
|
namespace MRBot.IsxEq2.Helpers
|
|
{
|
|
/// <summary>
|
|
/// CallbackDelegate used in trace logging.
|
|
/// </summary>
|
|
/// <param name="method"></param>
|
|
/// <param name="args"></param>
|
|
public delegate void CallbackDelegate(string method, string args);
|
|
/// <summary>
|
|
/// Class used for trace logging.
|
|
/// </summary>
|
|
public class Tracing
|
|
{
|
|
/// <summary>
|
|
/// Callback to use for logging.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")]
|
|
public static CallbackDelegate Callback;
|
|
|
|
/// <summary>
|
|
/// Add a callback delegate for trace logging.
|
|
/// </summary>
|
|
/// <param name="callbackDelegate"></param>
|
|
public static void AddCallback(CallbackDelegate callbackDelegate)
|
|
{
|
|
Callback = callbackDelegate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the callback delegate.
|
|
/// </summary>
|
|
public static void RemoveCallback()
|
|
{
|
|
Callback = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use the callback delegate to send a log message.
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <param name="args"></param>
|
|
public static void SendCallback(string message, params object[] args)
|
|
{
|
|
if (Callback == null) return;
|
|
|
|
var argString = new StringBuilder();
|
|
if (args.Length > 0)
|
|
{
|
|
argString.Append(args[0].ToString());
|
|
}
|
|
for (int idx = 1; idx < args.Length; idx++)
|
|
{
|
|
argString.Append(String.Format(CultureInfo.InvariantCulture, ", {0}", args[idx]));
|
|
}
|
|
|
|
Callback(message, argString.ToString());
|
|
}
|
|
}
|
|
|
|
public static class Util
|
|
{
|
|
private static readonly Dictionary<Type, Type> _implementingTypesByInterfaceType = new Dictionary<Type, Type>();
|
|
private static readonly Dictionary<Type, ConstructorInfo> _constructorInfoByType = new Dictionary<Type, ConstructorInfo>();
|
|
|
|
private static T[] PrefixArray<T>(T first, T[] rest)
|
|
{
|
|
var newArray = new T[rest.Length + 1];
|
|
|
|
newArray[0] = first;
|
|
|
|
for (var i = 0; i < rest.Length; i++)
|
|
newArray[i + 1] = rest[i];
|
|
|
|
return newArray;
|
|
}
|
|
|
|
private static Type GetImplementingTypeForInterfaceType(Type interfaceType)
|
|
{
|
|
if (!_implementingTypesByInterfaceType.ContainsKey(interfaceType))
|
|
{
|
|
var implementingType = interfaceType.Assembly.GetTypes()
|
|
.Where(t => t.IsClass)
|
|
.FirstOrDefault(interfaceType.IsAssignableFrom);
|
|
|
|
if (implementingType == null)
|
|
{
|
|
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not find implementing type for interface type {0}.", interfaceType.Name));
|
|
}
|
|
|
|
_implementingTypesByInterfaceType.Add(interfaceType, implementingType);
|
|
}
|
|
|
|
return _implementingTypesByInterfaceType[interfaceType];
|
|
}
|
|
|
|
private static ConstructorInfo GetConstructorInfoForType(Type type)
|
|
{
|
|
if (!_constructorInfoByType.ContainsKey(type))
|
|
{
|
|
ConstructorInfo constructorInfo;
|
|
if (type.IsInterface)
|
|
{
|
|
var implementingType = GetImplementingTypeForInterfaceType(type);
|
|
constructorInfo = implementingType.GetConstructor(new[] { typeof(LavishScriptObject) });
|
|
}
|
|
else
|
|
{
|
|
constructorInfo = type.GetConstructor(new[] { typeof(LavishScriptObject) });
|
|
}
|
|
|
|
if (constructorInfo == null)
|
|
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not find a constructor for type {0}.", type.Name));
|
|
|
|
_constructorInfoByType.Add(type, constructorInfo);
|
|
}
|
|
|
|
return _constructorInfoByType[type];
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")]
|
|
private static List<T> IndexToLavishScriptObjectList<T>(LavishScriptObject index, string lsTypeName)
|
|
{
|
|
|
|
var list = new List<T>();
|
|
var count = index.GetMember<int>("Used");
|
|
|
|
if (count == 0)
|
|
{
|
|
InnerSpace.Echo($"IndexToLavishScriptObjectList: count is 0");
|
|
return list;
|
|
}
|
|
|
|
//Tracing.SendCallback(methodName, "get constructor info");
|
|
var constructorInfo = GetConstructorInfoForType(typeof(T));
|
|
|
|
//Tracing.SendCallback(methodName, "loop add items");
|
|
for (var i = 1; i <= count; i++)
|
|
{
|
|
var objectLso = index.GetIndex(i.ToString(CultureInfo.CurrentCulture));
|
|
|
|
if (LavishScriptObject.IsNullOrInvalid(objectLso))
|
|
{
|
|
Tracing.SendCallback(String.Format(CultureInfo.InvariantCulture, "Error: Index contains invalid LSO. NewObject will fail; aborting."));
|
|
InnerSpace.Echo($"Error: Index contains invalid LSO at index {i}. NewObject will fail; aborting.");
|
|
continue;
|
|
}
|
|
|
|
var objectId = objectLso.GetString("ID");
|
|
|
|
if (objectId == null)
|
|
{
|
|
Tracing.SendCallback(String.Format(CultureInfo.InvariantCulture, "Error: LStype \"{0}\" has no ID member. NewObject will fail; aborting.", lsTypeName));
|
|
InnerSpace.Echo($"Error: LStype {lsTypeName} has no ID member. NewObject will fail; abortig.");
|
|
return list;
|
|
}
|
|
|
|
if (objectId == string.Empty)
|
|
{
|
|
Tracing.SendCallback(String.Format(CultureInfo.InvariantCulture, "Error: LStype \"{0}\" has an ID member but it is returning an empty string. NewObject will fail; aborting.", lsTypeName));
|
|
InnerSpace.Echo($"Error: LStype {lsTypeName} has an ID member but it is returning an empty string. NewObject will fail; aborting.");
|
|
return list;
|
|
}
|
|
|
|
var lsObject = LavishScript.Objects.NewObject(lsTypeName, objectId);
|
|
var item = (T)constructorInfo.Invoke(new object[] { lsObject });
|
|
list.Add(item);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private static List<T> IndexToStructList<T>(LavishScriptObject index)
|
|
{
|
|
InnerSpace.Echo($"IndexToStructList: {typeof(T).Name}");
|
|
var list = new List<T>();
|
|
var count = index.GetMember<int>("Used");
|
|
|
|
for (var i = 1; i <= count; i++)
|
|
list.Add(index.GetIndex<T>(i.ToString(CultureInfo.CurrentCulture)));
|
|
|
|
return list;
|
|
}
|
|
|
|
public static List<T> IndexToList<T>(LavishScriptObject index, string lsTypeName)
|
|
{
|
|
InnerSpace.Echo($"IndexToList: {typeof(T).Name}");
|
|
return (typeof(LavishScriptObject)).IsAssignableFrom(typeof(T)) ? IndexToLavishScriptObjectList<T>(index, lsTypeName) : IndexToStructList<T>(index);
|
|
}
|
|
|
|
public static T GetIndexMember<T>(LavishScriptObject index, int number)
|
|
{
|
|
if (typeof(T).IsSubclassOf(typeof(LavishScriptObject)))
|
|
return (T)typeof(T).GetConstructor(new[] { typeof(LavishScriptObject) }).Invoke(new object[] { index.GetIndex(number.ToString(CultureInfo.CurrentCulture)) });
|
|
return index.GetIndex<T>(number.ToString(CultureInfo.CurrentCulture));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Translate an index of a given LavishScript type returned from the given method on the given object to a List of a .NET datatype equivalent.
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
/// <param name="methodName"></param>
|
|
/// <param name="lsTypeName"></param>
|
|
/// <param name="args"></param>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <returns></returns>
|
|
public static List<T> GetListFromMethod<T>(this ILSObject obj, string methodName, string lsTypeName, params string[] args)
|
|
{
|
|
if (obj == null || !obj.IsValid)
|
|
{
|
|
InnerSpace.Echo("GetListFromMethod: obj is null or invalid");
|
|
return null;
|
|
}
|
|
|
|
|
|
using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName))
|
|
{
|
|
string[] allargs;
|
|
if (args.Length > 0)
|
|
{
|
|
allargs = PrefixArray(index.LSReference, args);
|
|
}
|
|
else
|
|
{
|
|
allargs = new string[1];
|
|
allargs[0] = index.LSReference;
|
|
}
|
|
|
|
if (!obj.ExecuteMethod(methodName, allargs))
|
|
{
|
|
InnerSpace.Echo($"Failed to execute method {methodName}");
|
|
return null;
|
|
}
|
|
|
|
|
|
using (var used = index.GetMember("Used"))
|
|
{
|
|
if (LavishScriptObject.IsNullOrInvalid(used))
|
|
{
|
|
InnerSpace.Echo($"LSO.IsNullOrInvalid (used)");
|
|
return null;
|
|
}
|
|
|
|
InnerSpace.Echo($"used: {used.GetValue<int>()}");
|
|
|
|
}
|
|
|
|
var list = IndexToList<T>(index, lsTypeName);
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|
|
internal static T GetFromIndexMethod<T>(ILSObject obj, string methodName, string lsTypeName, int number, params string[] args)
|
|
{
|
|
// argument is 0-based
|
|
number += 1;
|
|
|
|
if (obj == null || !obj.IsValid || number <= 0)
|
|
return default;
|
|
|
|
using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName))
|
|
{
|
|
var allargs = PrefixArray(index.LSReference, args);
|
|
|
|
if (!obj.ExecuteMethod(methodName, allargs))
|
|
return default;
|
|
|
|
using (var used = index.GetMember("Used"))
|
|
{
|
|
// if it failed or we want one off the end, return
|
|
if (LavishScriptObject.IsNullOrInvalid(used) || used.GetValue<int>() < number)
|
|
return default;
|
|
}
|
|
|
|
var member = GetIndexMember<T>(index, number);
|
|
return member;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Translate an index of a given LavishScript type returned from the given member on the given object to a List of a .NET datatype equivalent.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="obj"></param>
|
|
/// <param name="memberName"></param>
|
|
/// <param name="lsTypeName"></param>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
public static List<T> GetListFromMember<T>(this LavishScriptObject obj, string memberName, string lsTypeName, params string[] args)
|
|
{
|
|
|
|
if (obj == null || !obj.IsValid)
|
|
return null;
|
|
|
|
//Tracing.SendCallback(methodName, "new index");
|
|
using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName))
|
|
{
|
|
//Tracing.SendCallback(methodName, "arg condensing");
|
|
var allargs = PrefixArray(index.LSReference, args);
|
|
|
|
//Tracing.SendCallback(methodName, "getmember retval");
|
|
using (var retval = obj.GetMember(memberName, allargs))
|
|
{
|
|
if (LavishScriptObject.IsNullOrInvalid(retval))
|
|
return null;
|
|
}
|
|
|
|
//Tracing.SendCallback(methodName, "index to list");
|
|
var list = IndexToList<T>(index, lsTypeName);
|
|
|
|
//Tracing.SendCallback(methodName, "invalidate");
|
|
|
|
//Tracing.SendCallback(methodName, "return");
|
|
return list;
|
|
}
|
|
}
|
|
|
|
internal static T GetFromIndexMember<T>(ILSObject obj, string memberName, string lsTypeName, int number, params string[] args)
|
|
{
|
|
// argument is 0-based
|
|
number += 1;
|
|
|
|
if (obj == null || !obj.IsValid)
|
|
return default;
|
|
|
|
using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName))
|
|
{
|
|
var allargs = PrefixArray(index.LSReference, args);
|
|
|
|
using (var retval = obj.GetMember(memberName, allargs))
|
|
{
|
|
if (LavishScriptObject.IsNullOrInvalid(retval) || retval.GetValue<int>() < number)
|
|
return default;
|
|
}
|
|
|
|
var member = GetIndexMember<T>(index, number);
|
|
return member;
|
|
}
|
|
}
|
|
}
|
|
} |