#include "LuaCall.h" #include "IntegrationGameModeBase.h" static void FatalBlueprintError(const TCHAR *message) { FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::FatalError, FText::FromString(FString(message))); FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); } static void CheckNotEmpty(const FlxStreamBuffer &sb) { if (sb.empty()) { FatalBlueprintError(TEXT("Must use LuaCallBegin before other LuaCall steps")); } } static constexpr uint64_t ParseNameAsToken(std::string_view str) { uint64_t result = 0; uint64_t maxint = uint64_t(-1); // Leading zeros are not allowed. if ((!str.empty()) && (str[0]=='0')) return 0; for (int i = 0; i < int(str.size()); i++) { char c = str[i]; uint64_t digit = 0; if ((c >= '0') && (c <= '9')) { digit = uint64_t(c - '0'); } else if ((c >= 'a') && (c <= 'z')) { digit = uint64_t(c - 'a' + 10); } else if ((c >= 'A') && (c <= 'Z')) { digit = uint64_t(c - 'A' + 10); } else { return maxint; } // Multiply existing number by 36, then add the digit. // We have two checks to prevent integer overflow. if (result > (maxint / 36)) return 0; result *= 36; if (digit > (maxint - result)) return 0; result += digit; } return result; } void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); mode->LuaCallBegin(); sb.write_string(cname); sb.write_string(fname); } void UlxLuaCallLibrary::LuaCallAddStringParameter(UObject *context, const FString &pstring) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); sb.write_string(pstring); } void UlxLuaCallLibrary::LuaCallAddNameParameter(UObject *context, const FName &pname) { FTCHARToUTF8 utf8str(pname.ToString()); std::string_view namestr(utf8str.Get(), utf8str.Length()); uint64_t tokvalue = ParseNameAsToken(namestr); if ((tokvalue == 0) && !namestr.empty()) { FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); } AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); sb.write_string(namestr); } void UlxLuaCallLibrary::LuaCallAddFloatParameter(UObject *context, double pfloat) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_double(pfloat); } void UlxLuaCallLibrary::LuaCallAddIntParameter(UObject *context, int value) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_double(value); } void UlxLuaCallLibrary::LuaCallAddVectorParameter(UObject *context, const FVector &pvector) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_fvector(pvector); } void UlxLuaCallLibrary::LuaCallAddVector2DParameter(UObject *context, const FVector2D &pvector) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_double(pvector.X); sb.write_double(pvector.Y); sb.write_double(0.0); } void UlxLuaCallLibrary::LuaCallAddBooleanParameter(UObject *context, bool pbool) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); sb.write_bool(pbool); } void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); mode->LuaCallEnd(InvocationKind::LUA_INVOKE, place); } void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); } void UlxLuaCallLibrary::InvokeEngioMove(UObject *context, const FString &action, const FVector &xyz, double facing) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallBegin(); sb.write_string("engio"); sb.write_string("move"); sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); sb.write_string(action); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_fvector(xyz); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_double(facing); mode->LuaCallEnd(InvocationKind::LUA_INVOKE); } ELpxSimpleDynamicTag UlxLuaCallLibrary::LuaCallNextResultType(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); if (sb.empty()) return ELpxSimpleDynamicTag::None; int64_t total_reads = sb.total_reads(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); sb.unread_to(total_reads); switch (tag) { case SimpleDynamicTag::STRING: return ELpxSimpleDynamicTag::String; case SimpleDynamicTag::TOKEN: return ELpxSimpleDynamicTag::Name; case SimpleDynamicTag::NUMBER: return ELpxSimpleDynamicTag::Float; case SimpleDynamicTag::VECTOR: return ELpxSimpleDynamicTag::Vector; case SimpleDynamicTag::BOOLEAN: return ELpxSimpleDynamicTag::Boolean; default: return ELpxSimpleDynamicTag::None; } } FString UlxLuaCallLibrary::LuaCallGetStringResult(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::STRING) FatalBlueprintError(TEXT("expected lua to return a string")); return sb.read_fstring(); } FName UlxLuaCallLibrary::LuaCallGetNameResult(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::TOKEN) FatalBlueprintError(TEXT("expected lua to return a name")); return sb.read_fname(); } double UlxLuaCallLibrary::LuaCallGetFloatResult(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::NUMBER) FatalBlueprintError(TEXT("expected lua to return a float")); return sb.read_double(); } FVector UlxLuaCallLibrary::LuaCallGetVectorResult(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::VECTOR) FatalBlueprintError(TEXT("expected lua to return a vector")); return sb.read_fvector(); } bool UlxLuaCallLibrary::LuaCallGetBooleanResult(UObject *context) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetResult(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); if (tag != SimpleDynamicTag::BOOLEAN) FatalBlueprintError(TEXT("expected lua to return a boolean")); return sb.read_bool(); }