Files
OpenRA/OpenRA.Game/Scripting/ScriptTypes.cs
2020-08-19 18:11:07 +01:00

193 lines
4.5 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using Eluant;
namespace OpenRA.Scripting
{
public static class LuaValueExts
{
public static Type WrappedClrType(this LuaValue value)
{
if (value.TryGetClrObject(out var inner))
return inner.GetType();
return value.GetType();
}
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
{
var ret = value.TryGetClrValue(typeof(T), out object temp);
clrObject = ret ? (T)temp : default(T);
return ret;
}
public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject)
{
// Is t a nullable?
// If yes, get the underlying type
var nullable = Nullable.GetUnderlyingType(t);
if (nullable != null)
t = nullable;
// Value wraps a CLR object
if (value.TryGetClrObject(out var temp))
{
if (temp.GetType() == t)
{
clrObject = temp;
return true;
}
}
if (value is LuaNil && !t.IsValueType)
{
clrObject = null;
return true;
}
if (value is LuaBoolean && t.IsAssignableFrom(typeof(bool)))
{
clrObject = value.ToBoolean();
return true;
}
if (value is LuaNumber && t.IsAssignableFrom(typeof(double)))
{
clrObject = value.ToNumber().Value;
return true;
}
// Need an explicit test for double -> int
// TODO: Lua 5.3 will introduce an integer type, so this will be able to go away
if (value is LuaNumber && t.IsAssignableFrom(typeof(int)))
{
clrObject = (int)value.ToNumber().Value;
return true;
}
if (value is LuaNumber && t.IsAssignableFrom(typeof(short)))
{
clrObject = (short)value.ToNumber().Value;
return true;
}
if (value is LuaNumber && t.IsAssignableFrom(typeof(byte)))
{
clrObject = (byte)value.ToNumber().Value;
return true;
}
if (value is LuaString && t.IsAssignableFrom(typeof(string)))
{
clrObject = value.ToString();
return true;
}
if (value is LuaFunction && t.IsAssignableFrom(typeof(LuaFunction)))
{
clrObject = value;
return true;
}
if (value is LuaTable && t.IsAssignableFrom(typeof(LuaTable)))
{
clrObject = value;
return true;
}
// Translate LuaTable<int, object> -> object[]
if (value is LuaTable && t.IsArray)
{
var innerType = t.GetElementType();
var table = (LuaTable)value;
var array = Array.CreateInstance(innerType, table.Count);
var i = 0;
foreach (var kv in table)
{
using (kv.Key)
{
object element;
if (innerType == typeof(LuaValue))
element = kv.Value;
else
{
var elementHasClrValue = kv.Value.TryGetClrValue(innerType, out element);
if (!elementHasClrValue || !(element is LuaValue))
kv.Value.Dispose();
if (!elementHasClrValue)
throw new LuaException("Unable to convert table value of type {0} to type {1}".F(kv.Value.WrappedClrType(), innerType));
}
array.SetValue(element, i++);
}
}
clrObject = array;
return true;
}
// Value isn't of the requested type.
// Set a default output value and return false
// Value types are assumed to specify a default constructor
clrObject = t.IsValueType ? Activator.CreateInstance(t) : null;
return false;
}
public static LuaValue ToLuaValue(this object obj, ScriptContext context)
{
if (obj is LuaValue)
return (LuaValue)obj;
if (obj == null)
return LuaNil.Instance;
if (obj is double)
return (double)obj;
if (obj is int)
return (int)obj;
if (obj is bool)
return (bool)obj;
if (obj is string)
return (string)obj;
if (obj is IScriptBindable)
{
// Object needs additional notification / context
var notify = obj as IScriptNotifyBind;
notify?.OnScriptBind(context);
return new LuaCustomClrObject(obj);
}
if (obj is Array)
{
var array = (Array)obj;
var i = 1;
var table = context.CreateTable();
foreach (var x in array)
using (LuaValue key = i++, value = x.ToLuaValue(context))
table.Add(key, value);
return table;
}
throw new InvalidOperationException("Cannot convert type '{0}' to Lua. Class must implement IScriptBindable.".F(obj.GetType()));
}
}
}