diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index 62fc6889..a3121a89 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:fdc80c07df541ad2254d1ccb085d08ee5ab903204c067a775e4032b9f9e6ed39 -size 120192 +oid sha256:465af1a30451634dfd313f1a3484455553c99c35fcf5619d2809c5a41cbdd516 +size 136099 diff --git a/Content/TanActor.uasset b/Content/TanActor.uasset index 505568e1..49584960 100644 --- a/Content/TanActor.uasset +++ b/Content/TanActor.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:933564ce1f62b2a7a4d5720c07f70ae3f1074e48aaa00ed87ed395315842edb1 -size 2378 +oid sha256:1c03b99e03173e391b62b05d3ecc242c3b9c7d38a0f115ac9af6e8e5fcc24243 +size 2318 diff --git a/Content/Tangibles/TanCharacter.uasset b/Content/Tangibles/TanCharacter.uasset new file mode 100644 index 00000000..be72b8df --- /dev/null +++ b/Content/Tangibles/TanCharacter.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:288476de6bd7f908e4bff2ccb1988f7c9d4963718acd5ab1c9ac6d8e8a8c02f8 +size 317046 diff --git a/Content/Tangibles/TanStaticMesh.uasset b/Content/Tangibles/TanStaticMesh.uasset new file mode 100644 index 00000000..b9e68884 --- /dev/null +++ b/Content/Tangibles/TanStaticMesh.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76c26a80d6928a8d08b4501472364c9ad74b0d228def034f5e2da3d8c9ac1250 +size 207861 diff --git a/Content/Tangibles/TangibleStaticMesh.uasset b/Content/Tangibles/TangibleStaticMesh.uasset deleted file mode 100644 index 5ba9a5fc..00000000 --- a/Content/Tangibles/TangibleStaticMesh.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a5b8ed1fcef22cb88a3f4a6034d389ba88c71645cef20ba032e411da2a423f7f -size 219262 diff --git a/Content/Tangibles/tangiblecharacter.uasset b/Content/Tangibles/tangiblecharacter.uasset deleted file mode 100644 index 4d2e606c..00000000 --- a/Content/Tangibles/tangiblecharacter.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b2d181b23ea00140050105562efb9dea47c6bdc21bcf5479099138fc6889a7a4 -size 334681 diff --git a/Content/__ExternalActors__/LpxLevel/E/9Q/NGAUKYIQ0ILXBND0RRPBAT.uasset b/Content/__ExternalActors__/LpxLevel/E/9Q/NGAUKYIQ0ILXBND0RRPBAT.uasset deleted file mode 100644 index e46fc00b..00000000 --- a/Content/__ExternalActors__/LpxLevel/E/9Q/NGAUKYIQ0ILXBND0RRPBAT.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:283117b0c9c83b8a51f13c706998ba9cf668788aa4cb46737e2f36d78f938467 -size 3925 diff --git a/Source/Integration/BlueprintErrors.cpp b/Source/Integration/BlueprintErrors.cpp index 00dffd89..2d134900 100644 --- a/Source/Integration/BlueprintErrors.cpp +++ b/Source/Integration/BlueprintErrors.cpp @@ -1,5 +1,6 @@ #include "BlueprintErrors.h" +#include "StringDecoder.h" #include "Internationalization/TextFormatter.h" #include "Kismet/KismetSystemLibrary.h" #include "Kismet2/KismetDebugUtilities.h" @@ -209,6 +210,15 @@ FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTra return Result; } +FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name) +{ + FFormatArgumentData Result; + Result.ArgumentValueType = EFormatArgumentType::Text; + Result.ArgumentName = Name; + Result.ArgumentValue = FText::FromString(Value->DebugString()); + return Result; +} + FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataBlank(const FString &Name) { FFormatArgumentData Result; diff --git a/Source/Integration/BlueprintErrors.h b/Source/Integration/BlueprintErrors.h index aad068eb..9b3a9285 100644 --- a/Source/Integration/BlueprintErrors.h +++ b/Source/Integration/BlueprintErrors.h @@ -14,6 +14,8 @@ #include "BlueprintErrors.generated.h" +class UlxLuaValues; + /* * enum class ElxLogVerbosity, below, contains all the same error severity levels * as ELogVerbosity, but in a form that the blueprint editor can manipulate. @@ -213,6 +215,9 @@ public: UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name); + + UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") + static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name); }; /* Debug Blueprint Errors output device. diff --git a/Source/Integration/ConsoleOutput.cpp b/Source/Integration/ConsoleOutput.cpp new file mode 100644 index 00000000..c0b784e6 --- /dev/null +++ b/Source/Integration/ConsoleOutput.cpp @@ -0,0 +1,65 @@ +#include "ConsoleOutput.h" +#include "CoreMinimal.h" + +////////////////////////////////////////////////////////////// +// +// ConsoleOutput +// +// Storing the text that goes in the unreal console. +// +////////////////////////////////////////////////////////////// + + +void FlxConsoleOutput::Truncate() { + int lines = 50; + int csize = Content.Len(); + int total = 0; + for (int i = csize - 1; i >= 0; i--) { + if (Content[i] == '\n') { + total += 1; + if (total == lines) { + Content = Content.RightChop(i + 1); + return; + } + } + } +} + +bool FlxConsoleOutput::MaybeAppendNewline() { + int csize = Content.Len(); + if ((csize > 0) && (Content[csize - 1] != '\n')) { + Content += TEXT("\n"); + return true; + } + else { + return false; + } +} + +bool FlxConsoleOutput::MaybeAppendText(const FString& text) { + if (!text.IsEmpty()) { + Content += text; + return true; + } + else { + return false; + } +} + +void FlxConsoleOutput::Append(const FString& text) { + bool modified = MaybeAppendText(text); + if (modified) { + Dirty = true; + Truncate(); + } +} + +void FlxConsoleOutput::AppendLine(const FString& text) { + bool modified = MaybeAppendNewline(); + modified |= MaybeAppendText(text); + modified |= MaybeAppendNewline(); + if (modified) { + Dirty = true; + Truncate(); + } +} diff --git a/Source/Integration/ConsoleOutput.h b/Source/Integration/ConsoleOutput.h new file mode 100644 index 00000000..c65a2e19 --- /dev/null +++ b/Source/Integration/ConsoleOutput.h @@ -0,0 +1,52 @@ +#pragma once + +////////////////////////////////////////////////////////////// +// +// ConsoleOutput +// +// This class stores the text that's in the unreal console. +// It stores it as one great big string, which contains +// newlines to denote line breaks. +// +// This class also contains a 'dirty' bit. Each time somebody +// appends a line of text to the console, the dirty bit is +// automatically set. The bit can be checked using 'IsDirty' +// and cleared using 'ClearDirty'. This makes it so that +// you don't have to update the unreal widget unless the +// text has actually changed. +// +////////////////////////////////////////////////////////////// + +class FlxConsoleOutput { +private: + FString Content; + bool Dirty; + + // Truncate the console to a reasonable number of + // lines. The length is hardwired. + void Truncate(); + + // Add a newline if there isn't one. Returns true if it changed anything. + bool MaybeAppendNewline(); + + // Append text. Returns true if it changed anything. + bool MaybeAppendText(const FString& text); + +public: + // Append a line of text to the console. + void Append(const FString& text); + + // Append a line of text to the console on a line by itself. + void AppendLine(const FString& text); + + // Get the console text as a string. + const FString& Get() const { return Content; } + + // Return if the dirty flag is set. + bool IsDirty() const { return Dirty; } + + // Clear the dirty flag. + void ClearDirty() { Dirty = false; } +}; + + diff --git a/Source/Integration/DebugPrint.cpp b/Source/Integration/DebugPrint.cpp deleted file mode 100644 index 40b4f5e6..00000000 --- a/Source/Integration/DebugPrint.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include "DebugPrint.h" -#include "CoreMinimal.h" - -using DPrintCallback = DebugPrintControl::DPrintCallback; - -////////////////////////////////////////////////////////////// -// -// DPrintState -// -// The global state of the DPrint routine. There is only -// ever one of these, and it is owned by DPrintAccess below. -// -////////////////////////////////////////////////////////////// - -struct DPrintState { - // True if buffering is enabled. - bool Collect; - - // The array of buffered messages. - TArray Messages; - - // The array of callback functions. - TArray Callbacks; -}; - -////////////////////////////////////////////////////////////// -// -// DPrintAccess -// -// This class grants you safe access to the global state -// of the DPrint routine. Constructing an object of -// class DPrintAccess will lock the global mutex and make -// sure that the DPrintState is initialized. Then it will -// give you unrestricted access to the DPrintState through -// operator right arrow. -// -////////////////////////////////////////////////////////////// - -class DPrintAccess { -private: - // The Mutex that protects the global state. - static FCriticalSection Mutex; - - // The Global State. A pointer, to avoid static init issues. - static DPrintState* State; - -public: - // Constructor. Locks mutex and initializes state if necessary. - DPrintAccess() { - Mutex.Lock(); - if (State == nullptr) { - State = new DPrintState; - State->Collect = false; - } - } - - // Destructor. Releases mutex. - ~DPrintAccess() { - Mutex.Unlock(); - } - - // Access the DPrintState. - DPrintState* operator ->() { - return State; - } -}; - -FCriticalSection DPrintAccess::Mutex; -DPrintState* DPrintAccess::State; - - -////////////////////////////////////////////////////////////// -// -// Namespace DebugPrint. -// -// This contains all the various versions of the DPrint routine. -// -////////////////////////////////////////////////////////////// - -namespace DebugPrint { - - // DPrint. Invoke all the callbacks, and store the message. - void DPrint(const FString& fs) { - DPrintAccess state; - for (DPrintCallback cb : state->Callbacks) { - cb(fs); - } - if (state->Collect) { - state->Messages.Emplace(fs); - } - } - - // Alternative interface to the dispatcher. - void DPrint(const char* msg) { - DPrint(FString(msg)); - } - - // Alternative interface to the dispatcher. - void DPrint(const char* msg, size_t len) { - DPrint(FString(len, (const UTF8CHAR*)msg)); - } - -} // namespace DebugPrint - -////////////////////////////////////////////////////////////// -// -// Namespace DebugPrintControl. -// -// Configuration and control for the DPrint routine. -// -////////////////////////////////////////////////////////////// - -namespace DebugPrintControl { - - // Register a callback. Check for duplication. - void RegisterCallback(DPrintCallback cb) { - DPrintAccess state; - for (DPrintCallback old : state->Callbacks) { - if (cb == old) return; - } - state->Callbacks.Add(cb); - } - - // Set the collection bit in the global state. - void EnableCollection() { - DPrintAccess state; - state->Collect = true; - } - - // Get the array of collected messages, if any. - TArray GetStored() { - DPrintAccess state; - TArray result = std::move(state->Messages); - state->Messages.Empty(); - return result; - } -}; - -////////////////////////////////////////////////////////////// -// -// ConsoleOutput -// -// Storing the text that goes in the unreal console. -// -////////////////////////////////////////////////////////////// - - -void FlxConsoleOutput::Truncate() { - int lines = 50; - int csize = Content.Len(); - int total = 0; - for (int i = csize - 1; i >= 0; i--) { - if (Content[i] == '\n') { - total += 1; - if (total == lines) { - Content = Content.RightChop(i + 1); - return; - } - } - } -} - -bool FlxConsoleOutput::MaybeAppendNewline() { - int csize = Content.Len(); - if ((csize > 0) && (Content[csize - 1] != '\n')) { - Content += TEXT("\n"); - return true; - } - else { - return false; - } -} - -bool FlxConsoleOutput::MaybeAppendText(const FString& text) { - if (!text.IsEmpty()) { - Content += text; - return true; - } - else { - return false; - } -} - -void FlxConsoleOutput::Append(const FString& text) { - bool modified = MaybeAppendText(text); - if (modified) { - Dirty = true; - Truncate(); - } -} - -void FlxConsoleOutput::AppendLine(const FString& text) { - bool modified = MaybeAppendNewline(); - modified |= MaybeAppendText(text); - modified |= MaybeAppendNewline(); - if (modified) { - Dirty = true; - Truncate(); - } -} diff --git a/Source/Integration/DebugPrint.h b/Source/Integration/DebugPrint.h deleted file mode 100644 index 8acec4f6..00000000 --- a/Source/Integration/DebugPrint.h +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once - -////////////////////////////////////////////////////////////// -// -// Namespace DebugPrint -// -// This namespace contains the function "DPrint", -// which is short for debugging print. This -// is what you should use if you want to print -// a debugging message. -// -// Messages printed using "DPrint" will -// be routed to several destinations. These -// might include a logfile, the unreal console, -// and the visual studio debugging log. -// -// In order for "DPrint" to work, you have to -// configure them once, at program initialization -// time. This is done using the functions in -// namespace DebugPrintConfig, below. -// -// We promise that namespace DebugPrint will -// only ever contain one function, 'DPrint.' -// that way, it's safe for you to put -// 'using namespace DebugPrint' in your code. -// -// All DPrint-related functionality is -// thread-safe. A single 'DPrint' is meant to -// be output atomically. -// -// It is fine for the content of the DPrint -// string to contain newlines. -// -////////////////////////////////////////////////////////////// - -namespace DebugPrint { - // Print text into the debug log. - void DPrint(const FString& fs); - - // Print text into the debug log. - void DPrint(const char* msg); - - // Print text into the debug log. - void DPrint(const char* msg, size_t len); -}; - -////////////////////////////////////////////////////////////// -// -// namespace DebugPrintControl -// -// This namespace contains the functions -// necessary to initialize "DPrint", and -// route its output to several destinations. -// -// "DPrint" is a callback-based design. -// Initialization might consist of: register a -// callback that sends a string to a logfile, -// register another callback that sends a string -// to visual studio's debug console, and register -// a third one that sends a string to the -// unreal console. Each time somebody calls -// 'DPrint', all three callbacks will get invoked. -// -// There is also a 'collect' option where you -// can ask "DPrint" to save the messages in a -// buffer, which you can then collect at -// your leisure. You can use buffering and -// callbacks at the same time. Note that -// buffering is inherently less than ideal for -// messages that warn of imminent program aborts. -// So it is recommended that you use at least one -// callback that sends its output without delay. -// -////////////////////////////////////////////////////////////// - -namespace DebugPrintControl { - // The prototype for a callback function. - // - using DPrintCallback = void (*)(const FString& fs); - - // Register a callback. - // - // Registering a callback that is already - // registered is a no-op. - // - void RegisterCallback(DPrintCallback f); - - // Enable collection in a buffer. - // - // If collection is already enabled, this is a no-op. - // - void EnableCollection(); - - // Collect all the stored messages, and clear the storage. - // - TArray GetStored(); -}; - -////////////////////////////////////////////////////////////// -// -// ConsoleOutput -// -// This class stores the text that's in the unreal console. -// It stores it as one great big string, which contains -// newlines to denote line breaks. -// -// This class also contains a 'dirty' bit. Each time somebody -// appends a line of text to the console, the dirty bit is -// automatically set. The bit can be checked using 'IsDirty' -// and cleared using 'ClearDirty'. This makes it so that -// you don't have to update the unreal widget unless the -// text has actually changed. -// -////////////////////////////////////////////////////////////// - -class FlxConsoleOutput { -private: - FString Content; - bool Dirty; - - // Truncate the console to a reasonable number of - // lines. The length is hardwired. - void Truncate(); - - // Add a newline if there isn't one. Returns true if it changed anything. - bool MaybeAppendNewline(); - - // Append text. Returns true if it changed anything. - bool MaybeAppendText(const FString& text); - -public: - // Append a line of text to the console. - void Append(const FString& text); - - // Append a line of text to the console on a line by itself. - void AppendLine(const FString& text); - - // Get the console text as a string. - const FString& Get() const { return Content; } - - // Return if the dirty flag is set. - bool IsDirty() const { return Dirty; } - - // Clear the dirty flag. - void ClearDirty() { Dirty = false; } -}; - - diff --git a/Source/Integration/LockedWrapper.cpp b/Source/Integration/LockedWrapper.cpp index 0b1a4432..44d87907 100644 --- a/Source/Integration/LockedWrapper.cpp +++ b/Source/Integration/LockedWrapper.cpp @@ -1,25 +1,32 @@ #include "LockedWrapper.h" -#include "DebugPrint.h" +#include "LuprexGameModeBase.h" #include "lpx-drvutil.hpp" #include "lpx-paths.hpp" using namespace CommonTypes; + +void FlxLockedWrapper::DPrintHook(const char *Msg, size_t Size) +{ + FString FMessage(Size, (const UTF8CHAR *)Msg); + UE_LOG(LogLuprex, Error, TEXT("%s"), *FMessage); +} + void FlxLockedWrapper::InitWrapper() { if (Lockable.Wrapper.play_initialize != nullptr) { // Already initialized. return; } FString dll((const UTF8CHAR*)LUPREX_DLL_PATH); - DebugPrint::DPrint(dll); + UE_LOG(LogLuprex, Verbose, TEXT("Luprex DLL Path: %s"), *dll); void* DLL = FPlatformProcess::GetDllHandle(*dll); if (DLL != nullptr) { using InitFn = void (*)(EngineWrapper*); InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper")); if (init != nullptr) { init(&Lockable.Wrapper); - Lockable.Wrapper.hook_dprint(DebugPrint::DPrint); + Lockable.Wrapper.hook_dprint(DPrintHook); } } } @@ -27,16 +34,9 @@ void FlxLockedWrapper::InitWrapper() { FString FlxLockedWrapper::FetchStdout() { uint32_t ndata; const char* data; Lockable.Wrapper.get_outgoing(Get(), 0, &ndata, &data); - - if (ndata == 0) { - return FString(); - } - - std::string_view src(data, ndata); - int consumed; - std::u16string cps = drvutil::utf8_to_ucs2(src, &consumed); - Lockable.Wrapper.play_sent_outgoing(Get(), 0, consumed); - return FString(cps.size(), (const UCS2CHAR*)(&cps[0])); + FString result(ndata, (const UTF8CHAR *)data); + Lockable.Wrapper.play_sent_outgoing(Get(), 0, ndata); + return result; } int64 FlxLockedWrapper::GetActor() { diff --git a/Source/Integration/LockedWrapper.h b/Source/Integration/LockedWrapper.h index 746d0962..3876bc50 100644 --- a/Source/Integration/LockedWrapper.h +++ b/Source/Integration/LockedWrapper.h @@ -4,6 +4,7 @@ #include "lpx-enginewrapper.hpp" #include "CommonTypes.h" + // Class FlxLockableWrapper // // Contains the EngineWrapper and a Mutex to lock it. @@ -31,11 +32,17 @@ class FlxLockedWrapper { private: FlxLockableWrapper& Lockable; + // This function is called by luprex to output debugging + // messages. It is a thin wrapper around UE_LOG. + // + static void DPrintHook(const char *Msg, size_t Size); + public: - // Import these types into our Namespace. - using IdArray = CommonTypes::IdArray; - using IdView = CommonTypes::IdView; - using StringViewVec = CommonTypes::StringViewVec; + // Import these types into our Namespace. + using IdArray = CommonTypes::IdArray; + using IdView = CommonTypes::IdView; + using StringViewVec = CommonTypes::StringViewVec; + public: // The constructor of the FlxLockedWrapper claims the mutex. diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 024ec353..4528ba09 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -1,6 +1,8 @@ #include "LuaCall.h" #include "LuprexGameModeBase.h" +#include "StringDecoder.h" + #include "EdGraphSchema_K2.h" static void FatalBlueprintError(const TCHAR *message) { @@ -203,7 +205,8 @@ FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix) // ///////////////////////////////////////////////////////////////// -void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) { +void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) +{ ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); mode->LuaCallBegin(); @@ -212,7 +215,8 @@ void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, con } -void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { +void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) +{ ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); @@ -220,31 +224,26 @@ void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { } -void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) { +void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) +{ ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); } - - -// ELpxSimpleDynamicTag UlxLuaCallLibrary::LuaCallNextResultType(UObject *context) { -// ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(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; -// } -// } +UlxLuaValues *UlxLuaCallLibrary::LuaCallGetRest(UObject *context) +{ + ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetResult(); + UlxLuaValues *Values = NewObject(context); + if (!Values->Initialize(sb.view())) + { + UE_LOG(LogBlueprint, Error, TEXT("Lua call returned corrupt data")); + return nullptr; + } + return Values; +} ///////////////////////////////////////////////////////////////// // diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index 043cdbdd..64c1db84 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -2,8 +2,10 @@ #include "CoreMinimal.h" #include "EdGraph/EdGraphPin.h" + #include "LuaCall.generated.h" +class UlxLuaValues; ///////////////////////////////////////////////////////////////// // @@ -119,6 +121,9 @@ public: UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallProbe(UObject *context, AActor *Place); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") + static UlxLuaValues *LuaCallGetRest(UObject *context); + // // Functions that pack arguments into the call buffer. // diff --git a/Source/Integration/LuaCallNode.cpp b/Source/Integration/LuaCallNode.cpp index 88099bfa..ed81b1e9 100644 --- a/Source/Integration/LuaCallNode.cpp +++ b/Source/Integration/LuaCallNode.cpp @@ -2,6 +2,7 @@ #include "LuaCallNode.h" +#include "StringDecoder.h" #include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintNodeSpawner.h" @@ -40,59 +41,41 @@ #define LOCTEXT_NAMESPACE "LuaCall" +#define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name))) + // All argument pins will have internal Names that start with "A:" -static bool IsArgumentPin(const UEdGraphPin *Pin) { +const FName UK2Node_LuaCall::FunctionPinName(TEXT("Lua Function Prototype")); +const FName UK2Node_LuaCall::InvokeOrProbePinName(TEXT("Invoke or Probe")); +const FName UK2Node_LuaCall::PlacePinName(TEXT("Place Tangible")); +const FName UK2Node_LuaCall::ExtraResultsPinName(TEXT("Extra Results")); + +bool UK2Node_LuaCall::IsPrefix(const UEdGraphPin *Pin, char Prefix) +{ TCHAR pname[FName::StringBufferSize]; Pin->PinName.ToString(pname); - return pname[0] == 'A' && pname[1] == ':'; + return pname[0] == Prefix && pname[1] == ':'; } -static FName ArgumentNameAddPrefix(const FString &name) { - FString Prefixed = FString("A:") + name; +FName UK2Node_LuaCall::AddPrefix(const FString &Name, char Prefix) +{ + TCHAR PrefixChars[3]; + PrefixChars[0] = Prefix; + PrefixChars[1] = ':'; + PrefixChars[2] = 0; + FString Prefixed = PrefixChars + Name; return FName(*Prefixed); } -static FString ArgumentNameRemovePrefix(const FName &name) { - return name.ToString().Mid(2, FName::StringBufferSize); -} - -// All return value pins will have internal Names that start with "R:" - -static bool IsReturnValuePin(const UEdGraphPin *Pin) { +FString UK2Node_LuaCall::RemovePrefix(FName Name, char Prefix) +{ TCHAR pname[FName::StringBufferSize]; - Pin->PinName.ToString(pname); - return pname[0] == 'R' && pname[1] == ':'; + Name.ToString(pname); + check(pname[0] == Prefix); + check(pname[1] == ':'); + return FString(pname + 2); } -static FName ReturnValueNameAddPrefix(const FString &name) { - FString Prefixed = FString("R:") + name; - return FName(*Prefixed); -} - -static FString ReturnValueNameRemovePrefix(const FName &name) { - return name.ToString().Mid(2, FName::StringBufferSize); -} - -// Builtin pins will have names with no prefixes. - -static const FName FunctionPinName(TEXT("Lua Function Prototype")); -static bool IsFunctionPin(const UEdGraphPin *Pin) { - return (Pin->PinName == FunctionPinName); -} - -static const FName InvokeOrProbePinName(TEXT("Invoke or Probe")); -static bool IsInvokeOrProbePin(const UEdGraphPin *Pin) { - return (Pin->PinName == InvokeOrProbePinName); -} - -static const FName PlacePinName(TEXT("Place Tangible")); -static bool IsPlacePin(const UEdGraphPin *Pin) { - return (Pin->PinName == PlacePinName); -} - -#define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name))) - static FEdGraphPinType GetPinType(const FProperty *Property) { FEdGraphPinType PinType; @@ -107,7 +90,6 @@ static FEdGraphPinType GetPinType(const FProperty *Property) } - UK2Node_LuaCall::UK2Node_LuaCall(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -154,29 +136,6 @@ void UK2Node_LuaCall::CreateCorrectPins() LuaFunctionPrototype = TEXT("class.func(int arg1, int arg2) : int ret1, int ret2"); } - if (FindPin(UEdGraphSchema_K2::PN_Execute) == nullptr) { - CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute); - } - - if (FindPin(UEdGraphSchema_K2::PN_Then) == nullptr) { - CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then); - } - - if (FindPin(FunctionPinName, EGPD_Input) == nullptr) { - UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, FunctionPinName); - P->DefaultValue = LuaFunctionPrototype; - } - - if (FindPin(InvokeOrProbePinName, EGPD_Input) == nullptr) { - UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum(), InvokeOrProbePinName); - P->DefaultValue = TEXT("Probe"); - P->AutogeneratedDefaultValue = P->DefaultValue; - } - - if (FindPin(PlacePinName, EGPD_Input) == nullptr) { - UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, AActor::StaticClass(), PlacePinName); - } - // Parse the lua function prototype. FlxParsedProto ParsedProto(LuaFunctionPrototype); if (!ParsedProto.ErrorMessage.IsEmpty()) @@ -186,26 +145,60 @@ void UK2Node_LuaCall::CreateCorrectPins() ErrorMsg = FString::Printf(TEXT("Syntax error in lua function prototype: %s"), *ParsedProto.ErrorMessage); } - // Transfer all Existing argument and return value pins to the Old Pins Maps. - TMap OldArgumentPins; - TMap OldReturnValuePins; + // Transfer all pins to the old pins map and clear the pin list. + TMap OldPins; for (auto It = Pins.CreateIterator(); It; ++It) { UEdGraphPin* CheckPin = *It; - if (IsArgumentPin(CheckPin)) { - OldArgumentPins.Add(CheckPin->PinName, CheckPin); - It.RemoveCurrent(); - } - if (IsReturnValuePin(CheckPin)) { - OldReturnValuePins.Add(CheckPin->PinName, CheckPin); - It.RemoveCurrent(); + OldPins.Add(CheckPin->PinName, CheckPin); + } + Pins.Empty(); + + // KeepPin is a function that moves a pin from the old pins + // map back onto the pins list. + auto KeepPin = [&](FName Name, FEdGraphPinType Type = FEdGraphPinType()) -> bool + { + UEdGraphPin **OldPin = OldPins.Find(Name); + if ((OldPin != nullptr) && (((*OldPin)->PinType == Type) || (Type.PinCategory == FName()))) + { + Pins.Emplace(*OldPin); + OldPins.Remove(Name); } + return OldPin != nullptr; + }; + + if (!KeepPin(UEdGraphSchema_K2::PN_Execute)) + { + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute); + } + + if (!KeepPin(UEdGraphSchema_K2::PN_Then)) + { + CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then); + } + + if (!KeepPin(FunctionPinName)) + { + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, FunctionPinName); + P->DefaultValue = LuaFunctionPrototype; + } + + if (!KeepPin(InvokeOrProbePinName)) + { + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum(), InvokeOrProbePinName); + P->DefaultValue = TEXT("Probe"); + P->AutogeneratedDefaultValue = P->DefaultValue; + } + + if (!KeepPin(PlacePinName)) + { + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, AActor::StaticClass(), PlacePinName); } // Create Argument pins in the correct order, reusing old pins where possible. for (const FlxParsedProto::Pin & Pin : ParsedProto.Arguments) { - FName PrefixedName = ArgumentNameAddPrefix(Pin.Name); + FName PrefixedName = AddPrefix(Pin.Name, 'A'); UFunction *Accessor = UlxLuaCallLibrary::GetArgumentPacker(Pin.Type); if (Accessor == nullptr) { bHasCompilerMessage = true; @@ -214,11 +207,8 @@ void UK2Node_LuaCall::CreateCorrectPins() continue; } FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Value"))); - UEdGraphPin **OldPin = OldArgumentPins.Find(PrefixedName); - if ((OldPin != nullptr) && ((*OldPin)->PinType == PinType)) { - Pins.Emplace(*OldPin); - OldArgumentPins.Remove(PrefixedName); - } else { + if (!KeepPin(PrefixedName, PinType)) + { CreatePin(EGPD_Input, PinType, PrefixedName); } } @@ -226,7 +216,7 @@ void UK2Node_LuaCall::CreateCorrectPins() // Create ReturnValue pins in the correct order, reusing old pins where possible. for (const FlxParsedProto::Pin & Pin : ParsedProto.ReturnValues) { - FName PrefixedName = ReturnValueNameAddPrefix(Pin.Name); + FName PrefixedName = AddPrefix(Pin.Name, 'R'); UFunction *Accessor = UlxLuaCallLibrary::GetReturnValueUnpacker(Pin.Type); if (Accessor == nullptr) { bHasCompilerMessage = true; @@ -235,28 +225,27 @@ void UK2Node_LuaCall::CreateCorrectPins() continue; } FEdGraphPinType PinType = GetPinType(Accessor->GetReturnProperty()); - UEdGraphPin **OldPin = OldReturnValuePins.Find(PrefixedName); - if ((OldPin != nullptr) && ((*OldPin)->PinType == PinType)) { - Pins.Emplace(*OldPin); - OldReturnValuePins.Remove(PrefixedName); - } else { + if (!KeepPin(PrefixedName, PinType)) + { CreatePin(EGPD_Output, PinType, PrefixedName); } } + if (ParsedProto.ExtraReturnValues) + { + if (!KeepPin(ExtraResultsPinName)) + { + CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, UlxLuaValues::StaticClass(), ExtraResultsPinName); + } + } + // Delete any unused pins. - for (auto &iter : OldArgumentPins) + for (auto &iter : OldPins) { iter.Value->Modify(); iter.Value->MarkAsGarbage(); } - for (auto &iter : OldReturnValuePins) - { - iter.Value->Modify(); - iter.Value->MarkAsGarbage(); - } - OldArgumentPins.Empty(); - OldReturnValuePins.Empty(); + OldPins.Empty(); } @@ -274,7 +263,7 @@ FText UK2Node_LuaCall::GetPinDisplayName(const UEdGraphPin* Pin) const } // Many pins can go unlabeled if they have default values. - if (IsFunctionPin(Pin) || IsInvokeOrProbePin(Pin)) + if ((Pin->PinName == FunctionPinName) || (Pin->PinName == InvokeOrProbePinName)) { if (Pin->LinkedTo.Num() == 0) { @@ -283,13 +272,15 @@ FText UK2Node_LuaCall::GetPinDisplayName(const UEdGraphPin* Pin) const } // For argument pins, we must strip off the Argument Pin Prefix. - if (IsArgumentPin(Pin)) { - return FText::FromString(ArgumentNameRemovePrefix(Pin->PinName)); + if (IsPrefix(Pin, 'A')) + { + return FText::FromString(RemovePrefix(Pin->PinName, 'A')); } // For return value pins, we must strip off the Return Value Pin Prefix. - if (IsReturnValuePin(Pin)) { - return FText::FromString(ReturnValueNameRemovePrefix(Pin->PinName)); + if (IsPrefix(Pin, 'R')) + { + return FText::FromString(RemovePrefix(Pin->PinName, 'R')); } // Otherwise, just return the Pin Name the normal way. @@ -309,7 +300,7 @@ void UK2Node_LuaCall::PinConnectionListChanged(UEdGraphPin* Pin) void UK2Node_LuaCall::PinDefaultValueChanged(UEdGraphPin* Pin) { - if(IsFunctionPin(Pin)) + if(Pin->PinName == FunctionPinName) { LuaFunctionPrototype = Pin->DefaultValue; CreateCorrectPins(); @@ -356,7 +347,7 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext, // Add Packing operations for all argument pins. for (const FlxParsedProto::Pin &PinInfo : ParsedProto.Arguments) { - UEdGraphPin *Pin = FindPinChecked(ArgumentNameAddPrefix(PinInfo.Name)); + UEdGraphPin *Pin = FindPinChecked(AddPrefix(PinInfo.Name, 'A')); UFunction *PackingFunc = UlxLuaCallLibrary::GetArgumentPacker(PinInfo.Type); if (PackingFunc == nullptr) { @@ -378,10 +369,10 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext, PrevNode->GetThenPin()->MakeLinkTo(ActionNode->GetExecPin()); PrevNode = ActionNode; - // Add Unpacking operations for all argument pins. + // Add Unpacking operations for all return value pins. for (const FlxParsedProto::Pin &PinInfo : ParsedProto.ReturnValues) { - UEdGraphPin *Pin = FindPinChecked(ReturnValueNameAddPrefix(PinInfo.Name)); + UEdGraphPin *Pin = FindPinChecked(AddPrefix(PinInfo.Name, 'R')); UFunction *UnpackingFunc = UlxLuaCallLibrary::GetReturnValueUnpacker(PinInfo.Type); if (UnpackingFunc == nullptr) { @@ -395,16 +386,26 @@ void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext, PrevNode = UnpackNode; } - // Make sure we didn't have return values for an invoke. - if (IsInvoke && (ParsedProto.ReturnValues.Num() > 0)) + // If there is an extra results pin, hook it up. + if (ParsedProto.ExtraReturnValues) { - CompilerContext.MessageLog.Error(TEXT("Lua Call in Invoke mode does not support return values")); + UEdGraphPin *Pin = FindPinChecked(ExtraResultsPinName); + UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallGetRest)); + CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue)); + PrevNode->GetThenPin()->MakeLinkTo(UnpackNode->GetExecPin()); + PrevNode = UnpackNode; } // Link up the Exec pins. CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin()); CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *PrevNode->GetThenPin()); + // Make sure we didn't have return values for an invoke. + if (IsInvoke && ((ParsedProto.ReturnValues.Num() > 0) || (ParsedProto.ExtraReturnValues))) + { + CompilerContext.MessageLog.Error(TEXT("Lua Call in Invoke mode does not support return values")); + } + BreakAllNodeLinks(); } @@ -457,14 +458,14 @@ UK2Node::ERedirectType UK2Node_LuaCall::DoPinsMatchForReconstruction(const UEdGr bool UK2Node_LuaCall::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const { // The function pin cannot be connected. - if (IsFunctionPin(MyPin)) + if (MyPin->PinName == FunctionPinName) { OutReason = LOCTEXT("Error_FunctionPrototypeMustBeHardwired", "Lua function prototype must be a hardwired constant.").ToString(); return true; } // The invoke-or-probe pin cannot be connected. - if (IsInvokeOrProbePin(MyPin)) + if (MyPin->PinName == InvokeOrProbePinName) { OutReason = LOCTEXT("Error_InvokeOrProbeMustBeHardwired", "Invoke vs Probe must be a hardwired constant.").ToString(); return true; diff --git a/Source/Integration/LuaCallNode.h b/Source/Integration/LuaCallNode.h index 70739f8f..aeb1fc08 100644 --- a/Source/Integration/LuaCallNode.h +++ b/Source/Integration/LuaCallNode.h @@ -69,9 +69,21 @@ class UK2Node_LuaCall : public UK2Node //~ End UK2Node Interface. private: + /** Create all necessary pins. */ void CreateCorrectPins(); + /** Pin Names for the three built-in Pins **/ + static const FName FunctionPinName; + static const FName InvokeOrProbePinName; + static const FName PlacePinName; + static const FName ExtraResultsPinName; + + /** Utility functions for manipulating pin names with prefixes **/ + static bool IsPrefix(const UEdGraphPin *Pin, char Prefix); + static FName AddPrefix(const FString &String, char Prefix); + static FString RemovePrefix(FName Name, char Prefix); + private: /** The lua function prototype, which must be saved as a property **/ UPROPERTY() diff --git a/Source/Integration/LuprexGameModeBase.cpp b/Source/Integration/LuprexGameModeBase.cpp index b383ee9b..476e759b 100644 --- a/Source/Integration/LuprexGameModeBase.cpp +++ b/Source/Integration/LuprexGameModeBase.cpp @@ -3,7 +3,7 @@ #include "LuprexGameModeBase.h" #include "lpx-drvutil.hpp" #include "lpx-paths.hpp" -#include "DebugPrint.h" +#include "ConsoleOutput.h" #include "Tangible.h" #include "TangibleManager.h" #include "CommonTypes.h" @@ -11,9 +11,11 @@ #include #include -using namespace DebugPrint; using namespace CommonTypes; +DEFINE_LOG_CATEGORY(LogLuprex); +DEFINE_LOG_CATEGORY(LogLuprexIntegration); + ALuprexGameModeBase::ALuprexGameModeBase() { TangibleManager = NewObject(); @@ -24,7 +26,6 @@ ALuprexGameModeBase::ALuprexGameModeBase() //PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong SetActorTickEnabled(true); SetActorTickInterval(0.0f); - DebugPrintControl::EnableCollection(); ResetToInitialState(); OnWorldPreActorTickHandle = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPreActorTick); OnWorldPostActorTickHandle = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPostActorTick); @@ -103,12 +104,6 @@ void ALuprexGameModeBase::UpdateConsoleOutput() { ConsoleOutput.Append(lockedwrap.FetchStdout()); } - // Copy Debugging Prints into the console. - TArray prints = DebugPrintControl::GetStored(); - for (const FString& fs : prints) { - ConsoleOutput.AppendLine(fs); - } - // If the Console text has changed, update the widget. if (ConsoleOutput.IsDirty()) { ConsoleSetOutput(ConsoleOutput.Get()); @@ -192,21 +187,7 @@ void ALuprexGameModeBase::LuaCallEnd(InvocationKind kind, AActor *place) { } void ALuprexGameModeBase::ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs) { - if (fs == "\\invokeplayer") { - DPrint(TEXT("Trying to invoke 'myfunction' in lua")); - FlxStreamBuffer &sb = LuaCallBegin(); - sb.write_string("engio"); - sb.write_string("myfunction"); - sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); - sb.write_double(3.0); - sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); - sb.write_string("Howdy"); - sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); - sb.write_fvector(FVector(2,3,4)); - LuaCallEnd(InvocationKind::LUA_INVOKE); - } else { - ConsoleOutput.AppendLine(TEXT("Unknown Command")); - } + // Nothing here right now. } void ALuprexGameModeBase::ConsoleSendInput(const FString& fs) @@ -262,16 +243,13 @@ void ALuprexGameModeBase::Tick(float deltaseconds) void ALuprexGameModeBase::BeginPlay() { - Super::BeginPlay(); - - // Make sure we're starting from a clean slate. - // Note: this claims the wrapper lock, so don't claim - // the lock before calling this. ResetToInitialState(); + InitializeGlobalState(); + Super::BeginPlay(); +} - // Now we're just going to claim the wrapper - // lock for the remainder. When we create the thread, - // the thread will hang until we release this lock. +void ALuprexGameModeBase::InitializeGlobalState() +{ FlxLockedWrapper w(LockableWrapper); // Sanity checks. Make sure everything is clean. @@ -284,7 +262,7 @@ void ALuprexGameModeBase::BeginPlay() // If we failed to initialize the wrapper, print an error message. if (w->play_initialize == nullptr) { - DPrint("Luprex wrapper initialization failed"); + UE_LOG(LogLuprexIntegration, Error, TEXT("Luprex wrapper initialization failed")); } // If wrapper is initialized, try to initialize the luprex engine. @@ -294,7 +272,8 @@ void ALuprexGameModeBase::BeginPlay() std::string srcpakerr = drvutil::package_lua_source(LUPREX_ROOT_PATH, &srcpak); if (!srcpakerr.empty()) { - DPrint(srcpakerr.c_str()); + FString FMessage((const UTF8CHAR *)(srcpakerr.c_str())); + UE_LOG(LogLuprexIntegration, Error, TEXT("Trying to read Lua source: %s"), *FMessage); } else { @@ -304,10 +283,11 @@ void ALuprexGameModeBase::BeginPlay() w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), ""); if (w->error[0]) { - DPrint(w->error); + FString FMessage((const UTF8CHAR *)w->error); + UE_LOG(LogLuprexIntegration, Error, TEXT("Calling Luprex play_initialize: %s"), *FMessage); } if (w->engine != nullptr) { - DPrint("Luprex initialize success"); + UE_LOG(LogLuprexIntegration, Verbose, TEXT("Luprex initialization success.")); Playing = true; } } diff --git a/Source/Integration/LuprexGameModeBase.h b/Source/Integration/LuprexGameModeBase.h index 6885b9ee..c9dfe895 100644 --- a/Source/Integration/LuprexGameModeBase.h +++ b/Source/Integration/LuprexGameModeBase.h @@ -5,7 +5,7 @@ #include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" #include "lpx-enginewrapper.hpp" -#include "DebugPrint.h" +#include "ConsoleOutput.h" #include "StringDecoder.h" #include "TangibleManager.h" #include "LuprexSockets.h" @@ -13,6 +13,11 @@ #include "BlueprintErrors.h" #include "LuprexGameModeBase.generated.h" +// Messages that come from inside the Luprex Core. +DECLARE_LOG_CATEGORY_EXTERN(LogLuprex, Warning, All); + +// Messages that pertain to our Luprex integration with Unreal. +DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Warning, All); class LookAtDetector; @@ -35,6 +40,9 @@ public: // includes: the Luprex engine, the thread, and the socket state. void ResetToInitialState(); + // Initialize the Luprex DLL, and do other global initialization. + void InitializeGlobalState(); + // Set the entire contents of the console output box. UFUNCTION(BlueprintImplementableEvent, Category = "Luprex|Miscellaneous") void ConsoleSetOutput(const FString& text); diff --git a/Source/Integration/LuprexSockets.cpp b/Source/Integration/LuprexSockets.cpp index 4ad0c0fc..1e4b4a6f 100644 --- a/Source/Integration/LuprexSockets.cpp +++ b/Source/Integration/LuprexSockets.cpp @@ -1,14 +1,13 @@ #include "LuprexSockets.h" +#include "LuprexGameModeBase.h" #include "lpx-enginewrapper.hpp" #include "lpx-drvutil.hpp" -#include "DebugPrint.h" #include "Sockets.h" #include "SocketTypes.h" #include "SocketSubsystem.h" #include "AddressInfoTypes.h" -using namespace DebugPrint; #define UI UI_ST THIRD_PARTY_INCLUDES_START @@ -215,9 +214,9 @@ public: FlxSocketsI(FlxLockedWrapper &w); virtual ~FlxSocketsI() override; - // Copy the trace to the DPrint output. - void DPrintTrace(); - + // Copy the trace to UE_LOG. + void LogTrace(); + // Error handling. void SetError(const std::string& s); virtual std::string GetError() override { return FatalError; } @@ -686,7 +685,7 @@ void FLpxChannel::AdvanceAccepting() { CloseChannelIfSSLErrorIsSerious( retval); } - LSI->DPrintTrace(); + LSI->LogTrace(); } void FLpxChannel::AdvanceReadWrite() @@ -933,12 +932,13 @@ FlxSocketsI::~FlxSocketsI() // TODO: Be more thorough. } -void FlxSocketsI::DPrintTrace() +void FlxSocketsI::LogTrace() { char* data; int ndata = BIO_get_mem_data(TraceBIO, &data); if (ndata == 0) return; - DPrint(data, ndata); + FString text(ndata, (const UTF8CHAR *)data); + UE_LOG(LogLuprexIntegration, Verbose, TEXT("SSL Trace: %s"), *text); BIO_reset(TraceBIO); } diff --git a/Source/Integration/StringDecoder.cpp b/Source/Integration/StringDecoder.cpp index 5ce873cd..19415d28 100644 --- a/Source/Integration/StringDecoder.cpp +++ b/Source/Integration/StringDecoder.cpp @@ -22,14 +22,14 @@ bool UlxLuaValues::Initialize(std::string_view data) SimpleDynamicTag Tag = Decoder.read_simple_dynamic_tag(); int64 Pos = Decoder.total_reads(); - ELxLuaValueType Type; + ElxLuaValueType Type; switch (Tag) { - case SimpleDynamicTag::BOOLEAN: Type=ELxLuaValueType::Boolean; Decoder.read_bool(); break; - case SimpleDynamicTag::NUMBER: Type=ELxLuaValueType::Float; Decoder.read_double(); break; - case SimpleDynamicTag::STRING: Type=ELxLuaValueType::String; Decoder.read_string_view(); break; - case SimpleDynamicTag::TOKEN: Type=ELxLuaValueType::Name; Decoder.read_string_view(); break; - case SimpleDynamicTag::VECTOR: Type=ELxLuaValueType::Vector; Decoder.read_fvector(); break; + case SimpleDynamicTag::BOOLEAN: Type=ElxLuaValueType::Boolean; Decoder.read_bool(); break; + case SimpleDynamicTag::NUMBER: Type=ElxLuaValueType::Float; Decoder.read_double(); break; + case SimpleDynamicTag::STRING: Type=ElxLuaValueType::String; Decoder.read_string_view(); break; + case SimpleDynamicTag::TOKEN: Type=ElxLuaValueType::Name; Decoder.read_string_view(); break; + case SimpleDynamicTag::VECTOR: Type=ElxLuaValueType::Vector; Decoder.read_fvector(); break; default: { Empty(); return false; @@ -42,22 +42,57 @@ bool UlxLuaValues::Initialize(std::string_view data) return true; } -bool UlxLuaValues::CheckType(ELxLuaValueType Type, ELxLuaValueType Desired) +FString UlxLuaValues::DebugString() const +{ + TStringBuilder<2048> Output; + Output << TEXT("{ "); + for (int i = 0; i < Types.Num(); i++) + { + if (i > 0) Output << TEXT(", "); + + ElxLuaValueType Type = Types[i]; + FlxStreamBuffer Decoder(Data[i]); + switch (Type) + { + case ElxLuaValueType::Boolean: + Output << Decoder.read_bool(); + break; + case ElxLuaValueType::Float: + Output << FString::Printf(TEXT("%lf"), Decoder.read_double()); + break; + case ElxLuaValueType::String: + Output << TEXT("\"") << Decoder.read_fstring() << TEXT("\""); + break; + case ElxLuaValueType::Name: + Output << Decoder.read_fname(); + break; + case ElxLuaValueType::Vector: { + FVector Vec = Decoder.read_fvector(); + Output << FString::Printf(TEXT("(%lf, %lf, %lf)"), Vec.X, Vec.Y, Vec.Z); + break; + } + } + } + Output << TEXT(" }"); + return Output.ToString(); +} + +bool UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) { if (Type != Desired) { - FString TypeName = StaticEnum()->GetDisplayNameTextByValue(int64(Type)).ToString(); - FString DesiredName = StaticEnum()->GetDisplayNameTextByValue(int64(Desired)).ToString(); + 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 true; } -ELxLuaValueType UlxLuaValues::GetType(int n) const +ElxLuaValueType UlxLuaValues::GetType(int n) const { - if (n < 0) return ELxLuaValueType::None; - if (n >= Types.Num()) return ELxLuaValueType::None; + if (n < 0) return ElxLuaValueType::None; + if (n >= Types.Num()) return ElxLuaValueType::None; return Types[Cursor]; } @@ -69,42 +104,42 @@ ELxLuaValueType UlxLuaValues::GetType(int n) const FString UlxLuaValues::GetString(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::String)) return FString(); + if (!CheckType(GetType(n), ElxLuaValueType::String)) return FString(); FlxStreamBuffer Decoder(Data[n]); return Decoder.read_fstring(); } FName UlxLuaValues::GetName(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::Name)) return FName(); + if (!CheckType(GetType(n), ElxLuaValueType::Name)) return FName(); FlxStreamBuffer Decoder(Data[n]); return Decoder.read_fname(); } double UlxLuaValues::GetFloat(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::Float)) return 0.0; + if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0.0; FlxStreamBuffer Decoder(Data[n]); return Decoder.read_double(); } int UlxLuaValues::GetInt(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::Float)) return 0; + if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0; FlxStreamBuffer Decoder(Data[n]); return int(Decoder.read_double()); } FVector UlxLuaValues::GetVector(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::Vector)) return FVector(); + if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector(); FlxStreamBuffer Decoder(Data[n]); return Decoder.read_fvector(); } FVector2D UlxLuaValues::GetVector2D(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::Vector)) return FVector2D(); + if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector2D(); FlxStreamBuffer Decoder(Data[n]); FVector v = Decoder.read_fvector(); return FVector2D(v.X, v.Y); @@ -112,7 +147,7 @@ FVector2D UlxLuaValues::GetVector2D(int n) const bool UlxLuaValues::GetBoolean(int n) const { - if (!CheckType(GetType(n), ELxLuaValueType::Boolean)) return false; + if (!CheckType(GetType(n), ElxLuaValueType::Boolean)) return false; FlxStreamBuffer Decoder(Data[n]); return Decoder.read_bool(); } diff --git a/Source/Integration/StringDecoder.h b/Source/Integration/StringDecoder.h index 01122a79..e3819e9c 100644 --- a/Source/Integration/StringDecoder.h +++ b/Source/Integration/StringDecoder.h @@ -77,7 +77,7 @@ public: ///////////////////////////////////////////////////////////////// UENUM(BlueprintType) -enum class ELxLuaValueType : uint8 { +enum class ElxLuaValueType : uint8 { None, String, Name, @@ -105,7 +105,7 @@ private: // For each chunk, the type of the chunk. // - TArray Types; + TArray Types; // For each chunk, a pointer to the serialized data. // @@ -120,16 +120,21 @@ private: // void Empty(); + // Compare two types. If they aren't equal, log an error and return false. + // + static bool CheckType(ElxLuaValueType Type, ElxLuaValueType Desired); + +public: + UlxLuaValues() { Cursor = 0; } + // Copies the data, and then preprocesses it to make sure // it's all valid. If it's not valid, returns false. // bool Initialize(std::string_view data); - // Compare two types. If they aren't equal, log an error and return false. - // - static bool CheckType(ELxLuaValueType Type, ELxLuaValueType Desired); + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + FString DebugString() const; -public: UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") int Length() const { return Types.Num(); } @@ -139,16 +144,17 @@ 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; + ElxLuaValueType GetType(int n) const; UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool IsType(int n, ELxLuaValueType Type) const { return GetType(n) == Type; } + bool IsType(int n, ElxLuaValueType Type) const { return GetType(n) == Type; } UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") FString GetString(int n) const; @@ -177,10 +183,10 @@ private: public: UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - ELxLuaValueType NextType() const { return GetType(Cursor); } + ElxLuaValueType NextType() const { return GetType(Cursor); } UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool IsNextType(ELxLuaValueType Type) const { return IsType(Cursor, Type); } + bool IsNextType(ElxLuaValueType Type) const { return IsType(Cursor, Type); } UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") FString ReadString() { return GetString(Cursor++); } diff --git a/Source/Integration/Tangible.cpp b/Source/Integration/Tangible.cpp index 67a0b014..c92b08b5 100644 --- a/Source/Integration/Tangible.cpp +++ b/Source/Integration/Tangible.cpp @@ -5,7 +5,7 @@ #include "TangibleManager.h" #include "LuprexGameModeBase.h" -#define DEFAULT_BLUEPRINT (TEXT("TangibleStaticMesh")) +#define DEFAULT_BLUEPRINT (TEXT("StaticMesh")) UlxTangible::UlxTangible() { @@ -24,17 +24,18 @@ void UlxTangible::Init(UlxTangibleManager* tm, int64 id) } #pragma optimize("", off) -void UlxTangible::SetActorBlueprint(const FString &name) { +void UlxTangible::SetActorBlueprint(const FString &XName) { + FString Name = XName.ToLower(); + // If we're already of the right class, do nothing. - if (ActorBlueprintName == name) { + if (ActorBlueprintName == Name) { return; } // Get the blueprint. - // If the blueprint doesn't exist, or doesn't implement - // TangibleInterface, then use the fallback blueprint. - UClass *blueprint = Manager->GetTangibleClass(name); - if ((!name.IsEmpty()) && (blueprint == nullptr)) { + UClass *blueprint = Manager->GetTangibleClass(Name); + if (blueprint == nullptr) + { blueprint = Manager->GetTangibleClass(DEFAULT_BLUEPRINT); check(blueprint != nullptr); } @@ -62,13 +63,16 @@ void UlxTangible::SetActorBlueprint(const FString &name) { } // Update the blueprint name - ActorBlueprintName = name; + ActorBlueprintName = Name; // Now create a new actor, unless the BP is nullptr. if (blueprint != nullptr) { UWorld* w = Manager->GetWorld(); FActorSpawnParameters params; + // Give the new actor a reasonable name. + params.Name = FName(*FString::Printf(TEXT("%s_%ld"), *Name, TangibleId)); + // Currently, the actor is spawned at (0,0,0), which is not good. // We should spawn at the actor's current location. I'll get to it // eventually. @@ -86,6 +90,9 @@ void UlxTangible::SetActorBlueprint(const FString &name) { AActor* a = w->SpawnActor(blueprint, &transform, params); check(a != nullptr); + // Make sure the label and the name are the same. + a->SetActorLabel(params.Name.ToString()); + // Insert a TangibleComponent into the actor. // Link the actor to its tangible, and the tangible to its actor. UActorComponent* ac = a->AddComponentByClass(UlxTangibleComponent::StaticClass(), false, FTransform::Identity, false); diff --git a/Source/Integration/Tangible.h b/Source/Integration/Tangible.h index e4844c41..40653603 100644 --- a/Source/Integration/Tangible.h +++ b/Source/Integration/Tangible.h @@ -124,6 +124,7 @@ public: private: + // Set the actor's blueprint, and recreate the actor if necessary. // // If the blueprint is the empty string, deletes the actor. diff --git a/Source/Integration/TangibleManager.cpp b/Source/Integration/TangibleManager.cpp index 137ba8cb..e6c3735c 100644 --- a/Source/Integration/TangibleManager.cpp +++ b/Source/Integration/TangibleManager.cpp @@ -3,11 +3,8 @@ #include "TangibleManager.h" #include "Tangible.h" -#include "DebugPrint.h" #include "LuprexGameModeBase.h" -using namespace DebugPrint; - UFunction *UlxTangibleManager::GetAnimationQueueChanged(UClass *uclass) { UFunction *result = uclass->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); if (result == nullptr) return nullptr; @@ -16,6 +13,8 @@ UFunction *UlxTangibleManager::GetAnimationQueueChanged(UClass *uclass) { } UClass *UlxTangibleManager::GetTangibleClass(const FString &name) { + UPackage *Pack = LoadObject(nullptr, TEXT("/Game/Tangibles")); + UE_LOG(LogBlueprint, Verbose, TEXT("Pack=%ld"), int64(Pack)); if (name.IsEmpty()) { return nullptr; } @@ -24,8 +23,10 @@ UClass *UlxTangibleManager::GetTangibleClass(const FString &name) { } FString path(TEXT("/Game/Tangibles/")); + path += TEXT("Tan"); path += name; path += TCHAR('.'); + path += TEXT("Tan"); path += name; path += TCHAR('_'); path += TCHAR('C'); diff --git a/Source/Integration/TriggeredTask.cpp b/Source/Integration/TriggeredTask.cpp index 84b16155..2d0c82fd 100644 --- a/Source/Integration/TriggeredTask.cpp +++ b/Source/Integration/TriggeredTask.cpp @@ -1,7 +1,4 @@ #include "TriggeredTask.h" -#include "DebugPrint.h" - -using namespace DebugPrint; FTriggeredTask::FTriggeredTask() { Client = nullptr; @@ -16,7 +13,6 @@ uint32 FTriggeredTask::Run() { { CallEvent->Wait(); if (ThreadStopRequested) { - DPrint("Thread stopping as requested"); break; } // The payload. diff --git a/luprex/cpp/core/world-accessor.cpp b/luprex/cpp/core/world-accessor.cpp index 702df5dd..16909130 100644 --- a/luprex/cpp/core/world-accessor.cpp +++ b/luprex/cpp/core/world-accessor.cpp @@ -328,7 +328,7 @@ LuaDefine(tangible_build, "config", if (!LS.isnil(bp)) { state.set_string("bp", LS.ckstring(bp)); } else { - state.set_string("bp", LS.ckstring(classname)); + state.set_string("bp", LS.classname(classtab)); } if (!LS.isnil(plane)) { state.set_string("plane", LS.ckstring(plane)); diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index 74a0b118..c4851b7f 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -1,21 +1,14 @@ makeclass('login') makeclass("engio") -makeclass('tree') - -function tree.init(self, config) - print("In tree.init:") - pprint{self=self, config=config} - wait(1) - print("tick"); - wait(1) -end +makeclass('cube') +makeclass('sphere') function login.init() local actor = tangible.actor() dprint("login.init:", actor) - local x = math.random(1, 100) - local y = math.random(1, 100) - tangible.animate(actor, nil, {bp="tangiblecharacter", action="warpto", plane="earth", xyz={x, y, 90}}) + tangible.animinit(actor, {bp="character", plane="earth", xyz={0, 0, 90}}) + tangible.build{class=cube, xyz={500,-100,0}} + tangible.build{class=sphere, xyz={500,100,0}} end function engio.move(action, xyz, facing) @@ -29,7 +22,7 @@ function engio.printhi(a1, a2, a3, a4, a5) pprint("Hi there", a1, a2, a3, a4, a5) end -function engio.retthree(a1, a2) - return 7, vec(8,9,10), "Yo" +function engio.retmany() + return 7, vec(8,9,10), "Yo", "Banana", 13.2, vec(2,3,4), "Hi" end