Lots of work on several unrelated things.

This commit is contained in:
2025-03-28 23:31:44 -04:00
parent 3741470b20
commit b26d56048f
30 changed files with 444 additions and 612 deletions

View File

@@ -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;

View File

@@ -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.

View File

@@ -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();
}
}

View File

@@ -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; }
};

View File

@@ -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<FString> Messages;
// The array of callback functions.
TArray<DPrintCallback> 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<FString> GetStored() {
DPrintAccess state;
TArray<FString> 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();
}
}

View File

@@ -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<FString> 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; }
};

View File

@@ -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() {

View File

@@ -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.

View File

@@ -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<UlxLuaValues>(context);
if (!Values->Initialize(sb.view()))
{
UE_LOG(LogBlueprint, Error, TEXT("Lua call returned corrupt data"));
return nullptr;
}
return Values;
}
/////////////////////////////////////////////////////////////////
//

View File

@@ -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.
//

View File

@@ -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<ElxInvokeOrProbe>(), 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<FName, UEdGraphPin *> OldArgumentPins;
TMap<FName, UEdGraphPin *> OldReturnValuePins;
// Transfer all pins to the old pins map and clear the pin list.
TMap<FName, UEdGraphPin *> 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<ElxInvokeOrProbe>(), 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;

View File

@@ -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()

View File

@@ -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 <string>
#include <string_view>
using namespace DebugPrint;
using namespace CommonTypes;
DEFINE_LOG_CATEGORY(LogLuprex);
DEFINE_LOG_CATEGORY(LogLuprexIntegration);
ALuprexGameModeBase::ALuprexGameModeBase()
{
TangibleManager = NewObject<UlxTangibleManager>();
@@ -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<FString> 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;
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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<ELxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString();
FString DesiredName = StaticEnum<ELxLuaValueType>()->GetDisplayNameTextByValue(int64(Desired)).ToString();
FString TypeName = StaticEnum<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString();
FString DesiredName = StaticEnum<ElxLuaValueType>()->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();
}

View File

@@ -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<ELxLuaValueType> Types;
TArray<ElxLuaValueType> 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++); }

View File

@@ -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);

View File

@@ -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.

View File

@@ -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<UPackage>(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');

View File

@@ -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.