487 lines
17 KiB
C#
487 lines
17 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using EpicGames.Core;
|
|
using EpicGames.UHT.Types;
|
|
using EpicGames.UHT.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace ScriptGeneratorUbtPlugin
|
|
{
|
|
static class LuaScriptCodeGeneratorStringBuilderExtensinos
|
|
{
|
|
public static StringBuilder AppendLuaWrapperFunctionDeclaration(this StringBuilder builder, UhtClass classObj, string functionName)
|
|
{
|
|
return builder.Append("int32 ").Append(classObj.EngineName).Append('_').Append(functionName).Append("(lua_State* InScriptContext)");
|
|
}
|
|
|
|
public static StringBuilder AppendLuaObjectDeclarationFromContext(this StringBuilder builder, UhtClass classObj)
|
|
{
|
|
return builder.Append("UObject* Obj = (").Append(classObj.SourceName).Append("*)lua_touserdata(InScriptContext, 1);");
|
|
}
|
|
|
|
public static StringBuilder AppendLuaReturnValueHandler(this StringBuilder builder, UhtClass _ /* classObj */, UhtProperty? returnValue, string? returnValueName)
|
|
{
|
|
if (returnValue != null)
|
|
{
|
|
if (returnValue is UhtIntProperty)
|
|
{
|
|
builder.Append("lua_pushinteger(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (returnValue is UhtFloatProperty)
|
|
{
|
|
builder.Append("lua_pushnumber(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (returnValue is UhtStrProperty)
|
|
{
|
|
builder.Append("lua_pushstring(InScriptContext, TCHAR_TO_ANSI(*").Append(returnValueName).Append("));\r\n");
|
|
}
|
|
else if (returnValue is UhtNameProperty)
|
|
{
|
|
builder.Append("lua_pushstring(InScriptContext, TCHAR_TO_ANSI(*").Append(returnValueName).Append(".ToString()));\r\n");
|
|
}
|
|
else if (returnValue is UhtBoolProperty)
|
|
{
|
|
builder.Append("lua_pushboolean(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (returnValue is UhtStructProperty structProperty)
|
|
{
|
|
if (structProperty.ScriptStruct.EngineName == "Vector2D")
|
|
{
|
|
builder.Append("FLuaVector2D::Return(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Vector")
|
|
{
|
|
builder.Append("FLuaVector::Return(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Vector4")
|
|
{
|
|
builder.Append("FLuaVector4::Return(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Quat")
|
|
{
|
|
builder.Append("FLuaQuat::Return(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "LinearColor")
|
|
{
|
|
builder.Append("FLuaLinearColor::Return(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Color")
|
|
{
|
|
builder.Append("FLuaLinearColor::Return(InScriptContext, FLinearColor(").Append(returnValueName).Append("));\r\n");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Transform")
|
|
{
|
|
builder.Append("FLuaTransform::Return(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else
|
|
{
|
|
throw new UhtIceException($"Unsupported function return value struct type: {structProperty.ScriptStruct.EngineName}");
|
|
}
|
|
}
|
|
else if (returnValue is UhtObjectPropertyBase)
|
|
{
|
|
builder.Append("lua_pushlightuserdata(InScriptContext, ").Append(returnValueName).Append(");\r\n");
|
|
}
|
|
else
|
|
{
|
|
throw new UhtIceException($"Unsupported function return type: {returnValue.GetType().Name}");
|
|
}
|
|
builder.Append("\treturn 1;");
|
|
}
|
|
else
|
|
{
|
|
builder.Append("return 0;");
|
|
}
|
|
return builder;
|
|
}
|
|
}
|
|
|
|
internal class LuaScriptCodeGenerator : ScriptCodeGeneratorBase
|
|
{
|
|
public LuaScriptCodeGenerator(IUhtExportFactory factory)
|
|
: base(factory)
|
|
{
|
|
}
|
|
|
|
protected override bool CanExportClass(UhtClass classObj)
|
|
{
|
|
if (!base.CanExportClass(classObj))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (UhtClass? current = classObj; current != null; current = current.SuperClass)
|
|
{
|
|
foreach (UhtType child in current.Children)
|
|
{
|
|
if (child is UhtFunction function)
|
|
{
|
|
if (CanExportFunction(classObj, function))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (UhtType child in classObj.Children)
|
|
{
|
|
if (child is UhtProperty property)
|
|
{
|
|
if (CanExportProperty(classObj, property))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override bool CanExportFunction(UhtClass classObj, UhtFunction function)
|
|
{
|
|
if (!base.CanExportFunction(classObj, function))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (UhtType child in function.Children)
|
|
{
|
|
if (child is UhtProperty property)
|
|
{
|
|
if (!IsPropertyTypeSupported(property))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected override bool CanExportProperty(UhtClass classObj, UhtProperty property)
|
|
{
|
|
return property.PropertyFlags.HasAnyFlags(EPropertyFlags.Edit) && IsPropertyTypeSupported(property);
|
|
}
|
|
|
|
private static bool IsPropertyTypeSupported(UhtProperty property)
|
|
{
|
|
if (property is UhtStructProperty structProperty)
|
|
{
|
|
return
|
|
structProperty.ScriptStruct.EngineName == "Vector2D" ||
|
|
structProperty.ScriptStruct.EngineName == "Vector" ||
|
|
structProperty.ScriptStruct.EngineName == "Vector4" ||
|
|
structProperty.ScriptStruct.EngineName == "Quat" ||
|
|
structProperty.ScriptStruct.EngineName == "LinearColor" ||
|
|
structProperty.ScriptStruct.EngineName == "Color" ||
|
|
structProperty.ScriptStruct.EngineName == "Transform";
|
|
}
|
|
else if (
|
|
property is UhtLazyObjectPtrProperty ||
|
|
property is UhtSoftObjectProperty ||
|
|
property is UhtSoftClassProperty ||
|
|
property is UhtWeakObjectPtrProperty)
|
|
{
|
|
return false;
|
|
}
|
|
else if (
|
|
property is UhtIntProperty ||
|
|
property is UhtFloatProperty ||
|
|
property is UhtStrProperty ||
|
|
property is UhtNameProperty ||
|
|
property is UhtBoolProperty ||
|
|
property is UhtObjectPropertyBase)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override void ExportClass(StringBuilder builder, UhtClass classObj)
|
|
{
|
|
builder.Append("#pragma once\r\n\r\n");
|
|
|
|
List<UhtFunction> functions = classObj.Children.Where(x => x is UhtFunction).Cast<UhtFunction>().Reverse().ToList();
|
|
|
|
//ETSTODO - Functions are reversed in the engine
|
|
for (UhtClass? current = classObj; current != null; current = current.SuperClass)
|
|
{
|
|
foreach (UhtFunction function in current.Functions.Reverse())
|
|
{
|
|
if (CanExportFunction(classObj, function))
|
|
{
|
|
ExportFunction(builder, classObj, function);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (UhtClass? current = classObj; current != null; current = current.SuperClass)
|
|
{
|
|
foreach (UhtType child in current.Children)
|
|
{
|
|
if (child is UhtProperty property && CanExportProperty(classObj, property))
|
|
{
|
|
ExportProperty(builder, classObj, property);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!classObj.ClassFlags.HasAnyFlags(EClassFlags.Abstract))
|
|
{
|
|
builder.AppendLuaWrapperFunctionDeclaration(classObj, "New").Append("\r\n");
|
|
builder.Append("{\r\n");
|
|
builder.Append("\tUObject* Outer = (UObject*)lua_touserdata(InScriptContext, 1);\r\n");
|
|
builder.Append("\tFName Name = FName(luaL_checkstring(InScriptContext, 2));\r\n");
|
|
builder.Append("\tUObject* Obj = NewObject<").Append(classObj.SourceName).Append(">(Outer, Name);\r\n");
|
|
builder.Append("\tif (Obj)\r\n\t{\r\n");
|
|
builder.Append("\t\tFScriptObjectReferencer::Get().AddObjectReference(Obj);\r\n");
|
|
builder.Append("\t}\r\n");
|
|
builder.Append("\tlua_pushlightuserdata(InScriptContext, Obj);\r\n");
|
|
builder.Append("\treturn 1;\r\n");
|
|
builder.Append("}\r\n\r\n");
|
|
|
|
builder.AppendLuaWrapperFunctionDeclaration(classObj, "Destroy").Append("\r\n");
|
|
builder.Append("{\r\n");
|
|
builder.Append('\t').AppendLuaObjectDeclarationFromContext(classObj).Append("\r\n");
|
|
builder.Append("\tif (Obj)\r\n\t{\r\n");
|
|
builder.Append("\t\tFScriptObjectReferencer::Get().RemoveObjectReference(Obj);\r\n");
|
|
builder.Append("\t}\r\n");
|
|
builder.Append("\treturn 0;\r\n");
|
|
builder.Append("}\r\n\r\n");
|
|
}
|
|
|
|
// Class: Equivalent of StaticClass()
|
|
builder.AppendLuaWrapperFunctionDeclaration(classObj, "Class").Append("\r\n");
|
|
builder.Append("{\r\n");
|
|
builder.Append("\tUClass* Class = ").Append(classObj.SourceName).Append("::StaticClass();\r\n");
|
|
builder.Append("\tlua_pushlightuserdata(InScriptContext, Class);\r\n");
|
|
builder.Append("\treturn 1;\r\n");
|
|
builder.Append("}\r\n\r\n");
|
|
|
|
// Library
|
|
builder.Append("static const luaL_Reg ").Append(classObj.EngineName).Append("_Lib[] =\r\n");
|
|
builder.Append("{\r\n");
|
|
if (!classObj.ClassFlags.HasAnyFlags(EClassFlags.Abstract))
|
|
{
|
|
builder.Append("\t{ \"New\", ").Append(classObj.EngineName).Append("_New },\r\n");
|
|
builder.Append("\t{ \"Destroy\", ").Append(classObj.EngineName).Append("_Destroy },\r\n");
|
|
builder.Append("\t{ \"Class\", ").Append(classObj.EngineName).Append("_Class },\r\n");
|
|
}
|
|
|
|
//ETSTODO - Functions are reversed in the engine
|
|
for (UhtClass? current = classObj; current != null; current = current.SuperClass)
|
|
{
|
|
foreach (UhtFunction function in current.Functions.Reverse())
|
|
{
|
|
if (CanExportFunction(classObj, function))
|
|
{
|
|
builder.Append("\t{ \"").Append(function.SourceName).Append("\", ").Append(classObj.EngineName).Append('_').Append(function.SourceName).Append(" },\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (UhtClass? current = classObj; current != null; current = current.SuperClass)
|
|
{
|
|
foreach (UhtType child in current.Children)
|
|
{
|
|
if (child is UhtProperty property && CanExportProperty(classObj, property))
|
|
{
|
|
builder.Append("\t{ \"Get_").Append(property.SourceName).Append("\", ").Append(classObj.EngineName).Append("_Get_").Append(property.SourceName).Append(" },\r\n");
|
|
builder.Append("\t{ \"Set_").Append(property.SourceName).Append("\", ").Append(classObj.EngineName).Append("_Set_").Append(property.SourceName).Append(" },\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
builder.Append("\t{ NULL, NULL }\r\n");
|
|
builder.Append("};\r\n\r\n");
|
|
}
|
|
|
|
protected void ExportFunction(StringBuilder builder, UhtClass classObj, UhtFunction function)
|
|
{
|
|
UhtClass? functionSuper = null;
|
|
if (function.Outer != null && function.Outer != classObj && function.Outer is UhtClass outerClass && base.CanExportClass(outerClass))
|
|
{
|
|
functionSuper = outerClass;
|
|
}
|
|
|
|
builder.AppendLuaWrapperFunctionDeclaration(classObj, function.SourceName).Append("\r\n");
|
|
builder.Append("{\r\n");
|
|
if (functionSuper == null)
|
|
{
|
|
builder.Append('\t').AppendLuaObjectDeclarationFromContext(classObj).Append("\r\n");
|
|
AppendFunctionDispatch(builder, classObj, function);
|
|
string returnValueName = function.ReturnProperty != null ? $"Params.{function.ReturnProperty.SourceName}" : String.Empty;
|
|
builder.Append('\t').AppendLuaReturnValueHandler(classObj, function.ReturnProperty, returnValueName).Append("\r\n");
|
|
}
|
|
else
|
|
{
|
|
builder.Append("\treturn ").Append(functionSuper.EngineName).Append('_').Append(function.SourceName).Append("(InScriptContext);\r\n");
|
|
}
|
|
builder.Append("}\r\n\r\n");
|
|
}
|
|
|
|
protected void ExportProperty(StringBuilder builder, UhtClass classObj, UhtProperty property)
|
|
{
|
|
UhtClass? propertySuper = null;
|
|
if (property.Outer != null && property.Outer != classObj && property.Outer is UhtClass outerClass && base.CanExportClass(outerClass))
|
|
{
|
|
propertySuper = outerClass;
|
|
}
|
|
|
|
// Getter
|
|
builder.AppendLuaWrapperFunctionDeclaration(classObj, $"Get_{property.SourceName}").Append("\r\n");
|
|
builder.Append("{\r\n");
|
|
if (propertySuper == null)
|
|
{
|
|
builder.Append('\t').AppendLuaObjectDeclarationFromContext(classObj).Append("\r\n");
|
|
builder.Append("\tstatic FProperty* Property = FindScriptPropertyHelper(").Append(classObj.SourceName).Append("::StaticClass(), TEXT(\"").Append(property.SourceName).Append("\"));\r\n");
|
|
builder.Append('\t').AppendPropertyText(property, UhtPropertyTextType.GenericFunctionArgOrRetVal).Append(" PropertyValue;\r\n");
|
|
builder.Append("\tProperty->CopyCompleteValue(&PropertyValue, Property->ContainerPtrToValuePtr<void>(Obj));\r\n");
|
|
builder.Append('\t').AppendLuaReturnValueHandler(classObj, property, "PropertyValue").Append("\r\n");
|
|
}
|
|
else
|
|
{
|
|
builder.Append("\treturn ").Append(propertySuper.EngineName).Append('_').Append("Get_").Append(property.SourceName).Append("(InScriptContext);\r\n");
|
|
}
|
|
builder.Append("}\r\n\r\n");
|
|
|
|
// Setter
|
|
builder.AppendLuaWrapperFunctionDeclaration(classObj, $"Set_{property.SourceName}").Append("\r\n");
|
|
builder.Append("{\r\n");
|
|
if (propertySuper == null)
|
|
{
|
|
builder.Append('\t').AppendLuaObjectDeclarationFromContext(classObj).Append("\r\n");
|
|
builder.Append("\tstatic FProperty* Property = FindScriptPropertyHelper(").Append(classObj.SourceName).Append("::StaticClass(), TEXT(\"").Append(property.SourceName).Append("\"));\r\n");
|
|
builder.Append('\t').AppendPropertyText(property, UhtPropertyTextType.GenericFunctionArgOrRetVal).Append(" PropertyValue = ");
|
|
AppendInitializeFunctionDispatchParam(builder, classObj, null, property, 0).Append(";\r\n");
|
|
builder.Append("\tProperty->CopyCompleteValue(Property->ContainerPtrToValuePtr<void>(Obj), &PropertyValue);\r\n");
|
|
builder.Append("\treturn 0;\r\n");
|
|
}
|
|
else
|
|
{
|
|
builder.Append("\treturn ").Append(propertySuper.EngineName).Append('_').Append("Set_").Append(property.SourceName).Append("(InScriptContext);\r\n");
|
|
}
|
|
builder.Append("}\r\n\r\n");
|
|
}
|
|
|
|
protected override void Finish(StringBuilder builder, List<UhtClass> classes)
|
|
{
|
|
HashSet<UhtHeaderFile> uniqueHeaders = new();
|
|
foreach (UhtClass classObj in classes)
|
|
{
|
|
uniqueHeaders.Add(classObj.HeaderFile);
|
|
}
|
|
List<string> sortedHeaders = new();
|
|
foreach (UhtHeaderFile headerFile in uniqueHeaders)
|
|
{
|
|
sortedHeaders.Add(headerFile.FilePath);
|
|
}
|
|
sortedHeaders.Sort(StringComparerUE.OrdinalIgnoreCase);
|
|
foreach (string filePath in sortedHeaders)
|
|
{
|
|
string relativePath = Factory.GetPluginShortestIncludePath(filePath);
|
|
builder.Append("#include \"").Append(relativePath).Append("\"\r\n");
|
|
}
|
|
|
|
classes.Sort((x, y) => StringComparerUE.OrdinalIgnoreCase.Compare(x.EngineName, y.EngineName));
|
|
sortedHeaders.Clear();
|
|
foreach (UhtClass classObj in classes)
|
|
{
|
|
builder.Append("#include \"").Append(classObj.EngineName).Append(".script.h\"\r\n");
|
|
}
|
|
builder.Append("\r\n");
|
|
builder.Append("void LuaRegisterExportedClasses(lua_State* InScriptContext)\r\n");
|
|
builder.Append("{\r\n");
|
|
foreach(UhtClass classObj in classes)
|
|
{
|
|
builder.Append("\tFLuaUtils::RegisterLibrary(InScriptContext, ").Append(classObj.EngineName).Append("_Lib, \"").Append(classObj.EngineName).Append("\");\r\n");
|
|
}
|
|
builder.Append("}\r\n\r\n");
|
|
}
|
|
|
|
protected override StringBuilder AppendInitializeFunctionDispatchParam(StringBuilder builder, UhtClass classObj, UhtFunction? function, UhtProperty property, int propertyIndex)
|
|
{
|
|
if (!property.PropertyFlags.HasAnyFlags(EPropertyFlags.ReturnParm))
|
|
{
|
|
int paramIndex = propertyIndex + 2;
|
|
if (property is UhtIntProperty)
|
|
{
|
|
builder.Append("(luaL_checkint");
|
|
}
|
|
else if (property is UhtFloatProperty)
|
|
{
|
|
builder.Append("(float)(luaL_checknumber");
|
|
}
|
|
else if (property is UhtStrProperty)
|
|
{
|
|
builder.Append("ANSI_TO_TCHAR(luaL_checkstring");
|
|
}
|
|
else if (property is UhtNameProperty)
|
|
{
|
|
builder.Append("FName(luaL_checkstring");
|
|
}
|
|
else if (property is UhtBoolProperty)
|
|
{
|
|
builder.Append("!!(lua_toboolean");
|
|
}
|
|
else if (property is UhtStructProperty structProperty)
|
|
{
|
|
if (structProperty.ScriptStruct.EngineName == "Vector2D")
|
|
{
|
|
builder.Append("(FLuaVector2D::Get");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Vector")
|
|
{
|
|
builder.Append("(FLuaVector::Get");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Vector4")
|
|
{
|
|
builder.Append("(FLuaVector4::Get");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Quat")
|
|
{
|
|
builder.Append("(FLuaQuat::Get");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "LinearColor")
|
|
{
|
|
builder.Append("(FLuaLinearColor::Get");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Color")
|
|
{
|
|
builder.Append("FColor(FLuaLinearColor::Get");
|
|
}
|
|
else if (structProperty.ScriptStruct.EngineName == "Transform")
|
|
{
|
|
builder.Append("(FLuaTransform::Get");
|
|
}
|
|
else
|
|
{
|
|
throw new UhtIceException($"Unsupported function param struct type: {structProperty.ScriptStruct.EngineName}");
|
|
}
|
|
}
|
|
else if (property is UhtClassProperty)
|
|
{
|
|
builder.Append("(UClass*)(lua_touserdata");
|
|
}
|
|
else if (property is UhtObjectPropertyBase)
|
|
{
|
|
builder.Append('(').AppendPropertyText(property, UhtPropertyTextType.GenericFunctionArgOrRetVal).Append(")(lua_touserdata");
|
|
}
|
|
else
|
|
{
|
|
throw new UhtIceException($"Unsupported function param type: {property.GetType().Name}");
|
|
}
|
|
builder.Append("(InScriptContext, ").Append(paramIndex).Append("))");
|
|
}
|
|
else
|
|
{
|
|
base.AppendInitializeFunctionDispatchParam(builder, classObj, function, property, propertyIndex);
|
|
}
|
|
return builder;
|
|
}
|
|
}
|
|
}
|