From c060b875566ca9f280153364137b5f7a784b15f8 Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 7 Apr 2025 19:29:47 -0400 Subject: [PATCH] Got the whole look-at demo up and running. --- Content/Luprex/lxGameMode.uasset | 4 +- Source/Integration/AssetLookup.cpp | 38 +++++---- Source/Integration/AssetLookup.h | 8 +- Source/Integration/LuaCall.cpp | 104 +++++++++++++--------- Source/Integration/LuaCall.h | 123 +++++++++++---------------- Source/Integration/LuprexWidgets.cpp | 0 luprex/cpp/core/world-accessor.cpp | 27 ++++-- luprex/lua/login.lua | 11 ++- 8 files changed, 166 insertions(+), 149 deletions(-) create mode 100644 Source/Integration/LuprexWidgets.cpp diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index 7c9182bf..80e7f05d 100644 --- a/Content/Luprex/lxGameMode.uasset +++ b/Content/Luprex/lxGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:998665157f72c0b5d2cbaf2b68e99c23e082af4a7ae15de58ad447a7c8aaf001 -size 160478 +oid sha256:c55be73df0293d6010f31b4c632d895ceeb2cf51974eac9620947b18ba538587 +size 163624 diff --git a/Source/Integration/AssetLookup.cpp b/Source/Integration/AssetLookup.cpp index 60ad8314..7941e880 100644 --- a/Source/Integration/AssetLookup.cpp +++ b/Source/Integration/AssetLookup.cpp @@ -93,85 +93,87 @@ FString UlxAssetLookup::WidgetLoadPath(const FName &AssetName) const return *Result; } -UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const FString &Name) +UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound, bool ErrorIfInvalid) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->StaticMeshLoadPath(FName(FString("SM_") + Name)); if (Path.IsEmpty()) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Static mesh not on search path: %s"), *Name); + if (ErrorIfNotFound) UE_LOG(LogLuprexIntegration, Error, TEXT("Static mesh not on search path: %s"), *Name); return nullptr; } UStaticMesh *Result = LoadObject(nullptr, *Path); if (Result == nullptr) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load static mesh: %s"), *Path); + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load static mesh: %s"), *Path); return nullptr; } return Result; } -TSubclassOf UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name) { +TSubclassOf UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound, bool ErrorIfInvalid) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name)); if (Path.IsEmpty()) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible not on search path: %s"), *Name); + if (ErrorIfNotFound) UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible not on search path: %s"), *Name); return nullptr; } UClass *Result = LoadObject(nullptr, *Path); if (Result == nullptr) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load tangible class: %s"), *Path); + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load tangible class: %s"), *Path); return nullptr; } if (!Result->IsChildOf(AActor::StaticClass())) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible class is not an actor: %s"), *Path); + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible class is not an actor: %s"), *Path); return nullptr; } UFunction *aqchanged = Result->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); if ((aqchanged == nullptr)||(aqchanged->ParmsSize != 0)) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible does not have 'Animation Queue Changed' function: %s"), *Path); + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible does not have 'Animation Queue Changed' function: %s"), *Path); return nullptr; } return Result; } -TSubclassOf UlxAssetLookup::GetWidgetByName(const UObject *Context, const FString &Name) { +TSubclassOf UlxAssetLookup::GetWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound, bool ErrorIfInvalid) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); if (Path.IsEmpty()) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Widget not on search path: %s"), *Name); + if (ErrorIfNotFound) UE_LOG(LogLuprexIntegration, Error, TEXT("Widget not on search path: %s"), *Name); return nullptr; } UClass *Result = LoadObject(nullptr, *Path); if (Result == nullptr) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load widget blueprint: %s"), *Path); + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load widget blueprint: %s"), *Path); return nullptr; } - if (!Result->IsChildOf(UUserWidget::StaticClass())) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Blueprint is not a Widget Blueprint: %s"), *Path); + if (!Result->IsChildOf(UUserWidget::StaticClass())) + { + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Blueprint is not a Widget Blueprint: %s"), *Path); return nullptr; } return Result; } -TSubclassOf UlxAssetLookup::GetLookAtWidgetByName(const UObject *Context, const FString &Name) { +TSubclassOf UlxAssetLookup::GetLookAtWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound, bool ErrorIfInvalid) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); if (Path.IsEmpty()) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Widget not on search path: %s"), *Name); + if (ErrorIfNotFound) UE_LOG(LogLuprexIntegration, Error, TEXT("Widget not on search path: %s"), *Name); return nullptr; } UClass *Result = LoadObject(nullptr, *Path); if (Result == nullptr) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load widget blueprint: %s"), *Path); + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load widget blueprint: %s"), *Path); return nullptr; } - if (!Result->IsChildOf(UlxLookAtWidget::StaticClass())) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Blueprint is not a Luprex Look-At Widget: %s"), *Path); + if (!Result->IsChildOf(UlxLookAtWidget::StaticClass())) + { + if (ErrorIfInvalid) UE_LOG(LogLuprexIntegration, Error, TEXT("Blueprint is not a Luprex Look-At Widget: %s"), *Path); return nullptr; } return Result; diff --git a/Source/Integration/AssetLookup.h b/Source/Integration/AssetLookup.h index ce9d4aaa..509e9da5 100644 --- a/Source/Integration/AssetLookup.h +++ b/Source/Integration/AssetLookup.h @@ -48,17 +48,17 @@ public: // Get a static mesh by name UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static UStaticMesh *GetStaticMeshByName(const UObject *Context, const FString &Name); + static UStaticMesh *GetStaticMeshByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false, bool ErrorIfInvalid = true); // Get a tangible class by name UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static TSubclassOf GetTangibleClassByName(const UObject *Context, const FString &Name); + static TSubclassOf GetTangibleClassByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false, bool ErrorIfInvalid = true); // Get a widget blueprint by name UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static TSubclassOf GetWidgetByName(const UObject *Context, const FString &Name); + static TSubclassOf GetWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false, bool ErrorIfInvalid = true); // Get a look-at widget blueprint by name UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static TSubclassOf GetLookAtWidgetByName(const UObject *Context, const FString &Name); + static TSubclassOf GetLookAtWidgetByName(const UObject *Context, const FString &Name, bool ErrorIfNotFound = false, bool ErrorIfInvalid = true); }; diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 83e42fd9..10dbf9c4 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -505,78 +505,100 @@ FString UlxLuaValues::DebugString() const return Output.ToString(); } -bool UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) +ElxSuccessOrError UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) { if (Type != Desired) { FString TypeName = StaticEnum()->GetDisplayNameTextByValue(int64(Type)).ToString(); FString DesiredName = StaticEnum()->GetDisplayNameTextByValue(int64(Desired)).ToString(); UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName); - return false; + return ElxSuccessOrError::Error; } - return true; + return ElxSuccessOrError::Success; } -ElxLuaValueType UlxLuaValues::GetType(int n) const +ElxLuaValueType UlxLuaValues::NextType() const { - if (n < 0) return ElxLuaValueType::None; - if (n >= Types.Num()) return ElxLuaValueType::None; + if (Cursor < 0) return ElxLuaValueType::None; + if (Cursor >= Types.Num()) return ElxLuaValueType::None; return Types[Cursor]; } -// -// Get the Nth value in the array. Array bounds-checking -// is handled by 'GetType', which returns 'None' for out-of-bounds -// accesses. -// - -FString UlxLuaValues::GetString(int n) const +void UlxLuaValues::ReadString(ElxSuccessOrError &Status, FString &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::String)) return FString(); - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_fstring(); + Status = CheckType(NextType(), ElxLuaValueType::String); + if (Status == ElxSuccessOrError::Error) + { + Result.Empty(); return; + } + Result = FlxStreamBuffer(Data[Cursor++]).read_fstring(); } -FName UlxLuaValues::GetName(int n) const +void UlxLuaValues::ReadName(ElxSuccessOrError &Status, FName &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::Name)) return FName(); - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_fname(); + Status = CheckType(NextType(), ElxLuaValueType::Name); + if (Status == ElxSuccessOrError::Error) + { + Result = FName(); return; + } + Result = FlxStreamBuffer(Data[Cursor++]).read_fname(); } -double UlxLuaValues::GetFloat(int n) const +void UlxLuaValues::ReadFloat(ElxSuccessOrError &Status, double &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0.0; - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_double(); + Status = CheckType(NextType(), ElxLuaValueType::Float); + if (Status == ElxSuccessOrError::Error) + { + Result = 0.0; return; + } + Result = FlxStreamBuffer(Data[Cursor++]).read_double(); } -int UlxLuaValues::GetInt(int n) const +void UlxLuaValues::ReadInt(ElxSuccessOrError &Status, int &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0; - FlxStreamBuffer Decoder(Data[n]); - return int(Decoder.read_double()); + Status = CheckType(NextType(), ElxLuaValueType::Float); + if (Status == ElxSuccessOrError::Error) + { + Result = 0.0; return; + } + double dvalue = FlxStreamBuffer(Data[Cursor++]).read_double(); + Result = int(dvalue); + if (double(Result) != dvalue) + { + Result = 0; Status = ElxSuccessOrError::Error; return; + } } -FVector UlxLuaValues::GetVector(int n) const +void UlxLuaValues::ReadVector(ElxSuccessOrError &Status, FVector &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector(); - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_fvector(); + Status = CheckType(NextType(), ElxLuaValueType::Vector); + if (Status == ElxSuccessOrError::Error) + { + Result = FVector(); return; + } + Result = FlxStreamBuffer(Data[Cursor++]).read_fvector(); } -FVector2D UlxLuaValues::GetVector2D(int n) const +void UlxLuaValues::ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector2D(); - FlxStreamBuffer Decoder(Data[n]); - FVector v = Decoder.read_fvector(); - return FVector2D(v.X, v.Y); + Status = CheckType(NextType(), ElxLuaValueType::Vector); + if (Status == ElxSuccessOrError::Error) + { + Result = FVector2D(); return; + } + FVector VValue = FlxStreamBuffer(Data[Cursor++]).read_fvector(); + Result = FVector2D(VValue.X, VValue.Y); } -bool UlxLuaValues::GetBoolean(int n) const +void UlxLuaValues::ReadBoolean(ElxSuccessOrError &Status, bool &Result) { - if (!CheckType(GetType(n), ElxLuaValueType::Boolean)) return false; - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_bool(); + Status = CheckType(NextType(), ElxLuaValueType::Boolean); + if (Status == ElxSuccessOrError::Error) + { + Result = false; return; + } + Result = FlxStreamBuffer(Data[Cursor++]).read_bool(); } + + diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index db7541c5..e5feb7cf 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -7,6 +7,36 @@ class UlxLuaValues; +///////////////////////////////////////////////////////////////// +// +// These are the types that can actually be packed into +// a serialized buffer. +// +///////////////////////////////////////////////////////////////// + + +UENUM(BlueprintType) +enum class ElxLuaValueType : uint8 { + None, + String, + Name, + Float, + Boolean, + Vector +}; + +///////////////////////////////////////////////////////////////// +// +// A general-purpose 'success or error' type. +// +///////////////////////////////////////////////////////////////// + +UENUM(BlueprintType) +enum class ElxSuccessOrError : uint8 { + Success, + Error, +}; + ///////////////////////////////////////////////////////////////// // // This is a little parser that parses Lua function 'prototypes'. @@ -177,24 +207,6 @@ public: -///////////////////////////////////////////////////////////////// -// -// These are the types that can actually be packed into -// a serialized buffer. -// -///////////////////////////////////////////////////////////////// - - -UENUM(BlueprintType) -enum class ElxLuaValueType : uint8 { - None, - String, - Name, - Float, - Boolean, - Vector -}; - ///////////////////////////////////////////////////////////////// // @@ -231,7 +243,7 @@ private: // Compare two types. If they aren't equal, log an error and return false. // - static bool CheckType(ElxLuaValueType Type, ElxLuaValueType Desired); + static ElxSuccessOrError CheckType(ElxLuaValueType Type, ElxLuaValueType Desired); public: UlxLuaValues() { Cursor = 0; } @@ -253,68 +265,33 @@ public: UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") void SetCursor(int n) { Cursor = n; } - -private: - // - // Functions that get values from the array, random access. - // - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - ElxLuaValueType GetType(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool IsType(int n, ElxLuaValueType Type) const { return GetType(n) == Type; } - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FString GetString(int n) const; + ElxLuaValueType NextType() const; UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FName GetName(int n) const; + bool IsNextType(ElxLuaValueType Type) const { return NextType() == Type; } + + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "ReturnValue"), Category = "Luprex|Lua Value Array") + ElxLuaValueType SwitchNextType() const { return NextType(); } - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - double GetFloat(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - int GetInt(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FVector GetVector(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FVector2D GetVector2D(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool GetBoolean(int n) const; - - // - // Functions that get values from the array using the cursor. - // - -public: - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - ElxLuaValueType NextType() const { return GetType(Cursor); } - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool IsNextType(ElxLuaValueType Type) const { return IsType(Cursor, Type); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FString ReadString() { return GetString(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadString(ElxSuccessOrError &Status, FString &Result); - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FName ReadName() { return GetName(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadName(ElxSuccessOrError &Status, FName &Result); - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - double ReadFloat() { return GetFloat(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadFloat(ElxSuccessOrError &Status, double &Result); - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - int ReadInt() { return GetInt(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadInt(ElxSuccessOrError &Status, int &Result); - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FVector ReadVector() { return GetVector(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadVector(ElxSuccessOrError &Status, FVector &Result); - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FVector2D ReadVector2D() { return GetVector2D(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result); - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - bool ReadBoolean() { return GetBoolean(Cursor++); } + UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") + void ReadBoolean(ElxSuccessOrError &Status, bool &Result); }; \ No newline at end of file diff --git a/Source/Integration/LuprexWidgets.cpp b/Source/Integration/LuprexWidgets.cpp new file mode 100644 index 00000000..e69de29b diff --git a/luprex/cpp/core/world-accessor.cpp b/luprex/cpp/core/world-accessor.cpp index 16909130..d23c5996 100644 --- a/luprex/cpp/core/world-accessor.cpp +++ b/luprex/cpp/core/world-accessor.cpp @@ -234,20 +234,17 @@ LuaDefine(tangible_setclass, "tan,class", return LS.result(); } -LuaDefine(tangible_getclass, "tan", - "|Get the class of the tangible, if any." +LuaDefine(tangible_getclassname, "tan", + "|Get the classname of the tangible, if any." "|" - "|The return value is a string, the class name, not" - "|the class table." + "|The return value is a string (or nil)." "|") { LuaArg tanobj; - LuaVar classtab; LuaRet classname; - LuaDefStack LS(L, tanobj, classtab, classname); + LuaDefStack LS(L, tanobj, classname); World *w = World::fetch_global_pointer(L); w->tangible_get(LS, tanobj, false); - LS.tangetclass(classtab, tanobj); - eng::string name = LS.classname(classtab); + eng::string name = LS.classname(tanobj); if (name == "") { LS.set(classname, LuaNil); } else { @@ -256,6 +253,20 @@ LuaDefine(tangible_getclass, "tan", return LS.result(); } +LuaDefine(tangible_getclass, "tan", + "|Get the class of the tangible, if any." + "|" + "|The return value is a class table (or nil)." + "|") { + LuaArg tanobj; + LuaRet classtab; + LuaDefStack LS(L, tanobj, classtab); + World *w = World::fetch_global_pointer(L); + w->tangible_get(LS, tanobj, false); + LS.tangetclass(classtab, tanobj); + return LS.result(); +} + LuaDefine(tangible_delete, "tan", "|Delete the specified tangible." "|" diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index 17310b25..d706fd19 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -27,8 +27,13 @@ function sphere.getlookat() end function engio.getlookat() - local place = tangible.place() - local class = tangible.getclass(place) - return class.getlookat() + local class = tangible.getclass(tangible.place()) + if class ~= nil then + local getlookat = class.getlookat + if getlookat ~= nil then + return getlookat() + end + end + return "" end