From 2bfa3024f1502f9f2d358fbbb5fa75617da2bf7b Mon Sep 17 00:00:00 2001 From: jyelon Date: Thu, 21 May 2026 18:41:09 -0400 Subject: [PATCH] Lots of work on the lua read-eval-print loop --- Content/Luprex/lxGameMode.uasset | 4 +- Content/Luprex/lxPlayerController.uasset | 4 +- Content/Testing/WB_Test.uasset | 4 +- Content/Widgets/WB_Console.uasset | 4 +- Content/Widgets/WB_Hotkeys.uasset | 4 +- Content/Widgets/WB_Menu.uasset | 4 +- Docs/TODO-List.md | 10 ---- Source/Integration/UtilityLibrary.cpp | 73 ++++++------------------ Source/Integration/UtilityLibrary.h | 49 +++------------- luprex/cpp/core/luastack.cpp | 2 +- luprex/cpp/core/printbuffer.cpp | 1 - luprex/cpp/core/util.cpp | 18 ++++++ luprex/cpp/core/util.hpp | 8 +++ luprex/cpp/core/world-core.cpp | 4 ++ luprex/lua/login.lua | 1 + 15 files changed, 70 insertions(+), 120 deletions(-) diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index f9c02b26..36a6caf5 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:e424d0d5fc045dd01ed31c3d6cc498550060e2221b47aae2febceac5fd1df784 -size 52149 +oid sha256:e53e2476e31369dd91a2066a214facf7635113d12df35a89716bb70d19f75ad1 +size 51898 diff --git a/Content/Luprex/lxPlayerController.uasset b/Content/Luprex/lxPlayerController.uasset index 97b9b8b8..f1b27f66 100644 --- a/Content/Luprex/lxPlayerController.uasset +++ b/Content/Luprex/lxPlayerController.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd5a35ff0e198aab059399dcd30e5690121e6015e698e766973241aec81ec209 -size 165165 +oid sha256:f90f81a656706ba63290f945f2fed5e2f17427c14a2ad2bed0073b2479170ead +size 165578 diff --git a/Content/Testing/WB_Test.uasset b/Content/Testing/WB_Test.uasset index 5728eaca..33a57d09 100644 --- a/Content/Testing/WB_Test.uasset +++ b/Content/Testing/WB_Test.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:576b1ac0e9360ece80e99a0d2cd9abf7356e4a62a0b42ab95b47ac6050b30e2b -size 44257 +oid sha256:075eb5048bce8c33b4687981db24d4bd42219baf66b67c81325b4d44b9c67ec6 +size 48621 diff --git a/Content/Widgets/WB_Console.uasset b/Content/Widgets/WB_Console.uasset index d288832f..d6921d85 100644 --- a/Content/Widgets/WB_Console.uasset +++ b/Content/Widgets/WB_Console.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35420a97cde729b12666a30362319b860bddd7fa292fbc0792956b3d3085368a -size 215860 +oid sha256:61efa6e7d2b14e2e7f44dfcb1e50d57030f097e374bf5aed00848724b1fe9a63 +size 232134 diff --git a/Content/Widgets/WB_Hotkeys.uasset b/Content/Widgets/WB_Hotkeys.uasset index daf5bce3..6bbde278 100644 --- a/Content/Widgets/WB_Hotkeys.uasset +++ b/Content/Widgets/WB_Hotkeys.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:129db4b554d1effb902d7c3887d4be922847ca350258b39e7d3444aa47000bdc -size 292218 +oid sha256:fee03f33a14cc6a311c82aa6309325a7d220c1964c595153a00849ec9f8e55ba +size 295424 diff --git a/Content/Widgets/WB_Menu.uasset b/Content/Widgets/WB_Menu.uasset index 84bcb540..69899a2d 100644 --- a/Content/Widgets/WB_Menu.uasset +++ b/Content/Widgets/WB_Menu.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5997c50fd19af7bb4a9c916de2e339e82b71ec99a28043a1a539b8effc488a5c -size 239534 +oid sha256:d683d740882033e2ad8ea56354a7283b97810586ae199270205ed6a4886fb421 +size 213214 diff --git a/Docs/TODO-List.md b/Docs/TODO-List.md index 590c2e94..9f01ff1f 100644 --- a/Docs/TODO-List.md +++ b/Docs/TODO-List.md @@ -1,20 +1,10 @@ -* UE Wingman rename functions. -* ue Wingman 'structprop' doesn't work for UWingXXXRef types, or for Widget slots. It needs to be implemented on top of getdetails. -* In the console, do not allow multi-line lua expressions unless it's something that reasonably should be multi-line, like a function definition or an if-statement. -* Keyboard Event Handling - -* Menus * Skeletal Mesh Tangible * Implement Interactive Temporary Variables -* A better text console - -* Get rid of 3x3 Gridpanel stuff - * Object-Oriented Lua Support diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index 44ee69f8..52e8c2f8 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -10,6 +10,8 @@ #include "Kismet/GameplayStatics.h" #include "Blueprint/UserWidget.h" #include "Components/GridPanel.h" +#include "Components/CanvasPanelSlot.h" +#include "Components/Widget.h" #include "InputMappingContext.h" #include "EnhancedInputComponent.h" #include "Animation/AnimSequenceBase.h" @@ -156,65 +158,28 @@ bool UlxUtilityLibrary::LineTraceThroughPixel(const APlayerController* PlayerCon return false; } -void UlxUtilityLibrary::SetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D UpperLeftXY, FVector2D LowerRightXY) +void UlxUtilityLibrary::ConfigureCanvasPanelSlot(UObject *Target, FAnchors Anchors, FVector2D Position, FVector2D Size, FVector2D Alignment, bool SizeToContent) { - if ((GridPanel == nullptr) || (GridPanel->ColumnFill.Num() != 3) || (GridPanel->RowFill.Num() != 3)) + UCanvasPanelSlot *CanvasSlot = Cast(Target); + if (CanvasSlot == nullptr) { - UE_LOG(LogBlueprint, Error, TEXT("SetPositionOfGridPanelMiddleCell only works on 3x3 GridPanels.")); + UWidget *Widget = Cast(Target); + if (Widget != nullptr) + { + CanvasSlot = Cast(Widget->Slot); + } + } + if (CanvasSlot == nullptr) + { + UE_LOG(LogBlueprint, Error, TEXT("ConfigureCanvasPanelSlot: object is not a CanvasPanelSlot, and is not a Widget in a CanvasPanel.")); return; } - if ((LowerRightXY.X < UpperLeftXY.X) || (LowerRightXY.Y < UpperLeftXY.Y)) - { - UE_LOG(LogBlueprint, Error, TEXT("LowerRightXY must be greater than or equal to UpperLeftXY")); - return; - } - - UpperLeftXY.X = FMath::Clamp(UpperLeftXY.X, 0.0f, 1.0f); - UpperLeftXY.Y = FMath::Clamp(UpperLeftXY.Y, 0.0f, 1.0f); - LowerRightXY.X = FMath::Clamp(LowerRightXY.X, 0.0f, 1.0f); - LowerRightXY.Y = FMath::Clamp(LowerRightXY.Y, 0.0f, 1.0f); - - GridPanel->SetRowFill(0, UpperLeftXY.Y); - GridPanel->SetRowFill(1, LowerRightXY.Y - UpperLeftXY.Y); - GridPanel->SetRowFill(2, 1.0 - LowerRightXY.Y); - - GridPanel->SetColumnFill(0, UpperLeftXY.X); - GridPanel->SetColumnFill(1, LowerRightXY.X - UpperLeftXY.X); - GridPanel->SetColumnFill(2, 1.0 - LowerRightXY.X); -} - -void UlxUtilityLibrary::GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY) -{ - TArray &Col = GridPanel->ColumnFill; - TArray &Row = GridPanel->RowFill; - - // Set default return value for error situations. - UpperLeftXY.X = 0.0; - LowerRightXY.X = 1.0; - UpperLeftXY.Y = 0.0; - LowerRightXY.Y = 1.0; - - if ((GridPanel == nullptr) || (Row.Num() != 3) || (Col.Num() != 3)) - { - UE_LOG(LogBlueprint, Error, TEXT("SetPositionOfGridPanelMiddleCell only works on 3x3 GridPanels.")); - return; - } - - double TotalX = Col[0] + Col[1] + Col[2]; - double TotalY = Row[0] + Row[1] + Row[2]; - - if (TotalX > 0) - { - UpperLeftXY.X = Col[0] / TotalX; - LowerRightXY.X = (Col[0] + Col[1]) / TotalX; - } - - if (TotalY > 0) - { - UpperLeftXY.Y = Row[0] / TotalY; - LowerRightXY.Y = (Row[0] + Row[1]) / TotalY; - } + CanvasSlot->SetAnchors(Anchors); + CanvasSlot->SetAlignment(Alignment); + CanvasSlot->SetPosition(Position); + CanvasSlot->SetSize(Size); + CanvasSlot->SetAutoSize(SizeToContent); } ElxUsedOrNotUsed UlxUtilityLibrary::IsKeyUsedByMappingContext(const FKey &Key, const UInputMappingContext *MappingContext) diff --git a/Source/Integration/UtilityLibrary.h b/Source/Integration/UtilityLibrary.h index dfa0eb93..57130668 100644 --- a/Source/Integration/UtilityLibrary.h +++ b/Source/Integration/UtilityLibrary.h @@ -7,6 +7,7 @@ #include "Input/Events.h" #include "Common.h" #include "Kismet/BlueprintFunctionLibrary.h" +#include "Components/CanvasPanelSlot.h" #include "UtilityLibrary.generated.h" @@ -91,51 +92,15 @@ public: ETraceTypeQuery TraceChannel, bool bTraceComplex, EDrawDebugTrace::Type DrawDebugType, bool bIgnorePlayerPawn, const TArray& ActorsToIgnore, FHitResult& HitResult); - // Set Position of GridPanel Middle Cell - // - // Sometimes, you want to specify the position of a widget, and you - // don't want to specify the position in slate units, instead, you - // want to specify the position using fractions: ie, (0,0) is the - // upper left corner of the screen, and (1,1) is the lower-right corner. - // - // One way to accomplish this is to put your widget in the middle cell - // of a 3x3 GridPanel. Then, you can position it by adjusting the grid - // fill rules. This utility routine can do the math necessary to - // correctly populate those fill rules. - // - // This routine must be passed a 3x3 GridPanel. This will reposition - // the middle cell. You must specify the upper-left and lower-right - // corners of the middle cell as fractions between (0,0) and (1,1). - // - // Be aware that if the content of a grid cell overflows the amount of - // space allocated for it, then the grid will adjust to make room. - // But that will mean that the grid is no longer faithful to the - // positions specified in its fill rules. One way to ensure that the - // grid remains faithful to its fill rules is to put an Overlay - // into the GridPanel cell, then put -1000 padding into the - // Overlay's GridPanel slot, then put +1000 padding into the Overlay - // slot. The two paddings cancel each other out, leaving the item in - // the Overlay at the originally-intended position. But if the item - // in the overlay overflows, it doesn't cause the Grid to deform. - // Instead, the item exceeds the bounds of the grid cell, but it leaves - // the grid cell where it belongs. - // - UFUNCTION(BlueprintCallable, Category="Widget") - static void SetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D UpperLeftXY, FVector2D LowerRightXY); - // Get Position of GridPanel Middle Cell + // Configure a CanvasPanelSlot's parameters in a single call. // - // The routine must be passed a 3x3 GridPanel. This will return the - // position of the middle cell of the gridpanel, expressed on a scale - // from (0,0) to (1,1). - // - // The numbers returned by this routine are based entirely on the - // GridPanel fill rules. If an item in the grid is overflowing its - // allocated space, causing the grid to deform, then that won't be - // reflected in the output of this routine. + // Target must be either a UCanvasPanelSlot directly, or a UWidget whose + // Slot is a UCanvasPanelSlot. If it is neither, logs an error and + // does nothing. // - UFUNCTION(BlueprintPure, Category="Widget") - static void GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY); + UFUNCTION(BlueprintCallable, Category = "Widget", meta = (SizeToContent = "true")) + static void ConfigureCanvasPanelSlot(UObject *Target, FAnchors Anchors, FVector2D Position, FVector2D Size, FVector2D Alignment, bool SizeToContent); // Check if a given key is used by the specified mapping context. // diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index 1a41d496..4b9eaf3b 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -336,7 +336,7 @@ eng::string LuaCoreStack::load(LuaSlot result, std::string_view code, std::strin const char *str = lua_tolstring(L_, -1, &len); eng::string message(str, len); lua_pop(L_, 1); - if (sv::has_suffix(message, "near ")) + if (sv::has_suffix(message, "near ") && sv::is_possible_long_lua_expression(code)) { message = "truncated lua"; } diff --git a/luprex/cpp/core/printbuffer.cpp b/luprex/cpp/core/printbuffer.cpp index c6532b6d..6db0d6e5 100644 --- a/luprex/cpp/core/printbuffer.cpp +++ b/luprex/cpp/core/printbuffer.cpp @@ -192,7 +192,6 @@ bool PrintChanneler::channel(const PrintBuffer *printbuffer, StreamBuffer *sb) { line_ = printbuffer->first_line(); } while (line_ < printbuffer->first_unchecked()) { - sb->write_bytes("|"); sb->write_bytes(printbuffer->nth(line_)); sb->write_bytes("\n"); line_ += 1; diff --git a/luprex/cpp/core/util.cpp b/luprex/cpp/core/util.cpp index ba1231b9..efa5213b 100644 --- a/luprex/cpp/core/util.cpp +++ b/luprex/cpp/core/util.cpp @@ -200,6 +200,24 @@ bool is_lua_comment(string_view s) { return s.substr(start, 2) == "--"; } +bool is_possible_long_lua_expression(string_view s) { + read_space(s); + string_view id = read_lua_identifier(s); + if (id.empty()) return false; + if ((id == "function") || (id == "if") || (id == "while") || (id == "for") || (id == "repeat") || (id == "do")) return true; + if (id == "local") + { + read_space(s); + id = read_lua_identifier(s); + } + read_space(s); + read_prefix(s, "="); // If not present, returns false but we continue anyway. + read_space(s); + if (has_prefix(s, "[")) return true; + if (has_prefix(s, "(")) return true; + return false; +} + bool is_whitespace(string_view s) { for (int i = 0; i < int(s.size()); i++) { if (!ascii_isspace(s[i])) { diff --git a/luprex/cpp/core/util.hpp b/luprex/cpp/core/util.hpp index bc037c62..7e180f1e 100644 --- a/luprex/cpp/core/util.hpp +++ b/luprex/cpp/core/util.hpp @@ -106,6 +106,14 @@ bool is_lua_classname(string_view s); // Return true if the line of code is a lua comment. bool is_lua_comment(string_view s); +// Return true if the line of code could be the beginning of a long expression. +// In a read-eval-print loop, if the user types something like "function foo", +// that's not a complete lua expression. But we don't want to just print an error, +// we want to give the user a chance to continue typing so that he can turn it +// into a complete lua expression. This function returns true if the string looks +// like the beginning of a long lua expression. This is only a heuristic. +bool is_possible_long_lua_expression(string_view s); + // Return true if the line is entirely whitespace. bool is_whitespace(string_view s); diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 3372078f..9244e441 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -1071,6 +1071,10 @@ void World::run_scheduled_threads() { PrettyPrint::Indented().print(LSCO, LuaSpecial(i), <hread_prints_); lthread_prints_ << std::endl; } + if (lthread_prints_.view().empty()) + { + lthread_prints_ << "ok\n"; + } } } else if (status == LUA_YIELD) { if (is_authoritative()) { diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index 43fbb326..379a1b8d 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -40,6 +40,7 @@ function cube.lookmenu(add) add("Cube Hi", function () dprint("Doing Cube Hi") end) add("Cube Bye", function () dprint("Doing Cube Bye") end) add("Cube Yo", function () dprint("Doing Cube Yo") end) + add("Cube Z", function () dprint("Doing Cube Z") end) end function sphere.lookhotkeys(add)