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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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.

View File

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

View File

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